ImageVerifierCode 换一换
格式:DOC , 页数:18 ,大小:75.50KB ,
资源ID:954993      下载积分:15 文钱
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,省得不是一点点
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.wenke99.com/d-954993.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: QQ登录   微博登录 

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(WinSock2编程之打造完整的SOCKET池.DOC)为本站会员(天***)主动上传,文客久久仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知文客久久(发送邮件至hr@wenke99.com或直接QQ联系客服),我们立即给予删除!

WinSock2编程之打造完整的SOCKET池.DOC

1、WinSock2 编程之打造完整的 SOCKET 池在 Winodows 平台上,网络编程的主要接口就是 WinSock,目前大多数的 Windows 平台上的WinSock 平台已经升级到 2.0 版,简称为 WinSock2。在 WinSock2 中扩展了很多很有用的 Windows 味很浓的 SOCKET 专用 API,为 Windows 平台用户提供高性能的网络编程支持。这些函数中的大多数已经不再是标准的“Berkeley” 套接字模型的 API 了。使用这些函数的代价就是你不能再将你的网络程序轻松的移植到“尤里平台”(我给 Unix +Linux 平台的简称)下,反过来因为 Wind

2、ows 平台支持标准的“Berkeley”套接字模型,所以你可以将大多数尤里平台下的网络应用移植到 Windows 平台下。如果不考虑可移植性(或者所谓的跨平台性),而是着重于应用的性能时,尤其是注重服务器性能时,对于 Windows 的程序,都鼓励使用 WinSock2 扩展的一些 API,更鼓励使用 IOCP 模型,因为这个模型是目前 Windows 平台上比较完美的一个高性能 IO 编程模型,它不但适用于 SOCKET 编程,还适用于读写硬盘文件,读写和管理命名管道、邮槽等等。如果再结合 Windows 线程池,IOCP 几乎可以利用当今硬件所有可能的新特性(比如多核,DMA,高速总线等

3、等),本身具有先天的扩展性和可用性。今天讨论的重点就是 SOCKET 池。很多 VC 程序员也许对 SOCKET 池很陌生,也有些可能很熟悉,那么这里就先讨论下这个概念。在 Windows 平台上 SOCKET 实际上被视作一个内核对象的句柄,很多 Windows API 在支持传统的 HANDLE 参数的同时也支持 SOCKET,比如有名的 CreateIoCompletionPort 就支持将 SOCKET 句柄代替 HANDLE 参数传入并调用。熟悉 Windows 内核原理的读者,立刻就会发现,这样的话,我们创建和销毁一个 SOCKET 句柄,实际就是在系统内部创建了一个内核对象,对于

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

5、开的 SOCKET 对象,不是简单的“销毁”了之(很多时候“断开”的含义不一定就等价于“ 销毁” ,可以仔细思考一下),更多时候希望能够重用这个 SOCKET 对象,这样我们甚至可以事先创建一批 SOCKET 对象组成一个“ 池”,在需要的时候“重用” 其中的 SOCKET 对象,不需要的时候将 SOCKET 对象重新丢入池中即可,这样就省去了频繁创建销毁 SOCKET 对象的性能损失。在原始的“Berkeley” 套接字模型中,想做到这点是没有什么办法的。而幸运的是在 Windows 平台上,尤其是支持 WinSock2 的平台上,已经提供了一套完整的 API 接口用于支持 SOCKET 池

6、。对于符合以上要求的 SOCKET 池,首先需要做到的就是对 SOCKET 句柄的“回收”,因为创建函数无论在那个平台上都是现成的,而最早能够实现这个功能的 WinSock 函数就是 TransmitFile,如果代替closesocket 函数像下面这样调用就可以“ 回收”一个 SOCKET 句柄,而不是销毁:(注意“回收”这个功能对于 TransmitFile 函数来说只是个“ 副业”。)TransmitFile(hSocket,NULL,0,0,NULL,NULL,TF_DISCONNECT | TF_REUSE_SOCKET );注意上面函数的最后一个参数,使用了标志 TF_DISCO

