socket()返回-1不一定失败,需查errno或wsagetlasterror();bind()端口占用可设so_reuseaddr;recv()返回0仅tcp表示对端关闭;select()会破坏fd_set,须每次重置。

socket() 函数返回 -1 是不是一定失败了?
不一定。C++ 里调用 socket() 返回 -1 只代表系统调用失败,但真正原因得看 errno——比如 EAFNOSUPPORT(地址族不支持)、EMFILE(进程打开文件数超限)或 ENFILE(系统级文件描述符耗尽)。别一看到 -1 就以为是代码写错了。
- 务必在
socket()后立即检查errno,而不是靠打印“创建失败”糊弄过去 - Linux 下可用
strerror(errno)获取可读提示;Windows 用WSAGetLastError(),且必须先调WSAStartup() - 常见误操作:在未初始化 Winsock 的 Windows 程序里直接调
socket(),此时返回 -1,errno无意义,得查WSAGetLastError()返回WSANOTINITIALISED
bind() 失败报 “Address already in use” 怎么办?
这是端口被占用的典型提示,但不一定是别的程序占着——更可能是你上次运行的程序没正确关闭 socket,TIME_WAIT 状态还在占着端口。直接改端口号只是绕开问题,不是解决。
- 服务端启动前,设置
SO_REUSEADDR选项能重用处于 TIME_WAIT 的本地地址:setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) -
SO_REUSEPORT(Linux 3.9+、macOS)可允许多个 socket 绑定同一地址+端口,但需谨慎:它会把连接随机分发到多个监听者,不是所有场景都适用 - 注意:Windows 默认不支持
SO_REUSEPORT,且SO_REUSEADDR在 Windows 上行为略有不同(比如允许跨协议重用),别盲目照搬 Linux 示例
recv() 返回 0 是不是网络断开了?
是,但只对 TCP 成立。这个返回值表示对端已调用 shutdown(SHUT_WR) 或正常 close(),TCP 连接进入 FIN_WAIT 状态,本端读缓冲区数据已空。UDP 的 recv() 永远不会返回 0。
- TCP 场景下,
recv()返回 0 应立即关闭本端 socket,否则可能造成资源泄漏或后续send()触发 SIGPIPE - 别用
recv()返回值是否为 0 来判断 UDP 是否“断连”——UDP 本就不维护连接,丢包、目标不可达都只会让recv()阻塞或超时,不会返回 0 - 如果想探测 TCP 对端是否存活,得用
SO_KEEPALIVE选项,或者自己实现应用层心跳;依赖recv()返回 0 只能捕获“优雅关闭”,抓不到崩溃掉线
select() 调用后 fd_set 里的 socket 全没了?
对,select() 是**破坏性调用**:每次返回后,fd_set 会被内核修改,只保留就绪的 socket。如果你没在每次循环开始前重新 FD_SET() 所有要监听的 socket,就会漏掉未就绪的那些。
立即学习“C++免费学习笔记(深入)”;
- 标准写法是:每次进入
select()前,用原始备份的fd_set(比如all_fds)重新赋值给待检测的readfds - 别把
fd_set当成状态缓存——它只是临时传给内核的“问题列表”,不是“结果列表” - 注意
select()的最大文件描述符限制(通常是 1024),超出会导致静默失败;现代项目建议直接用epoll(Linux)或kqueue(macOS/BSD),它们不修改输入结构体,也无硬上限
errno 值在 Linux 和 macOS 上含义可能不同,Windows 完全另起一套错误码体系。别指望一份错误处理逻辑跑遍所有平台。











