tcp服务端最小可行流程是:socket()创建套接字→bind()绑定ip和端口→listen()启动监听;windows需先调wsastartup,linux无需初始化;bind前须用htons转换端口、memset清零sockaddr_in结构体,监听队列长度建议设为somaxconn。

如何用 socket + bind + listen 启动一个基础 TCP 服务端
直接能跑起来的最小可行服务端,核心就是三步:创建套接字、绑定地址、进入监听。Windows 和 Linux 都支持这套 API,但初始化略有不同——Linux 不需要额外初始化,Windows 必须先调用 WSAStartup,否则 socket 返回 INVALID_SOCKET。
关键点在于:AF_INET 表示 IPv4,SOCK_STREAM 表示 TCP,IPPROTO_TCP 可显式指定(也可传 0)。绑定前记得把端口号用 htons 转成网络字节序,否则监听会失败(常见错误:端口没反应,bind 返回 -1,errno 或 WSAGetLastError() 提示 WSAEADDRINUSE 或 EADDRINUSE)。
-
sockaddr_in结构体必须 memset 清零,尤其sin_zero字段,否则可能因垃圾值导致bind失败 - 绑定
INADDR_ANY(即0.0.0.0)才能接受所有网卡来的连接;绑127.0.0.1只能本地连 -
listen的第二个参数是等待队列长度(backlog),设为SOMAXCONN最安全,硬写小数字(如 1)会导致并发连接被拒绝
怎么用 accept 正确收连接并避免阻塞主线程
accept 默认是阻塞的,没有新连接时会卡住。如果只做简单测试,直接调用没问题;但只要想同时处理多个连接,就必须解决阻塞问题。最轻量的做法是把 socket 设为非阻塞模式,再配合轮询或 select;更常见的做法是每收到一个连接就 fork(Linux)或创建新线程(跨平台),让子进程/线程去处理通信。
注意:accept 返回的是一个**新的 socket fd**,和监听 socket 无关。别拿它继续 listen,也别在循环里重复 close 监听 socket。
- 调用
accept前检查返回值是否为INVALID_SOCKET(Windows)或-1(Linux),再通过WSAGetLastError()或errno判断是否是WSAEWOULDBLOCK/EAGAIN(非阻塞场景下正常现象) - 客户端地址信息通过
sockaddr_in*参数传出,需传入地址长度指针(如&client_len),否则 Windows 下可能崩溃 - 不要在
accept后立刻send,TCP 握手完成才真正可发数据;但现代内核通常已隐式保证,一般不用额外等
为什么 recv 有时只读到部分数据,甚至返回 0
TCP 是流式协议,recv 不保证一次把应用层“一条消息”全读完。它只按当前缓冲区实际数据量返回,可能是 1 字节,也可能是几百字节,还可能因对端关闭连接而返回 0(不是错误,是正常 EOF)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
常见误操作:假定 recv 会填满你给的缓冲区,或忽略返回值直接处理整个 buffer。结果就是粘包、丢数据、或把垃圾内存当有效内容解析。
- 必须检查
recv返回值:> 0是收到数据,== 0表示对方已关闭连接,才是错误(再看 <code>errno或WSAGetLastError()) - 需要自己实现消息边界,比如约定每条消息以
\n结尾,或头部带 4 字节长度字段——不能依赖recv拆分 - 如果用非阻塞 socket,
recv返回SOCKET_ERROR且错误码是WSAEWOULDBLOCK,说明暂时没数据,应跳过处理,而不是报错退出
关闭 socket 时 closesocket 和 shutdown 有什么区别
shutdown 控制连接的读写方向,closesocket(Windows)或 close(Linux)才是彻底释放资源。如果只调 shutdown(SD_SEND),还能继续 recv 对方发来的剩余数据;而 closesocket 一调,双方通道立即断开,未收完的数据会被丢弃。
典型遗漏:服务器在子线程处理完客户端请求后,只 close 了通信 socket,却忘了 close 监听 socket —— 导致程序退出后端口仍被占用(TIME_WAIT 状态残留),下次启动报 Address already in use。
- 多线程环境下,不能在多个线程里重复
close同一个 socket fd,会导致未定义行为 - Linux 下可设
SO_REUSEADDR选项,让bind忽略TIME_WAIT端口,避免重启失败 - Windows 下记得最后调
WSACleanup(),虽然不调也不一定出问题,但属于未释放资源
真正麻烦的从来不是写通第一版服务端,而是处理半开连接、超时、缓冲区溢出、信号中断这些边界情况——它们不会在 hello world 例子里出现,但上线后第一个凌晨三点的告警,往往就来自其中某一个。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