7、NNECT 和 TF_REUSE_SOCKET,第一个值表示断开,第二个值则明确的表示“ 重用” 实际上也就是回收这个 SOCKET,经过这个处理的 SOCKET句柄,就可以直接再用于 connect 等操作,但是此时我们会发现,这个回收来的 SOCKET 似乎没什么用,因为其他套接字函数没法直接利用这个回收来的 SOCKET 句柄。这时就要 WinSock2 的一组专用 API 上场了。我将它们按传统意义上的服务端和客户端分为两组:一、 服务端:SOCKET WSASocket(_in int af,_in int type,_in int protocol,_in LPWSAPROTOCO

8、L_INFO lpProtocolInfo,_in GROUP g,_in DWORD dwFlags);BOOL AcceptEx(_in SOCKET sListenSocket,_in SOCKET sAcceptSocket,_in PVOID lpOutputBuffer,_in DWORD dwReceiveDataLength,_in DWORD dwLocalAddressLength,_in DWORD dwRemoteAddressLength,_out LPDWORD lpdwBytesReceived,_in LPOVERLAPPED lpOverlapped);BOO

9、L DisconnectEx(_in SOCKET hSocket,_in LPOVERLAPPED lpOverlapped,_in DWORD dwFlags,_in DWORD reserved);二、 客户端:SOCKET WSASocket(_in int af,_in int type,_in int protocol,_in LPWSAPROTOCOL_INFO lpProtocolInfo,_in GROUP g,_in DWORD dwFlags);BOOL PASCAL ConnectEx(_in SOCKET s,_in const struct sockaddr* na

10、me,_in int namelen,_in_opt PVOID lpSendBuffer,_in DWORD dwSendDataLength,_out LPDWORD lpdwBytesSent,_in LPOVERLAPPED lpOverlapped);BOOL DisconnectEx(_in SOCKET hSocket,_in LPOVERLAPPED lpOverlapped,_in DWORD dwFlags,_in DWORD reserved);注意观察这些函数,似乎和传统的“Berkeley”套接字模型中的一些函数“ 大同小异”,其实仔细观察他们的参数,就已经可以发现一

11、些调用他们的“玄机” 了。首先我们来看 AcceptEx 函数,与 accept 函数不同,它需要两个 SOCKET 句柄作为参数,头一个参数的含义与 accept 函数的相同,而第二个参数的意思就是 accept 函数返回的那个代表与客户端通信的SOCKET 句柄,在传统的 accept 内部,实际在返回那个代表客户端的 SOCKET 时,是在内部调用了一个 SOCKET 的创建动作,先创建这个 SOCKET 然后再“accept”让它变成代表客户端连接的 SOCKET,而 AcceptEx 函数就在这里“扩展”(实际上是“阉割” 才对)accept 函数,省去了内部那个明显的创建SOCKE

12、T 的动作,而将这个创建动作交给最终的调用者自己来实现。 AcceptEx 要求调用者创建好那个sAcceptSocket 句柄然后传进去,这时我们立刻发现,我们回收的那个 SOCKET 是不是也可以传入呢?答案是肯定的,我们就是可以利用这个函数传入那个“回收” 来的 SOCKET 句柄,最终实现服务端的SOCKET 重用。这里需要注意的就是,AcceptEx 函数必须工作在非阻塞的 IOCP 模型下,同时即使 AcceptEx 函数返回了,也不代表客户端连接进来或者连接成功了,我们必须依靠它的“完成通知” 才能知道这个事实,这也是 AcceptEx 函数区别于 accept 这个阻塞方式函数

