iocp是windows高并发服务器专用完成端口模型,需绑定句柄、堆分配overlapped、用completionkey关联上下文,严格管控各对象生命周期,依赖完成包确认i/o结果而非返回值。

IOCP 在 Windows 上不是“异步 IO”的通用解法,而是专为高并发服务器设计的完成端口模型
它不替代 ReadFile/WriteFile 的异步调用,而是把它们的完成通知统一收口到一个线程池里。你得先调用 CreateIoCompletionPort 绑定句柄,再对每个句柄发起重叠 I/O(OVERLAPPED 必须堆分配且生命周期可控),否则会立刻崩溃或静默失败。
-
OVERLAPPED结构体不能是栈变量——回调发生时栈早已销毁,必须new分配并随完成包一起传入GetQueuedCompletionStatus - 每个 socket 或文件句柄调用
CreateIoCompletionPort时,第三个参数CompletionKey是你唯一能关联业务上下文的地方,建议塞入this指针或自定义结构体指针 - 不要在
GetQueuedCompletionStatus返回后直接 deleteOVERLAPPED——它可能被多个完成包复用,应由业务逻辑决定释放时机
启动 IOCP 线程池前,必须手动设置线程亲和性或限制并发数
Windows 默认允许任意数量线程在 IOCP 上等待,但线程切换开销会吃掉性能。真实场景中,4–8 个常驻线程通常比 CPU 核心数更多更稳,因为部分时间线程会卡在 WSARecv 等系统调用里。
- 调用
SetThreadAffinityMask不是必须的,但若绑定到特定核心,需确保该核心不被其他高优先级进程抢占 -
SetThreadPriority设为THREAD_PRIORITY_HIGHEST容易引发饥饿,反而降低吞吐;保持默认或略提一级即可 - 务必用
WaitForSingleObject+ 超时检测线程是否僵死,IOCP 线程一旦卡在用户代码里(比如死锁、长时间计算),整个端口就堵住
socket 使用 IOCP 前必须关闭重叠 I/O 的自动缓冲行为
Winsock 默认开启 SO_SNDBUF/SO_RCVBUF 缓冲区,这会让 WSASend 看似成功返回,实则数据还在内核队列里。你无法靠返回值判断是否真正发出去了,必须依赖完成包里的 dwNumberOfBytesTransferred。
- 调用
setsockopt关闭SO_SNDBUF(设为 0)没用,Windows 会强制设为最小值;正确做法是接受缓冲存在,只在完成包里确认字节数 -
WSARecv的lpBuffers指向的内存必须全程有效——不能是局部数组,也不能在完成前被realloc或free - 断连检测不能只靠
recv返回 0,要检查WSAGetLastError()是否为WSAEDISCON或WSAENETRESET,否则会漏掉静默断连
错误码 ERROR_IO_PENDING 不是异常,而是 IOCP 正常流程的起点
每次调用 ReadFile、WSARecv 或 WriteFile 后返回 FALSE 并且 GetLastError() == ERROR_IO_PENDING,说明操作已进内核队列,等完成包唤醒线程处理——这是 IOCP 高效的关键,不是 bug。
- 如果没收到完成包,先检查是否忘了调用
GetQueuedCompletionStatus,或者传入了错误的HANDLE -
GetQueuedCompletionStatus返回FALSE且GetLastError()==WAIT_TIMEOUT是正常现象,不代表出错 - 真正危险的是
GetQueuedCompletionStatus返回TRUE但dwNumberOfBytesTransferred == 0且操作是接收——大概率是对方关闭连接,需清理资源
IOCP 的复杂点不在 API 调用顺序,而在于所有对象(socket、OVERLAPPED、buffer 内存、completion key 关联的对象)的生命周期必须由你严格控制。没人帮你回收,错一次就内存泄漏或访问违规。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!









