IOCP SOCKET池的实现

来源:互联网 发布:mysql win10 64位安装 编辑:程序博客网 时间:2024/04/29 20:32

1. SOCKET池的必要性:

创建和销毁一个SOCKET句柄,实际就是在系统内部创建了一个内核对象,对于Windows来说这牵扯到从Ring3层到Ring0层的耗时操作,再加上复杂的安全审核机制,实际创建和销毁一个SOCKET内核对象的成本还是蛮高的。尤其对于一些面向连接的SOCKET应用,服务端往往要管理n多个代表客户端通信的SOCKET对象,而且因为客户的变动性,主要面临的大量操作除了一般的收发数据,剩下的就是不断创建和销毁SOCKET句柄,对于一个频繁接入和断开的服务器应用来说,创建和销毁SOCKET的性能代价立刻就会体现出来,典型的例如WEB服务器程序,就是一个需要频繁创建和销毁SOCKET句柄的SOCKET应用。


2. SOCKET如何回收和复用

在原始的“Berkeley”套接字模型中,想复用SOCKET是没有什么办法的。而在Windows平台上,尤其是支持WinSock2的平台上,已经提供了一套完整的API接口用于支持SOCKET池。

DisconnectEx方法使得“回收”SOCKET成为可能。

调用DisconnectEx和CloseSocket方法相比,DisconnectEx方法是“graceful”的。

当正在使用的SOCKET出现异常,释放用户资源,投递DisconnectEx。

投递DisconnectEx和AcceptEx类似,按照相关文档,需创建个新的OverLapped结构体。

   //投递之前,创建新的OverLapped   SIOCPRWContext* pRWContext = m_IOCPManager.GetFreeIOContext("Disconnnect param",false,mSocket,IOCP_SOCKET_DISCONNECTED,NULL,DEFAULT_BUFFER_LEN);    CancelIo((HANDLE)mSocket); //取消投递   BOOL bFlag= m_pfnDisconnectEx(pRWContext->m_socket,&pRWContext->m_overlapped,TF_REUSE_SOCKET,0);   if ((bFlag == FALSE) && (WSA_IO_PENDING != WSAGetLastError()))   {   // log   }
当DisconnectEx投递返回后,将该socket入池。

本方案中使用Queue来存放。

queue<SOCKET>q;q.push(mSock);
在多线程调用Queue时,当然要考虑线程安全问题,这个和本主题没有直接关联。

AcceptEx 和DisconnectEx的配套使用:


如果采用传统的Accept方式,复用的SOCKET也没有办法直接使用,AcceptEx函数的引入,使复用成为可能。AccpetEx要求server端Listen成功后,投递需要数量的Accept,Accept投递的Socket是先要创建的,这样就可以New也可以从Socket池中获取。

     // 投递AcceptEx

    bResult=m_lpfnAcceptEx(m_ListenSocket, pNewRWContext->m_socket, pNewRWContext->m_wsaBuf.buf, 0,sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, &dwBytes, &pNewRWContext->m_overlapped);    if ((bResult == FALSE) && (WSA_IO_PENDING != WSAGetLastError())){    // Log}
pNewRWContext中的m_socket是从Queue中Pop出来的SOCKET,这样就完成了复用。

3. 问题

当客户端异常断开SOCKET后,DisconnectEx投递可以立即返回;但是当Server端在处理超时Client时,投递需要等待一段时间才能返回,这是因为SOCKET进入了TIME_WAIT状态,关于这个主题见前一篇转载的文章《TCP的TIME_WAIT状态》,文章中有详细的说明。

 

对于C#来说,SocketAsyncEventArgs的DisconnectReuseSocket属性与socket复用相关。此属性用于改变Socket.DisconnectAsync 方法的行为。如果为 true,则由Socket.DisconnectAsync 方法断开的套接字在断开操作完成之后,可以在后续的套接字接受或连接操作中重用。底层的实现应该也是基于DisconnectEX。

 

对于SOCKET回收,身边也有牛人不是很认同,理由是如果SOCKET的处理如果不当,会引发严重的系统问题。为了保证系统的稳定性,直接调用closesocket方法来的更稳妥些。复用SOCKET是否影响系统稳定性,需要长期运行,用实际测试效果来说话。

参考文献

http://gamebabyrocksun.blog.163.com/blog/static/571534632010115104634310/

1 0
原创粉丝点击