IOCP小结
http://www.cnblogs.com/Hybird3D/archive/2012/02/02/2335000.html
http://www.cnblogs.com/Hybird3D/archive/2012/02/04/2337652.html
IOCP完成端口详解(译)
http://wenku.baidu.com/view/b439f5c30c22590102029d3c.html
IOCP完全解析(译)
http://wenku.baidu.com/view/543cacd6c1c708a1284a4459.html
IOCP 写服务程序时的关键问题研究(论文)
http://wenku.baidu.com/view/5fd5fdc68bd63186bcebbc11.html
关于Socket和IOCP的一些值得注意的地方
http://www.cppblog.com/sleepwom/archive/2009/04/13/79766.html
IOCP相关的一些总结
http://laokaddk.blog.51cto.com/368606/610780
谈谈IOCP发送数据时的一些误区及技巧
http://www.cppblog.com/sleepwom/archive/2009/01/31/72731.html
完成端口(IOCP)编程探讨
http://www.cppblog.com/sherrylso/archive/2007/08/26/30858.html
CreateIoCompletionPort和完成端口
http://blog.csdn.net/hionceshine/article/details/3362669
IOCP编程注意事项
http://blog.csdn.net/easyiocp/article/details/6364580
编写大容量和健壮的服务器系列—处理IOCP连接关闭
http://hi.baidu.com/winnyang/blog/item/2cdb8108f94e0537e8248817.html
WSA_IO_PENDING返回值
http://hi.baidu.com/jiajia288/blog/item/64260a81c01535d99123d9f2.html
正文
IOCP是windows下的异步IO模型. 它完整的实现了<UNIX网络编程>里提到的异步IO模型.
IOCP主要函数和结构:
CreateIoCompletionPort()GetQueuedCompletionStatus()
PostQueueCompletionStatus()
OVERLAPPED 结构
Windows 异步网络IO函数:
socket,WSASocket,bind,listen,WSAAccept,AcceptEx,WSAConnect,WSASend,WSARecv,CancelIo,
使用IOCP模型时的要点.
1. Handle关联给IOCP后,无法取消关联.唯一的手段是closesocket(),iocp内部会自动删除关联.如果需要handle的IO操作不产生iocp通知,可以将传递的overlapped的event成员低位置1.
2. CompletionKey一般用来指示对应的Handle,该值在内部与对应Handle关联,这种关联无法修改.它属于per_handle_data的概念,一般设置CompletionKey为handle,或者用户封装handle的对象指针.在需要退出的时候,可以传递CompletionKey为0,线程判断后做相应的退出处理.
3. OVERLAPPED结构,在每次执行异步IO操作的时候,都需要传递一个overlapped结构,即OVERLAPPED结构属于per_io_operation_data概念.我们应该继承OVERPLAPPED实现用户自定义的类(或定义OVERLAPPED结构为第一成员的结构),这个对象我们称为operation对象.在GetQueuedCompletionPort返回时,再将OVERLAPPED指针转换成operation对象指针. 在operation对象中一般需要附带上io操作相关的数据,如完整的数据包指针,大小等,以便处理线程进行相应操作.operation对象的个数由投递的IO操作次数来决定,由于一个handle可以同时多次投递IO操作,所以一个handle对应的operation对象也就不只一个.在不冲突的情况下,可以重复利用operation对象.注意在从io操作投递到获得完成通知的过程中,应该保证operation对象是有效的.此外要注意OVERLAPPED结构成员的初始化.
4. 在一个handle上投递多个IO操作.
假设应用在一个链接上投递多个数据发送和接收,iocp保证最底层IO操作的执行顺序与投递顺序一样,但处理线程收到完成通知的顺序是混乱的.因此顺序敏感的情况下,operation对象中要封装相关数据,要做好排序处理.投递多个IO操作可以使IO一有数据或准备就绪时就能立即执行IO操作,而无时间浪费.
6. 关闭缓冲区
关于缓冲区的论述:
Let's look at how the system handles a typical send call when the send buffer size is non-zero. When an application makes a send call, if there is sufficient buffer space, the data is copied into the socket's send buffers,the call completes immediately with success, and the completion is posted.On the other hand, if the socket's send buffer is full, then the application's send buffer is locked and the send call fails with
WSA_IO_PENDING. After the data in the send buffer is processed (for example,handed down to TCP for processing), then Winsock will process the locked buffer directly. That is, the data is handed directly to TCP from the
application's buffer and the socket's send buffer is completely bypassed.The opposite is true for receiving data. When an overlapped receive call is performed, if data has already been received on the connection, it will be
buffered in the socket's receive buffer. This data will be copied directly into the application's buffer (as much as will fit), the receive call returns success, and a completion is posted. However, if the socket's receive buffer is empty, when the overlapped receive call is made, the application's buffer is locked and the call fails with WSA_IO_PENDING. Once data arrives on the connection, it will be copied directly into the application's buffer, bypassing the socket's receive buffer altogether.
关闭接受缓冲区是毫无必要的,当对方数据来到时,都会被系统缓冲,不论是AFD.sys级还是更底层,复制的代价肯定是有的.
关闭发送缓冲区,会使WSASend在数据被对方底层接收之后才返回.重要的是AFD.sys级没有缓存帮你保证持续不断的发送,因此应用如果在进行大量的发送,则它必须保证一个socket上同时有多个WSASend提交,这样才不会浪费2次WSASend之间的时间.
总而言之,关闭缓冲区没太大必要.
7.资源不足错误.
a.通过前面的描述可知,若关闭了缓冲区,或AFD.sys的发送缓存已满,则WSASend调用传递的缓存会被锁定,而OS锁定页面的个数是有上限的.
b.异步IO操作时,OS会对传递的OVERLAPPED锁定为非页面缓存,OS支持的非页面缓存数量是有限的.
CPU处理数据的能力远超IO传输能力,假设我们要发送大量数据,很可能瞬间就能在一个handle上投递大量operation对象.导致a或b两种情况超过限制.产生ERROR_INSUFFICIENT_RESOURCES, WSAENOBUFS错误.
解决方法: 同时只能投递一个WSASend一个WSARecv, 或使用0字节手法消除投递多个WSARecv的需要,或设置投递个数的上限.
8.0字节手法.
在执行WSASend和WSARecv的时候,可以指定长度为0的缓冲区,这可以起到查询的效果.若底层接收缓冲区有数据,0字节WSARecv就会得到完成通知.若底层可以发送,则0字节WSASend也会得到完成通知.iocp是一个proactor形式的IO,我们可以通过该手法将其改造成reactor.先投递0字节的IO操作,iocp通知IO可操作时,再投递真正的IO操作.
0字节手法也用于避免在一个handle上投递多个recv,先投递0字节WSARecv,如果IO有数据可读的时候,再调用非阻塞型的普通rec v,直到返回EWOULDBLOCK.
9.线程中不应该使用可能长时间阻塞的函数.
io执行线程本意是来处理IO完成通知,并投递异步操作的,如果进入阻塞状态,则iocp的完成通知就可能暂时得不到处理,影响系统处理效率.所以io执行线程中不应该调用阻塞的函数,如send,recv.
10.IO与逻辑分离设计
A. iocp的执行线程只负责IO数据的读写,逻辑线程负责处理相应的数据包,2者共同操作一个数据缓存队列.
B. IO进程和逻辑进程,两者通过 共享内存的队列 进行通信.
C. 连接服务器+逻辑服务器的设计,前者只负责处理外部链接,所有数据包通过另一个简单的IO连接与逻辑服务器进行交互.
后2者 在逻辑处理部分立即重启后, 系统仍旧可以继续正常工作,不会断开客户的连接.
11.资源释放与退出.
a.套接字的优雅关闭.
http://blog.csdn.net/afxid/article/details/698032
b.释放顺序.
停止监听和接受新套接字
通知所有IO操作返回
关闭现有套接字
等待现有套接字关闭完成,判断套接字上的IO操作全部返回,发送和接收完全结束.
释放IO操作相关的数据结构
close iocp handle
12.所有会阻塞的函数 都应该尽可能使用timeout机制, 所有连接都必须使用心跳timeout机制,超时断开.
多线程IO程序在实际工作运行下状态可能非常复杂,代码存在难以通过测试或工具发现的错误,timeout是最后的安全保证.此外TCP连接在无数据发送的时候是无法判断连接是否真实存在,因此必须设定一些心跳或超时机制.