socket()参数顺序固定为AF_INET在前、SOCK_STREAM在后,写反则返回-1且errno为EINVAL;AF_INET6/AF_UNIX不可混用,SOCK_DGRAM对应UDP;bind()失败常见于端口占用、未设SO_REUSEADDR或权限不足;recv()/send()不保证收发完整数据,需应用层处理粘包;close()后继续读写会出错,须检查返回值判断连接状态。

socket() 创建套接字时,AF_INET 和 SOCK_STREAM 不能写反
很多人一上来就抄示例,把 socket(AF_INET, SOCK_STREAM) 写成 socket(SOCK_STREAM, AF_INET),结果 socket() 直接返回 -1,errno 是 EINVAL。这不是系统玄学,是 POSIX 明确定义的参数顺序:第一个是地址族(AF_INET),第二个是套接字类型(SOCK_STREAM)。
实际使用中还要注意:AF_INET6 和 AF_UNIX 是不同地址族,混用会导致绑定失败或连接被拒;SOCK_DGRAM 对应 UDP,和 TCP 场景完全不兼容。
- Linux/macOS 下可以用
strace跟踪系统调用,快速确认参数是否被内核拒绝 - Windows 上用
WSAGetLastError()查错误码,别只看返回值是否为INVALID_SOCKET - 初始化前记得调用
WSAStartup()(Windows)或忽略(Unix-like),否则socket()必败
bind() 失败常见于端口被占用或权限不足
bind() 返回 -1 且 errno 是 EADDRINUSE,最常见原因是端口正被其他进程监听。但还有两个容易被忽略的情况:一是没设 SO_REUSEADDR,服务重启时 TIME_WAIT 状态导致端口无法立即复用;二是非 root 用户尝试绑定 1024 以下端口(如 80、443),直接被内核拒绝。
实操建议:
- 服务端启动前加
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)),避免重启卡住 - 测试时优先用 8080、9000 这类高位端口,绕开权限检查
- Linux 下用
lsof -i :端口号或ss -tuln | grep 端口号查谁占着端口
recv() 和 send() 不保证一次收发完整消息
TCP 是字节流协议,recv() 可能只读到部分数据,send() 也可能只发出一部分——哪怕你传了 1024 字节,返回值可能是 327。这不是 bug,是正常行为。很多初学者直接假设“一次 recv() 就是一条完整请求”,结果解析错乱、粘包、截断全来了。
解决思路不是硬等,而是设计应用层边界:
- 用固定长度头(比如前 4 字节存 body 长度),先读够头再读 body
- 用分隔符(如
\n)时,必须循环recv()直到找到分隔符,不能只调一次 - 别依赖
send()返回值等于你传的长度,要检查并重发剩余部分(尤其在非阻塞模式下)
close() / closesocket() 后继续 read/write 会触发 SIGPIPE 或返回 -1
客户端断开后,服务端如果还往已关闭的连接 send(),Linux 默认发 SIGPIPE 信号(进程直接退出);Windows 返回 -1,WSAGetLastError() 是 WSAENOTCONN。很多人没做错误检查,程序莫名崩溃或卡死。
关键动作是:每次 send() 和 recv() 都必须检查返回值。
-
recv()返回 0 表示对端正常关闭连接,该清理资源了 -
recv()返回 -1 且errno是EAGAIN或EWOULDBLOCK(非阻塞模式),说明暂时没数据,不是错误 -
send()返回 -1 且errno是EPIPE或ECONNRESET,说明连接已断,别再往这个 fd 写
真实网络里连接中断比想象中频繁,靠运气不检查返回值,迟早在线上出问题。


















