Address already in use 错误应先查杀残留进程,再设 SO_REUSEADDR;backlog 需显式设为 1024 或 SOMAXCONN;recv() 返回 0 表示对端关闭,需主动 close()。

bind() 失败提示 Address already in use 怎么办
端口被占是服务端启动失败最常见原因,不是代码写错了,而是上次进程没彻底退出,或者系统还没回收连接状态。
- 先用
netstat -tuln | grep :<your_port></your_port>或lsof -i :<your_port></your_port>查端口谁在用 - 启动前加
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)),opt = 1,避免 TIME_WAIT 卡住端口 -
SO_REUSEPORT在 Linux 3.9+ 也支持,但多进程复用时才需要,单服务端用SO_REUSEADDR就够了 - 别一看到报错就改端口号——掩盖问题不如清掉残留进程:
kill $(lsof -t -i :<your_port>)</your_port>
accept() 阻塞住,客户端连不上?检查 listen() 参数
listen() 的第二个参数是 backlog,它不是“最大并发数”,而是内核里已完成三次握手、但还没被 accept() 取走的连接队列长度。设太小会导致 SYN 收到后直接丢包,客户端超时或重传失败。
- Linux 默认 backlog 是 128,但很多发行版实际限制更严(如 systemd 会截断),建议显式设为
1024或SOMAXCONN - 如果客户端反复显示
Connection refused,先确认服务端是否真执行到了listen()之后——加个printf("listening on %d\n", port)看输出 - 防火墙和 SELinux 也可能拦截:临时关掉测试:
sudo systemctl stop firewalld(仅调试)
recv() 返回 0 不代表出错,而是对端 close() 了
C++ 里没有“连接已断开”的异常抛出,所有状态都靠返回值判断。很多人把 recv() 返回 0 当成错误处理,结果逻辑跳过清理,导致 fd 泄漏或后续读写崩溃。
-
recv()返回-1才要查errno:比如EAGAIN(非阻塞模式下无数据)、ECONNRESET(对方 RST 断连) - 返回
0表示对端调用了close()或shutdown(SHUT_WR),该连接进入半关闭状态,你该close()自己这端了 - 别在循环里只写
if (n ——漏掉了 <code>n == 0的语义,应明确区分:if (n == 0) { /* 对端关闭 */ } else if (n
struct sockaddr_in 初始化不全,bind() 随机绑定到奇怪端口
忘记清零结构体,sin_port 或 sin_addr 里是栈上脏数据,bind() 可能成功但监听在不可预期的地址或端口,客户端连不上还查不出原因。
立即学习“C++免费学习笔记(深入)”;
- 必须初始化:
struct sockaddr_in addr{};(C++11 起支持聚合初始化清零)或用memset(&addr, 0, sizeof(addr)) -
htons()必须用:端口号要转网络字节序,addr.sin_port = htons(8080),写8080直接赋值是错的 -
addr.sin_family = AF_INET不能漏,否则可能匹配到 IPv6 套接字行为,尤其在 dual-stack 系统上表现诡异
实际跑通一个最小服务端,核心就四步:socket() → bind() → listen() → accept() 循环。难点不在语法,而在每个系统调用返回后你有没有真正看懂它想告诉你什么。特别是 errno 和返回值组合,比任何文档都真实。










