IOCP

1.IOCP自动维护worker线程,没必要将worker组织为线程池,即不采用IOCP+线程池

 

2.

lpCompletionKey与File Handle关联,每当File Handle上的请求完成时lpCompletionKey都会随IO完成包出现在完成队列。

File Handle出现问题,之前投递的所有请求都会返回,如何释放lpCompletionKey堆空间?

有做法如下,使用引用计数,外带一把锁,投递请求计数加1,请求完成计数减一,检查计数为0,则删除lpCompletionKey,删除引用计数,销毁锁。

问题:设线程A于SOCKET s上已投递WSARecv,则s对象引用计数为1;某时刻线程B于s投递WSASend或WSARecv,投递成功、请求未完成,则B主动申请s锁以递增引用计数。若恰在此时连接被Client关闭,则A和B投递的请求均从GetQueuedCompletionStatus返回,则两者都将申请s锁以递减引用计数。假设A投递的请求返回后由线程D处理,D先与B获取到s锁,递减引用计数后值为0,则执行清理操作。D释放lpCompletionKey堆空间、释放引用计数、销毁锁,那么B将面临锁被销毁的不确定因素和之后访问引用计数带来的内存崩溃。若D仅释放lpCompletionKey,则程序长时间运行必将导致引用计数和锁资源的泄露问题。

AlarmServer面对此问题的解决方案:

    不使用任何lpCompletionKey:CreateIoCompletionPort((HANDLE)s, hCompletionPort, NULL, 0);

    或仅传入普通变量SOCKET:CreateIoCompletionPort((HANDLE)s, hCompletionPort, s, 0);

    一切记录工作由PER_IO_DATA完成。

    切忌传入堆地址CreateIoCompletionPort((HANDLE)s, hCompletionPort, pphd, 0);势必将引入引用计数、临界区、堆释放的邪恶深渊。


该问题的解决方法:

对于每条连接,同时投递CPU个数的接收,设CPU个数为n。

连接出现异常,如主动关闭或对方关闭时,处理PER_HANDLE_DATA的线程原子递增该结构中的计数器iReleaseCount。

iReleaseCount == n,则对此结构进行清理操作。

清理完成,此结构加入空闲列表表尾,空闲列表长度超过限定,从表头delete释放空间。


3. 

BOOL WINAPI GetQueuedCompletionStatus(
  _In_   HANDLE CompletionPort,
  _Out_  LPDWORD lpNumberOfBytes,
  _Out_  PULONG_PTR lpCompletionKey,
  _Out_  LPOVERLAPPED *lpOverlapped,
  _In_   DWORD dwMilliseconds
);

Client关闭连接的判断条件是:bRet == true && lpNumberOfBytes == 0 && OP_ACCEPT != iTypeOpt


4. 把所有的closesocket(s)放在一个函数里面:在连接异常断开时,在该函数中添加断点,方便定位。

原文地址:https://www.cnblogs.com/chaikefusibushiji/p/6775784.html