C++网络编程基础:面试官让我写一个socket通信,我连bind和listen的顺序都搞反了
多线程板块聊完了。今天进入一个新的领域——网络编程。
在机器人开发里,网络通信无处不在。多台机器人之间的协作、机器人和云端服务器的通信、远程遥控、遥测数据上报,都要走网络。即使你用ROS2的DDS通信,底层也是socket。
先讲个面试糗事。
面试官让我手写一个简单的TCP服务器。我心想这有什么难的,socket、bind、listen、accept、recv、send,流程背得滚瓜烂熟。结果写的时候一紧张,把bind和listen的顺序搞反了——先listen再bind。面试官看了一眼代码,笑着说"你确定这个顺序?"
当时那个尴尬,至今记忆犹新。
TCP通信的基本流程
TCP是面向连接的协议。通信双方分客户端和服务器端,流程不一样。
服务器端的流程:
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
// 1. 创建socket
int serverFd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 绑定地址和端口
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
addr.sin_port = htons(8080); // 端口8080
bind(serverFd, (struct sockaddr*)&addr, sizeof(addr));
// 3. 开始监听
listen(serverFd, 5); // 5是等待队列的长度
// 4. 接受连接
struct sockaddr_in clientAddr;
socklen_t clientLen = sizeof(clientAddr);
int clientFd = accept(serverFd, (struct sockaddr*)&clientAddr, &clientLen);
// 5. 收发数据
char buffer[1024];
int n = recv(clientFd, buffer, sizeof(buffer), 0);
send(clientFd, "Hello!", 6, 0);
// 6. 关闭
close(clientFd);
close(serverFd);
客户端的流程简单一些:
int clientFd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.100");
serverAddr.sin_port = htons(8080);
connect(clientFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
send(clientFd, "Hello Server!", 13, 0);
char buffer[1024];
recv(clientFd, buffer, sizeof(buffer), 0);
记住顺序:服务器端是socket→bind→listen→accept→recv/send,客户端是socket→connect→send/recv。bind必须在listen之前——你得先告诉系统"我在哪个端口上等着",然后才能开始监听。
UDP通信
UDP是无连接的,不需要listen和accept。发就发,收就收:
// UDP服务器
int fd = socket(AF_INET, SOCK_DGRAM, 0);
bind(fd, ...);
char buffer[1024];
struct sockaddr_in clientAddr;
socklen_t len = sizeof(clientAddr);
recvfrom(fd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&clientAddr, &len);
sendto(fd, "reply", 5, 0,
(struct sockaddr*)&clientAddr, len);
UDP比TCP快,但不可靠——包可能丢失、可能乱序。在机器人开发里,实时性要求高但允许偶尔丢包的场景(比如视频流、实时位姿),用UDP比较多。需要可靠传输的场景(比如控制指令、地图数据),用TCP。
在机器人开发中的应用
多机器人协作的时候,机器人之间需要通信。一个常见的架构是:一台机器人当"主节点",其他机器人当"从节点",用TCP连接:
class RobotCommServer {
int serverFd_;
vector<int> clientFds_;
mutex mtx_;
public:
void start(int port) {
serverFd_ = socket(AF_INET, SOCK_STREAM, 0);
// bind, listen...
// 用线程池处理连接
thread([this] {
while (true) {
int clientFd = accept(serverFd_, nullptr, nullptr);
lock_guard<mutex> lock(mtx_);
clientFds_.push_back(clientFd);
}
}).detach();
}
void broadcast(const string& msg) {
lock_guard<mutex> lock(mtx_);
for (int fd : clientFds_) {
send(fd, msg.c_str(), msg.size(), 0);
}
}
};
ROS2底层的DDS通信,虽然封装得很好,但本质上也是socket通信。理解socket编程,有助于你调试网络层面的问题——比如为什么消息延迟高了、为什么丢包了。
面试中的关键考点
"TCP和UDP的区别?"TCP面向连接、可靠传输、保证顺序、有流量控制;UDP无连接、不可靠、不保证顺序、速度快。
"三次握手和四次握手?"TCP建立连接要三次握手(SYN→SYN-ACK→ACK),断开要四次挥手(FIN→ACK→FIN→ACK)。为什么断开多一次?因为TCP是全双工的,每个方向的关闭是独立的。
"socket的阻塞和非阻塞有什么区别?"默认情况下socket是阻塞的——recv没有数据时会一直等。非阻塞模式下,recv没有数据会立即返回-1(EAGAIN)。高性能服务器通常用非阻塞socket配合IO多路复用(select/poll/epoll)。
给正在准备面试的你
再补充一个面试中经常被问到的实战话题:怎么调试网络通信问题。在机器人开发里,网络通信出问题时,最常用的排查工具是wireshark——它可以抓取所有经过网卡的数据包,让你看到实际发送和接收的字节内容。如果不想安装wireshark,tcpdump在命令行下也能抓包。另外在代码层面,给每次send和recv加上日志打印数据长度和前几个字节的十六进制值,能快速定位数据是否正确传输。还有一个容易被忽略的点:TCP是流式协议,没有消息边界。你发两次send各10字节,接收方可能一次recv收到20字节,也可能分两次各收到10字节。所以在设计通信协议时,必须在应用层自己定义消息边界——通常用长度前缀或者特殊分隔符。面试时如果你能聊到TCP粘包问题和解决方案,面试官会觉得你有实际的网络编程经验。
网络编程在机器人面试里不算高频考点,但偶尔会问。基本的socket流程要会写,TCP和UDP的区别要能讲清楚。调试网络问题的时候,wireshark抓包和netstat查看连接状态是两个最常用的手段,建议也了解一下。
如果你做的是ROS2开发,面试官可能不会直接考socket,但会问"DDS的通信机制你了解吗"。这时候如果你能提到"底层是UDP组播做发现,UDP单播做数据传输",面试官会觉得你对底层有了解。
下篇聊序列化与反序列化——protobuf在机器人通信中的应用。网络上传输的数据需要序列化成字节流,接收方再反序列化回来。protobuf是机器人领域最常用的序列化方案。
如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。
「机器人软件开发面试·从入门到精通」连载系列 上一篇:第58篇 多线程之线程池设计——机器人系统中的标配组件 下一篇预告:第60篇 序列化与反序列化——protobuf在机器人通信中的应用
有任何问题欢迎评论区留言,我会尽量回复。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)