socket() 仅创建文件描述符,需服务端调用 bind()+listen()+accept()、客户端调用 connect() 才能通信;send()/recv() 不保证一次完成,须循环处理并检查返回值。

怎么用 socket() 创建一个能通的连接
直接调用 socket() 不等于就能发数据——它只分配了一个文件描述符,还没绑定地址、没指定对方、也没建立连接。常见错误是跳过 bind()(服务端必须)或 connect()(客户端必须),结果 send() 时返回 -1,errno 是 EDESTADDRREQ 或 ENOTCONN。
典型流程分两路:
- 服务端:
socket()→bind()(填sockaddr_in的sin_addr.s_addr = INADDR_ANY)→listen()→accept() - 客户端:
socket()→connect()(目标 IP + 端口必须有效,DNS 未解析会卡在connect()超时)
注意:IPv4 和 IPv6 的 struct sockaddr_in / struct sockaddr_in6 字段名和大小不同,混用会导致 bind() 失败且 errno = EINVAL。
send() 和 recv() 为什么总收不全或卡住
这两个函数不保证一次传完或收完——它们只按当前 TCP 缓冲区状态尽力操作。常见现象:发 1024 字节,send() 返回 321;收 1024 字节,recv() 只返回 128,然后阻塞住。这不是 bug,是 TCP 流式特性的正常表现。
立即学习“C++免费学习笔记(深入)”;
解决办法不是重写协议,而是补全逻辑:
- 发送方:检查返回值,小于预期就循环调用
send(),直到发完(注意send()可能返回 0,表示对端关闭连接) - 接收方:不能假设一次
recv()拿到完整消息,得自己拼包。比如用“长度前缀”:先读 4 字节整数得到 body 长度,再循环recv()直到凑够 - 别忽略
MSG_WAITALL标志——它只对recv()有效,且在非阻塞 socket 下会被忽略
示例片段:
int total = 0, n;<br>while (total < len) {<br> n = send(sockfd, buf + total, len - total, 0);<br> if (n <= 0) break;<br> total += n;<br>}
云模块_YunMOK网站管理系统采用PHP+MYSQL为编程语言,搭载自主研发的模块化引擎驱动技术,实现可视化拖拽无技术创建并管理网站!如你所想,无限可能,支持创建任何网站:企业、商城、O2O、门户、论坛、人才等一块儿搞定!永久免费授权,包括商业用途; 默认内置三套免费模板。PC网站+手机网站+适配微信+文章管理+产品管理+SEO优化+组件扩展+NEW Login界面.....目测已经遥遥领先..
select() / poll() 怎么避免 CPU 空转或漏事件
用 select() 监听多个 socket 时,最常踩的坑是没重置 fd_set。系统调用会修改它,下一轮必须用 FD_ZERO() + FD_SET() 重建,否则漏 fd 或崩溃。
poll() 稍好些,但要注意 struct pollfd 的 revents 字段是输出值,每次循环前不用清零,但得检查它是否含 POLLIN / POLLOUT / POLLHUP,而不是只看 events。
-
select()最大监听数受限于FD_SETSIZE(通常是 1024),超了要换epoll或kqueue -
poll()没硬上限,但数组长度大了性能下降明显,1000+ fd 建议上epoll - 无论哪种,都要处理
EBADF(fd 已关闭)和EINTR(被信号中断),否则循环会崩
close() 后还能不能立刻重用端口
不能,默认情况下 close() 后端口进入 TIME_WAIT 状态(持续 2MSL,通常 60 秒),防止旧包干扰新连接。如果频繁启停服务,会遇到 bind(): Address already in use。
加 SO_REUSEADDR 可绕过:
- 服务端:在
bind()前设setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) - 客户端一般不需要,因为内核自动选临时端口
- 注意:
SO_REUSEADDR不等于SO_LINGER,后者控制close()是否等待 FIN-ACK 完成,误设可能拖慢关闭速度
这个选项在 Windows 和 Linux 行为一致,但 macOS 上对 TIME_WAIT 的处理更激进,有时不设也能复用——别依赖这点。
网络编程里最麻烦的从来不是写第一行 socket(),而是处理所有“本不该发生但偏偏发生了”的情况:连接突然断、缓冲区满、信号打断、字节序错、超时没设、错误码没判。这些地方不填平,程序上线后就会在凌晨三点给你打电话。










