网络模型之IOCP与扩展函数

Posted CPP编程客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络模型之IOCP与扩展函数相关的知识,希望对你有一定的参考价值。

上次我们用IOCP实现了一个简单的服务器,在处理消息方面性能已经不错了,但是接爱请求函数却依旧使用的是accept函数,所以这部分性能并不够,而Windows在扩展函数中为我们提供了一些选择,本篇就来介绍这些函数。

0x0. AcceptEx

这个函数用来异步地投递一个调用来接受客户端连接,其原型如下:

 1BOOL AcceptEx 
2  SOCKET sListenSocket,          // 监听套接字
3  SOCKET sAcceptSocket,          // 分配给待连接客户端的套接字
4  PVOID lpOutputBuffer,          // 用来接收用户第一份数据的缓冲区
5  DWORD dwReceiveDataLength,     // 缓冲区的字节数
6  DWORD dwLocalAddressLength,    // 本地套接字地址结构大小
7  DWORD dwRemoteAddressLength,   // 远程套接字地址结构大小
8  LPDWORD lpdwBytesReceived,     // 新建的客户机连接上所收到的字节数
9  LPOVERLAPPED lpOverlapped      // 重叠结构
10)
;

这个函数稍微比accept函数麻烦了一点,那么在这里我们列出accept的原型来对比着展开介绍:

1SOCKET accept (
2  SOCKET s,                   
3  struct sockaddr FAR* addr,  
4  int FAR* addrlen            
5)
;

AcceptEx函数第一个参数也是监听套接字,而第二个参数是客户端的套接字,这说明accept函数是在接受客户端连接后才为其创建套接字的,而AcceptEx函数却是先创建好套接字,在客户端连接进来时直接给它用。这就是这个函数效率高之所在,因为创建套接字花费颇高,当用户连接时才为其创建套接字会导致处理变慢。

第三个参数是用来接收客户端的第一份数据的,在客户端连接后AcceptEx函数就顺便将其第一份数据接收了,就保存在这里,而第四个参数就是接收这份数据的缓冲区大小。而我们若是想像accept函数那样只处理接受请求,后面再接收第一份数据,我们需要将缓冲区的长度设置为0,即第四个参数置0。

The number of bytes reserved for the remote address information. This must be at least 16 bytes more than the maximum address length for the transport protocol in use.

这两个参数是保存在缓冲区中的,所以实际我们的缓冲区大小应该减去这字节的大小,所以dwReceiveDataLength应该传入:len - (sizeof(SOCKADDR_IN) + 16) * 2。

lpdwBytesReceived用来保存所接收第一份数据的实际大小,最后一个参数是重叠参数,我们在重叠IO中就说过,一个函数若有此参数,即表明它是异步的,所以AcceptEx函数就是异步的。

0x1. GetAcceptExSockaddrs

 1VOID GetAcceptExSockaddrs 
2  PVOID lpOutputBuffer,         // 缓冲区
3  DWORD dwReceiveDataLength,    // 缓冲区字节数
4  DWORD dwLocalAddressLength,   // 本地地址长度
5  DWORD dwRemoteAddressLength,  // 远程地址长度
6  LPSOCKADDR *LocalSockaddr,    // 本地socket地址
7  LPINT LocalSockaddrLength,    // 本地socket地址长度
8  LPSOCKADDR *RemoteSockaddr,   // 远程socket地址
9  LPINT RemoteSockaddrLength    // 远程socket地址长度
10)
;

0x2.加载扩展函数

AcceptEx和GetAcceptExSockaddrs都包含在< MSWSock.h>中,可以直接通过链接mswsock库来使用这两个函数,但是因为扩展函数并不存在于Winsock2的结构体系中,所以这种方式消耗较大,这里我将介绍动态获取这些函数的方法。

Winsock2提供了WSAIoctl函数用于对Windows套接字提供IO控制,之前我们还用过Winsock1中的ioctlsocket函数,只是WSAIoctl提供了更多的选项(ctl是control的缩写),我们可以通过这个函数来动态获取到扩展函数。其原型如下:

 1int WSAIoctl (
2  SOCKET s,               // IO操作的套接字                                               
3  DWORD dwIoControlCode,  // IO控制命令                                  
4  LPVOID lpvInBuffer,     // 指向传入数据缓冲区                     
5  DWORD cbInBuffer,       // 数据的长度                   
6  LPVOID lpvOUTBuffer,    // 指向返回的数据缓冲区                        
7  DWORD cbOUTBuffer,      // 返回数据的长度                          
8  LPDWORD lpcbBytesReturned,   // 实际返回的字节数                             
9  LPWSAOVERLAPPED lpOverlapped,  // 重叠结构           
10  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE  // 例行程序
11)
;

要获取扩展函数,dwIoControlCode应该指定SIO_GET_EXTENSION_FUNCTION_POINTER控制命令。

 1LPFN_ACCEPTEX lpfnAcceptEx;  // AcceptEx函数指针
2LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs;  // GetAcceptExSockaddrs函数指针
3GUID guidAcceptEx = WSAID_ACCEPTEX;  // AcceptEx数据
4GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;  // GetAcceptExSockaddrs数据
5DWORD dwBytes;
6
7// 加载AcceptEx函数
8WSAIoctl(servSock,
9    SIO_GET_EXTENSION_FUNCTION_POINTER,
10    &guidAcceptEx, sizeof(guidAcceptEx),
11    &lpfnAcceptEx, sizeof(lpfnAcceptEx),
12    &dwBytes, NULLNULL
13);
14
15// 加载GetAcceptExSockaddrs函数
16WSAIoctl(servSock,
17    SIO_GET_EXTENSION_FUNCTION_POINTER,
18    &guidGetAcceptExSockaddrs, sizeof(guidGetAcceptExSockaddrs),
19    &lpfnGetAcceptExSockaddrs, sizeof(lpfnGetAcceptExSockaddrs),
20    &dwBytes, NULLNULL
21);

大家可以看到,获取扩展函数其实并不麻烦。传入要获取的数据,WSAIoctl返回该数据的函数指针,GUID类型描述了全局唯一的一个ID,需要用它来指定要获取的数据,具体代码中已经很清晰了,便不多说了,若还有哪里不明白可以留言。

具体使用就放到下篇了,网络编程已经由浅入深地写了好长时间了,不知大家都理解了多少呢?可能这里好多人并不是搞服务器的(我也不是),这些大多其实也都不是C++的东西,而是关于操作系统的,所以我有时间就快点把网络编程这一系列结束吧。我前段时间抽时间学习了下Linux的epoll模型,感觉比iocp实现起来要简单,说起来都是对select模型的改进,再把iocp用一篇写完就顺便说下epoll模型,以全面的介绍Windows和Linux上主要的网络模型,让大家可以对比着参考理解,接着把asio说了就返回写C++的东西了。

以上是关于网络模型之IOCP与扩展函数的主要内容,如果未能解决你的问题,请参考以下文章

Libevent源码分析--- IOCP

Libevent源码分析--- IOCP

[杂烩]Windows IOCP与Linux的epoll机制对比

socket通讯IOCP模型

ConnectEx 与 IOCP 问题

winsock编程IOCP模型实现代码