IOCP学习笔记
参考:http://www.codeproject.com/internet/iocp_server_client.asp
IOCP解决一个线程服务一个客户端的问题
要使用IOCP,你得做三件事:
1.将一个SOCKET与一个完成端口关联
2.处理异步IO调用
3.同步线程
取得异步IO的结果,你得传递两个参数:CompletionKey,OVERLAPPED
CompletionKey--DWORD 你可以通过它传递唯一的值,一般情况下,它是一个指针,指向一个结构或类,包含一些客户端的一些信息
OVERLAPPED--结构 通常用来传递内存缓冲区以用于异步IO调用
调用PostQueuedCompletionStatus进行异步IO调用
调用GetQueuedCompletionStatus进行线程同步
4个IOCP常遇到的问题及解决方案
WSAENOBUFS错误,通常会以为是普通的死锁及内存泄漏
对服务器进行压力测试可能会出现这个错误
--解决方法:调用WSARead读取0字节缓冲区,当调用完成,我们知道有数据进入了TCP/IP栈
,然后通过调用几个异步WSARead(MAXIMUMPACKAGESIZE最大包字节)
这个方法锁定内存当数据到达时,解决了WSAENOBUFS这个错误,但是这个方法减少了服务器的数据吞吐量
包重排序问题
解决方案:添加序数到缓冲类中
在接收及发送中进行包排序
异步未决读取和大块字节处理问题
大部份的服务协议是以包为基础的协议,在包的开始X字节为头部,在头部包含了整个包的长度详情
大块字节流可能包含一个和几个通信包,也可能只包含包的一部份
存取异常
相对于IOCP的专属问题,这算是小问题了
当客户端连接丢失,并且IO调用反回一个错误标志,那么这个客户端已经死掉了
在CompletionKey,这是一个指针,包含了客户端的相关数据,如果我们释放它占用的内存,那么当其它的IO调用从相同的客户端
返回错误标志,当我们将CompletionKey转换为指针,尝试访问或者删除它时,这时将发生存取异常
解决方案:添中一个记数到结构中,这个记数包含了有多少未决的IO调用,然后当我们删除这个结构时,如果这个数为0,表明没有IO调用再需要它了
一些经验技巧
1不要在没有锁定的情况下就对ClientContext进行读取与写入
2避免或特别小心死锁
pContext-> m_ContextLock.Lock();
//… code code ..
pContext2-> m_ContextLock.Lock();
// code code..
pContext2-> m_ContextLock.Unlock();
// code code..
pContext-> m_ContextLock.Unlock();
上面这段代码可有造成死锁
3不要在通知函数之外存取ClientContext,除非你用临界区将其包起来