Windows socket之重叠IO:事件通知
2017-07-19 10:59
465 查看
转载自:http://blog.csdn.net/ithzhang/article/details/8496232
关于异步IO机制可以参考:《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步设备IO。
Windows socket重叠IO延续了win32 IO模型。从发送和接收的角度来看,重叠IO模型与前面介绍的Select模型、WSAAsyncSelect模型和WSAEventSelect模型都不同。因为在这三个模型中IO操作还是同步的,例如:在应用程序调用recv函数时,都会在recv函数内阻塞,直到接收数据完毕后才返回。而重叠IO模型会在调用recv后立即返回。等数据准备好后再通知应用程序。
系统向应用程序发送通知的形式有两种:一是事件通知。二是完成例程。后面将会介绍这两种形式。
注意:套接字的重叠IO属性不会对套接字的当前工作模式产生影响。创建具有重叠属性的套接字执行重叠IO操作,并不会改变套接字的阻塞模式。套接字的阻塞模式与重叠IO操作不相关。重叠IO模型仅仅对WSASend和WSARecv的行为有影响。 对listen,如果是阻塞模式,直到有客户请求到达时才会返回。这点要特别注意。
前三个模型的第一个阶段的区别: select模型利用select函数主动检查系统中套接字是否满足可读条件。WSAAsyncSelect模型和WSAEventSelect模型则被动等待系统的通知。
第二个阶段的区别:此阶段前三个模型基本相同,在将数据从系统复制到用户缓冲区时,线程阻塞。而重叠IO与它们都不同,应用程序在调用输入函数后,只需等待接收系统完成的IO操作完成通知。
套接字重叠IO模型主要有一下相关函数:
当使用socket函数创建套接字时,会默认设置WSA_FLAG_OVERLAPPED标志。
WSARecv函数 介绍如下:
s :为接收套接字。
lpBuffers :接受缓冲区。
dwBufferCount :为接收缓冲区数组元素的个数。如果就一个缓冲区,就指定为1.
lpNumberOfBytesRecvd :如果接收操作立即完成,该参数指明接收数据的字节数。
lpFlags :标志位,一般为0.
lpOverlapped :指向WSAOVERLAPPED结构指针。
lpCompletionROUTINE :完成例程。
该函数可以使用一个或多个缓冲区接收数据。如果接收操作未能立即完成。应用程序利用完成例程或是事件对象获得通知。
如果操作立即完成,该函数返回值为0。lpNumberOfBytesRecvd参数指明接收数据的字节数。如果未能立即完成,函数返回SOCKET_ERROR值。WSAGetLastError返回WSA_IO_PENDING。
如果lpOverlapped和lpCompletionROUTINE都为NULL,则该套接字作为同步IO套接字使用。
如果lpCompletionRoutine参数为NULL,当接收完成时,lpOverlapped参数的事件变为触发状态。在应用程序中可以调用WSAWaitForMultipleEvents或者是WSAGetOverlappedResult等待该事件。
重叠IO采用事件或者是完成例程通知程序异步操作已完成。这可以通过调用WSASend或WSARecv来实现。之所以前面首先介绍WSARecv是因为:我们并不知道客户发送的数据何时到来。当在有客户请求进来时,我们就调用了WSARecv函数,并采用重叠IO模型,并且在收到数据后,再次调用WSARecv,这样在任何时刻只要此客户的数据到来我们就会收到通知。如果应用程序调用WSARecv的时间先于数据到达的时间,则数据到达时会直接放入用户的接收缓冲区。这就避免了从系统缓冲区向用户缓冲区的复制操作。对于提高性能很有帮助。
WSBUF定义如下:
WSAOVERLAPPED结构。
WSARecv函数使用WSAOVERLAPPED结构作为参数,将事件对象和重叠IO关联在一起。该结构声明如下:
4000
应用程序可以执行一下步骤将一个事件对象与套接字关联起来:
调用WSACreateEvent创建事件对象。
将该事件赋值给WSAOVERLAPPED结构的hEvent字段。
使用该重叠结构,调用WSASend或WSARecv函数。
当重叠操作完成时,重叠IO结构的事件对象变为已触发状态。可以调用WSAWaitForMultipleEvents函数等待该事件发生。关于该函数,前面在介绍WSAEventSelect模型时有详细的介绍,此处不再赘述。注意它最多只能等待64个事件对象。
WSAGetOverlappedResult函数。
该函数返回在套接字重叠IO的结构:
s为发起重叠操作的套接字。
lpOverlapped发起重叠操作的WSAOVERLAPPED结构指针。
lpcbTransfer:实际发送或接收的字节数。
fWait:函数返回的方式。如果为TRUE,该函数直到重叠IO完成时才返回。当为false时,如果操作仍然处于等待执行状态,则函数返回false。错误代码为WSA_IO_INCOMPLELE。
lpdwFlags:接收完成状态的附加标志。
当函数返回TRUE时,重叠IO已经完成,lpOverlapped指明实际返回的数据。当函数返回false时,重叠IO还未完成。
注意:由于微软已经公布了WSAOVERLAPPED和OVERLAPPED结构的个字节的意义。Internal表示错误代码。InternalHigh表示已传输字节数。因此在调用WSAWaitForMultipleEvents等待事件对象返回后,得到此事件对应的WSAOVERLAPPED结构后,就可以根据这两个字段判断异步IO的执行结果。无需再调用GetOverlappedResult结构。具体请参考《Windows核心编程》。
WSASend函数声明如下:
s:为发送套接字。
lpBuffer:指向WSABUF结构指针,用于发送数据。
dwBufferCount : lpBuffers数组中元素数量。
lpNumberOfBytesSent:如果发送立即完成,该参数指明发送字节数。
lpFlags:标志位。
lpOverlapped : 指向WSAOVERLAPPED结构指针。
lpCompletionROUTINE:完成例程。
分为以下步骤:
创建具有WSAOVERLAPPED标志的套接字。如果调用socket()函数,则默认创建具有WSAOVERLAPPED标志的套接字。如果调用WSASocket函数,需要指定WSAOVERLAPPED标志。
为套接字定义WSAOVERLAPPED结构,并清零。
调用WSACreateEvent函数创建事件对象,并将该事件句柄分配给WSAOVERLAPPED结构的hEvent字段。
调用输入或者输出函数。
调用WSAWaitForMultipleEvents函数等待与重叠IO关联在一起的事件变为已触发状态。
WSAWaitForMultipleEvents返回后,调用WSAResetEvent函数,将该事件对象恢复为未触发态。
调用WSAGetOverlappedResult函数判断重叠IO的完成状态。
下面一个实例演示了使用socket 重叠IO模型开发服务程序的步骤。该程序设计两个线程,接收线程用于接受客户端连接请求,初始化重叠IO操作。服务线程用于重叠IO处理。
CClient:该类在前面各模型的介绍中都涉及过。它主要用于管理连接后的客户端。
管理客户端链表:存储CClient*,用于管理连接的客户端。
接受线程:
服务线程:
如有纰漏,请不吝赐教!!!
2013.1.12于山西大同
Windows socket重叠IO模型开发
利用套接字重叠IO模型,应用程序能一次投递一个或多个IO请求,当系统完成IO操作后通知应用程序。该模型以win32异步IO机制为基础。与前面介绍的所有IO模型相比较,该模型是真正意义上的异步IO模型,它能使Windows socket应用程序达到更高的性能。关于异步IO机制可以参考:《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步设备IO。
Windows socket重叠IO延续了win32 IO模型。从发送和接收的角度来看,重叠IO模型与前面介绍的Select模型、WSAAsyncSelect模型和WSAEventSelect模型都不同。因为在这三个模型中IO操作还是同步的,例如:在应用程序调用recv函数时,都会在recv函数内阻塞,直到接收数据完毕后才返回。而重叠IO模型会在调用recv后立即返回。等数据准备好后再通知应用程序。
系统向应用程序发送通知的形式有两种:一是事件通知。二是完成例程。后面将会介绍这两种形式。
注意:套接字的重叠IO属性不会对套接字的当前工作模式产生影响。创建具有重叠属性的套接字执行重叠IO操作,并不会改变套接字的阻塞模式。套接字的阻塞模式与重叠IO操作不相关。重叠IO模型仅仅对WSASend和WSARecv的行为有影响。 对listen,如果是阻塞模式,直到有客户请求到达时才会返回。这点要特别注意。
与其他模型的区别
在Windows socket中,接收数据的过程可以分为:等待数据和将数据从系统复制到用户空间两个阶段。各种IO模型的区别在于这两个阶段上。前三个模型的第一个阶段的区别: select模型利用select函数主动检查系统中套接字是否满足可读条件。WSAAsyncSelect模型和WSAEventSelect模型则被动等待系统的通知。
第二个阶段的区别:此阶段前三个模型基本相同,在将数据从系统复制到用户缓冲区时,线程阻塞。而重叠IO与它们都不同,应用程序在调用输入函数后,只需等待接收系统完成的IO操作完成通知。
套接字重叠IO模型主要有一下相关函数:
WSASocket() //创建套接字。 WSASend 和 WSASendTo //发送数据。 WSARecv 和 WSARecvFrom //接收数据。 WSAIoctl //控制套接字模式。 AcceptEx //接受连接。
创建套接字
要在套接字上使用重叠IO模型,在创建套接字时,必须使用WSA_FLAG_OVERLAPPED标志创建。SOCKET s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
当使用socket函数创建套接字时,会默认设置WSA_FLAG_OVERLAPPED标志。
接收数据
应用程序调用WSARecv和WSARecvFrom函数接收数据。WSARecv函数 介绍如下:
int WSARecv( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSWOVERLAPPED lpOverlapped, LPWSOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUNTINE);
s :为接收套接字。
lpBuffers :接受缓冲区。
dwBufferCount :为接收缓冲区数组元素的个数。如果就一个缓冲区,就指定为1.
lpNumberOfBytesRecvd :如果接收操作立即完成,该参数指明接收数据的字节数。
lpFlags :标志位,一般为0.
lpOverlapped :指向WSAOVERLAPPED结构指针。
lpCompletionROUTINE :完成例程。
该函数可以使用一个或多个缓冲区接收数据。如果接收操作未能立即完成。应用程序利用完成例程或是事件对象获得通知。
如果操作立即完成,该函数返回值为0。lpNumberOfBytesRecvd参数指明接收数据的字节数。如果未能立即完成,函数返回SOCKET_ERROR值。WSAGetLastError返回WSA_IO_PENDING。
如果lpOverlapped和lpCompletionROUTINE都为NULL,则该套接字作为同步IO套接字使用。
如果lpCompletionRoutine参数为NULL,当接收完成时,lpOverlapped参数的事件变为触发状态。在应用程序中可以调用WSAWaitForMultipleEvents或者是WSAGetOverlappedResult等待该事件。
重叠IO采用事件或者是完成例程通知程序异步操作已完成。这可以通过调用WSASend或WSARecv来实现。之所以前面首先介绍WSARecv是因为:我们并不知道客户发送的数据何时到来。当在有客户请求进来时,我们就调用了WSARecv函数,并采用重叠IO模型,并且在收到数据后,再次调用WSARecv,这样在任何时刻只要此客户的数据到来我们就会收到通知。如果应用程序调用WSARecv的时间先于数据到达的时间,则数据到达时会直接放入用户的接收缓冲区。这就避免了从系统缓冲区向用户缓冲区的复制操作。对于提高性能很有帮助。
WSBUF定义如下:
typedef struct _WSABUF { u_long len; //缓冲区长度。 har *buf; //缓冲区。 } WSABUF, *LPWSABUF;
WSAOVERLAPPED结构。
WSARecv函数使用WSAOVERLAPPED结构作为参数,将事件对象和重叠IO关联在一起。该结构声明如下:
typdef struct _WSAOVERLAPPED { DWROD Internal; //错误代码。 DWORD InternalHigh; //已传输字节数 DWORD Offset; //低32位文件偏移。 DWORD OffsetHigh; //高32位文件偏移 WSAEVENT hEvent; //事件对象句柄。 } WSAOVERLAPPED, *LPWSAOVERLAPPED;
4000
应用程序可以执行一下步骤将一个事件对象与套接字关联起来:
调用WSACreateEvent创建事件对象。
将该事件赋值给WSAOVERLAPPED结构的hEvent字段。
使用该重叠结构,调用WSASend或WSARecv函数。
当重叠操作完成时,重叠IO结构的事件对象变为已触发状态。可以调用WSAWaitForMultipleEvents函数等待该事件发生。关于该函数,前面在介绍WSAEventSelect模型时有详细的介绍,此处不再赘述。注意它最多只能等待64个事件对象。
WSAGetOverlappedResult函数。
该函数返回在套接字重叠IO的结构:
bool WSAGetOverlappedResult( SOCKET s, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags);
s为发起重叠操作的套接字。
lpOverlapped发起重叠操作的WSAOVERLAPPED结构指针。
lpcbTransfer:实际发送或接收的字节数。
fWait:函数返回的方式。如果为TRUE,该函数直到重叠IO完成时才返回。当为false时,如果操作仍然处于等待执行状态,则函数返回false。错误代码为WSA_IO_INCOMPLELE。
lpdwFlags:接收完成状态的附加标志。
当函数返回TRUE时,重叠IO已经完成,lpOverlapped指明实际返回的数据。当函数返回false时,重叠IO还未完成。
注意:由于微软已经公布了WSAOVERLAPPED和OVERLAPPED结构的个字节的意义。Internal表示错误代码。InternalHigh表示已传输字节数。因此在调用WSAWaitForMultipleEvents等待事件对象返回后,得到此事件对应的WSAOVERLAPPED结构后,就可以根据这两个字段判断异步IO的执行结果。无需再调用GetOverlappedResult结构。具体请参考《Windows核心编程》。
发送数据
应用程序调用WSASend或者WSASendTo发送数据。WSASend函数声明如下:
int WSASend( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE);
s:为发送套接字。
lpBuffer:指向WSABUF结构指针,用于发送数据。
dwBufferCount : lpBuffers数组中元素数量。
lpNumberOfBytesSent:如果发送立即完成,该参数指明发送字节数。
lpFlags:标志位。
lpOverlapped : 指向WSAOVERLAPPED结构指针。
lpCompletionROUTINE:完成例程。
事件通知
套接字重叠IO的事件通知方法要求事件对象与WSAOVERLAPPED结构关联在一起。当IO操作完成后,该事件对象从未触发状态变为触发状态。在应用程序中先调用WSAWaitForMultipleEvents函数等待该事件的发生。获得该事件对象对应的WSAOVERLAPPED结构后可以根据Internal和InternalHigh字段(也可以调用WSAGetOverlappedResult函数)判断IO完成的情况。分为以下步骤:
创建具有WSAOVERLAPPED标志的套接字。如果调用socket()函数,则默认创建具有WSAOVERLAPPED标志的套接字。如果调用WSASocket函数,需要指定WSAOVERLAPPED标志。
为套接字定义WSAOVERLAPPED结构,并清零。
调用WSACreateEvent函数创建事件对象,并将该事件句柄分配给WSAOVERLAPPED结构的hEvent字段。
调用输入或者输出函数。
调用WSAWaitForMultipleEvents函数等待与重叠IO关联在一起的事件变为已触发状态。
WSAWaitForMultipleEvents返回后,调用WSAResetEvent函数,将该事件对象恢复为未触发态。
调用WSAGetOverlappedResult函数判断重叠IO的完成状态。
下面一个实例演示了使用socket 重叠IO模型开发服务程序的步骤。该程序设计两个线程,接收线程用于接受客户端连接请求,初始化重叠IO操作。服务线程用于重叠IO处理。
CClient:该类在前面各模型的介绍中都涉及过。它主要用于管理连接后的客户端。
管理客户端链表:存储CClient*,用于管理连接的客户端。
接受线程:
#include"iostream" #include"Client.h" #pragma comment(lib,"WS2_32.lib") SOCKET sListenSocket; UINT totalEvent; //事件数组元素个数。 WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS]; //事件对象数组。 bool InitSocket() { WSAData wsa; WSAStartup(MAKEWORD(2,2),&wsa); //ServSocket = socket(AF_INET,SOCK_STREAM,0); sListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); if(sListenSocket == INVALID_SOCKET) { return false; } sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.100"); addr.sin_port = htons(4000); int ret = bind(sListenSocket, (SOCKADDR*)&addr, sizeof(addr)); if(ret == SOCKET_ERROR) { return false; } ret = listen(sListenSocket,10); if(ret == SOCKET_ERROR) { return false; } return true; } DWORD WINAPI AcceptThread(PVOID ppram) { SOCKET newSocket; while(true) { newSocket = accept(sListenSocket, NULL, NULL); if(totalEvent > WSA_MAXIMUM_WAIT_EVENTS) { return -1; } if(WSA_INVALID_EVENT == ([totalEvent] = WSACreateEvent())) { return -1; } CClient* pClient = new CClient(newSocket, eventArray[totalEvent]); //m_list.add(newSocket)//新接受的套接字加入套接字链表,用以对客户端的管理。 totalEvent++; } //DeleteAllNode(); closesocket(sListenSocket); WSACleanup(); return 0; }
服务线程:
DWORD WINAPI ServiceThread(PVOID pparam) { int dwIndex; while(true) { if((dwIndex = WSAWaitForMultipleEvents(totalEvent, eventArray, false, WSA_INFINITE, false)) == WSA_WAIT_FAILED) { return -1; } WSAEVENT h = eventArray[dwIndex-WSA_WAIT_EVENT_0]; WSAResetEvent(h); CClient* pClient; //获得与该套接字对应的CClient指针。 pClient = GetClient(h); //判断IO操作完成情况。 if(pClient->m_ol.InternalHigh == 0)//发生错误或客户端关闭了连接。 { //将此节点从客户端链表中删除。 //将此节点对应的事件对象从事件对象数组中删除。 } else { //1,数据已经到达缓冲区。 //2,处理数据。 //3,投递接收数据重叠IO请求,确保任何数据到来前都有一个异步接收IO请求。 pClient->RecvData(); } } return 0; }
如有纰漏,请不吝赐教!!!
2013.1.12于山西大同
相关文章推荐
- Windows socket之重叠IO:事件通知
- Windows socket之重叠IO:事件通知
- Socket IO重叠模型(事件通知)
- 模型设计与实践---(六)重叠IO,事件通知(Overlap Event)
- Windows Socket I/O模型之 重叠I/O事件通知模式
- WinSock IO模型四: 重叠I/O (事件通知)
- winsock IO 模型---重叠IO之事件通知 example code
- 重叠IO之事件通知模型
- 网络编程(54)—— Windows下使用WSASocket基于事件进行IO重叠
- 事件通知方式实现的重叠I/O模型
- 重叠I/O之事件对象通知
- 重叠I/O之事件通知
- 用事件通知方式实现的重叠I/O模型
- 重叠I/O之事件通知
- Overlapped重叠I/O之事件通知
- 基于事件通知的重叠I/O网络模型
- Socket I/O模型之重叠I/O(overlapped I/O)--事件通知
- 重叠I/O之事件对象通知
- select模式的IO的事件通知一定是准确的吗?
- epoll,IO事件通知机制