socket()返回-1且errno为0,因Windows未调用WSAStartup()或Linux未链接-lsocket/-lnsl;connect()阻塞需设非阻塞+select()或线程超时;send()/recv()需循环处理;shutdown()可半关闭,close()全关闭。

为什么socket()返回-1却没报错?
常见现象是调用socket(AF_INET, SOCK_STREAM, 0)后返回-1,但errno为0或未检查——这是因为Windows下必须先调用WSAStartup(),Linux下则需确认是否链接了-lsocket -lnsl(部分旧系统)。不初始化或漏链接会导致函数直接失败且errno未置位。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- Linux:编译时加
g++ client.cpp -o client -lsocket -lnsl(现代glibc通常只需-lpthread,但某些嵌入式环境仍需显式链接) - Windows:在
main()开头调用WSAStartup(MAKEWORD(2,2), &wsaData),且必须配对WSACleanup() - 跨平台判断:用
#ifdef _WIN32包裹初始化和清理逻辑,避免Linux下编译报错
connect()阻塞到超时怎么办?
默认connect()是阻塞的,遇到目标端口未监听、防火墙拦截或网络不可达时,会卡住几十秒才返回失败。这不是bug,而是TCP协议栈的重传机制决定的。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 改非阻塞+轮询:设置
fcntl(sockfd, F_SETFL, O_NONBLOCK)(Linux)或ioctlsocket(sockfd, FIONBIO, &nonblocking)(Windows),再用select()或poll()等待可写事件(连接完成时套接字变为“可写”) - 更简单方案:用
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))设接收超时(注意:这仅对recv()有效;connect()本身不响应SO_RCVTIMEO) - 真正可控的超时:用
alarm()(不推荐,信号干扰大)或线程+std::chrono+shutdown()组合(C++11起较稳妥)
send()和recv()为什么不能一次发完/收全?
这是最常被误解的点:send()不保证把缓冲区数据全发出,recv()也不保证一次收完应用层消息。它们只按TCP流当前窗口和底层缓冲状态尽力而为,返回值是实际操作字节数。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 发送:用循环封装
send(),检查返回值是否等于待发长度,小于则更新指针和剩余长度继续发 - 接收:不能假设
recv()一次拿到整包。需配合协议头(如前4字节存包长)或分隔符(如\n)做粘包处理 - 别用
while(recv(...)>0)无限读——对方关闭连接时recv()返回0,错误时返回-1,需分别判断
close()和shutdown()有什么区别?
close()是文件描述符级操作,调用后立即释放fd,内核在发送完已排队数据后发FIN;shutdown(sockfd, SHUT_WR)则只关闭写方向,让对端收到EOF,但本端仍可recv()——这对实现“半关闭”协议(如HTTP keep-alive)很关键。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 客户端发完请求想等响应:用
shutdown(sockfd, SHUT_WR),再循环recv()直到返回0 - 服务端处理完请求要断连:直接
close()即可 - 多线程场景慎用
close():若多个线程共享同一fd,一个线程close()后其他线程再send()会触发SIGPIPE,默认终止进程
真正难的不是写出能通的代码,而是处理send()返回值小于预期、recv()中途断开、以及不同平台对EINTR/WSAEWOULDBLOCK的差异化响应——这些细节不写进循环和错误分支里,程序在高并发或弱网下必然出问题。