13、的最大之处。通常可以利用 AcceptEx 的非阻塞特性和 IOCP 模型的优点,一次可以“预先” 发出成千上万个 AcceptEx 调用,“ 等待”客户端的连接。对于习惯了accept 阻塞方式的程序员来说,理解 AcceptEx 的工作方式还是需要费一些周折的。下面的例子就演示了如何一次调用多个 AcceptEx:/批量创建 SOCKET,并调用对应的 AcceptExfor(UINT i = 0; i GetAddrBuf();/4、发出 AcceptEx 调用/注意将 AcceptEx 函数接收连接数据缓冲的大小设定成了 0,这将导致此函数立即返回,虽然与/不设定成 0 的方式而言,这

14、导致了一个较低下的效率,但是这样提高了安全性,所以这种效率/牺牲是必须的if(!AcceptEx(m_skServer, skAccept,pAddrBuf-m_pBuf, 0,/将接收缓冲置为 0,令 AcceptEx 直接返回,防止拒绝服务攻击GRS_ADDRBUF_SIZE, GRS_ADDRBUF_SIZE, NULL,(LPOVERLAPPED)pAcceptOL)int iError = WSAGetLastError();if( ERROR_IO_PENDING != iError skAccept = INVALID_SOCKET;if( NULL != pAcceptOL)G

15、RS_ISVALID(pAcceptOL,sizeof(CGRSOverlappedData);delete pAcceptOL;pAcceptOL = NULL;以上的例子只是简单的演示了 AcceptEx 的调用,还没有涉及到真正的“回收重用” 这个主题,那么下面的例子就演示了如何重用一个 SOCKET 句柄:if(INVALID_SOCKET = skClient)throw CGRSException(_T(“SOCKET 句柄是无效的!“);OnPreDisconnected(skClient,pUseData,0);CGRSOverlappedData*pData= new GRS

16、OverlappedData(GRS_OP_DISCONNECTEX,this,skClient,pUseData);/回收而不是关闭后再创建大大提高了服务器的性能DisconnectEx(skClient, ./在接收到 DisconnectEx 函数的完成通知之后,我们就可以重用这个 SOCKET 了CGRSAddrbuf*pBuf = NULL;pNewOL = new CGRSOverlappedData(GRS_OP_ACCEPT,this,skClient,pUseData);pBuf = pNewOL-GetAddrBuf();/把这个回收的 SOCKET 重新丢进连接池if(!

17、AcceptEx(m_skServer,skClient,pBuf-m_pBuf, 0,/将接收缓冲置为 0,令 AcceptEx 直接返回,防止拒绝服务攻击GRS_ADDRBUF_SIZE, GRS_ADDRBUF_SIZE, NULL,(LPOVERLAPPED)pNewOL)int iError = WSAGetLastError();if( ERROR_IO_PENDING != iError /注意在这个 SOCKET 被重新利用后,重新与 IOCP 绑定一下,该操作会返回一个已设置的错误,这个错误直接被忽略即可:BindIoCompletionCallback(HANDLE)skC

18、lient,Server_IOCPThread, 0);至此回收重用 SOCKET 的工作也就结束了,以上的过程实际理解了 IOCP 之后就比较好理解了,例子的最后我们使用了 BindIoCompletionCallback 函数重新将 SOCKET 丢进了 IOCP 线程池中,实际还可以再次使用 CreateIoCompletionPort 函数达到同样的效果,这里列出这一步就是告诉大家,不要忘了再次绑定一下完成端口和 SOCKET。对于客户端来说,可以使用 ConnectEx 函数来代替 connect 函数,与 AcceptEx 函数相同,ConnectEx 函数也是以非阻塞的 IOCP

19、 方式工作的,唯一要注意的就是在 WSASocket 调用之后,在ConnectEx 之前要调用一下 bind 函数,将 SOCKET 提前绑定到一个本地地址端口上,当然回收重用之后,就无需再次绑定了,这也是 ConnectEx 较之 connect 函数高效的地方之一。与 AcceptEx 函数类似,也可以一次发出成千上万个 ConnectEx 函数的调用,可以连接到不同的服务器,也可以连接到相同的服务器,连接到不同的服务器时,只需提供不同的 sockaddr 即可。通过上面的例子和讲解,大家应该对 SOCKET 池概念以及实际的应用有个大概的了解了,当然核心仍然是理解了 IOCP 模型,否

20、则还是寸步难行。在上面的例子中,回收 SOCKET 句柄主要使用了 DisconnectEx 函数,而不是之前介绍的TransmitFile 函数,为什么呢?因为 TransmitFile 函数在一些情况下会造成死锁,无法正常回收SOCKET,毕竟不是专业的回收重用 SOCKET 函数,我就遇到过好几次死锁,最后偶然的发现了DisconnectEx 函数这个专用的回收函数,调用之后发现比 TransmitFile 专业多了,而且不管怎样都不会死锁。最后需要补充的就是这几个函数的调用方式,不能像传统的 SOCKET API 那样直接调用它们,而需要使用一种间接的方式来调用,尤其是 AcceptE

21、x 和 DisconnectEx 函数,下面给出了一个例子类,用于演示如何动态载入这些函数并调用之:class CGRSMsSockFunpublic:CGRSMsSockFun(SOCKET skTemp = INVALID_SOCKET)if( INVALID_SOCKET != skTemp )LoadAllFun(skTemp);public:virtual CGRSMsSockFun(void)protected:BOOL LoadWSAFun(SOCKETBOOL bRet = TRUE;pFun = NULL;BOOL bCreateSocket = FALSE;tryif(IN

22、VALID_SOCKET = skTemp)skTemp = :WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);bCreateSocket = (skTemp != INVALID_SOCKET);if(INVALID_SOCKET = skTemp)throw CGRSException(DWORD)WSAGetLastError();if(SOCKET_ERROR = :WSAIoctl(skTemp,SIO_GET_EXTENSION_FUNCTION_POINTER, throw CGRSException(DWORD)WSAGetLastError();catch(CGRSExceptionreturn NULL != pFun;protected:LPFN_ACCEPTEX m_pfnAcceptEx;

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。