overlapped

8.2.4 重叠模型
在Wi n s o c k中,相比我们迄今为止解释过的其他所有I / O模型,重叠I / O(Overlapped I/O)
模型使应用程序能达到更佳的系统性能。重叠模型的基本设计原理便是让应用程序使用一个重叠的数据结构,一次投递一个或多个Winsock I/O请求。针对那些提交的请求,在它们完成之后,应用程序可为它们提供服务。该模型适用于除Windows CE之外的各种Wi n d o w s平台。
模型的总体设计以Wi n 3 2重叠I / O机制为基础。那个机制可通过R e a d F i l e和Wr i t e F i l e两个函数,针对设备执行I / O操作。
最开始的时候,Wi n s o c k重叠I / O模型只能应用于Windows NT操作系统上运行的Wi n s o c k1 . 1应用程序。作为应用程序,它可针对一个套接字句柄,调用R e a d F i l e以及Wr i t e F i l e,同时指定一个重叠式结构(稍后详述),从而利用这个模型。自Winsock 2发布开始,重叠I / O便已集成到新的Wi n s o c k函数中,比如W S A S e n d和W S A R e c v。这样一来,重叠I / O模型便能适用于安装了Winsock 2的所有Wi n d o w s平台。

注意在Winsock 2发布之后,重叠I/O仍可在Windows NT和Windows 2000这两个操作系统中,随R e a d F i l e和Wr i t e F i l e两个函数使用。但是,Windows 95和Windows 98均不具备这一功能。考虑到应用程序的跨平台兼容问题,同时考虑到性能方面的因素,应尽量避免使用Wi n 3 2的R e a d F i l e和Wr i t e F i l e函数,分别换以W S A R e c v和W S A S e n d函数。本节只打算讲述通过新的Winsock 2函数,来使用重叠I/O模型。
要想在一个套接字上使用重叠I / O模型,首先必须使用W S A _ F L A G _ O V E R L A P P E D这个标志,创建一个套接字。如下所示:

  s = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
  创建套接字的时候,假如使用的是s o c k e t函数,而非W S A S o c k e t函数,那么会默认设置W S A _ F L A G _ O V E R L A P P E D标志。成功建好一个套接字,同时将其与一个本地接口绑定到一起后,便可开始进行重叠I / O 操作,方法是调用下述的Wi n s o c k 函数,同时指定一个
W S A O V E R L A P P E D结构(可选):
■ W S A S e n d
■ W S A S e n d To
■ W S A R e c v
■ W S A R e c v F r o m
■ W S A I o c t l
■ A c c e p t E x
■ Tr n a s m i t F i l e
大家现在或许已经知道,其中每个函数都与一个套接字上数据的发送、数据接收以及连接的接受有关。因此,这些活动可能会花极少的时间才能完成。这正是每个函数都可接受一个W S A O V E R L A P P E D结构作为参数的原因。若随一个W S A O V E R L A P P E D结构一起调用这些函数,函数会立即完成并返回,无论套接字是否设为锁定模式(本章开头已详细讲过了)。它
们依赖于W S A O V E R L A P P E D结构来返回一个I / O请求的返回。
主要有两个方法可用来管理一个重叠I / O请求的完成:我们的应用程序可等待“事件对象通知”,亦可通过“完成例程”,对已经完成的请求加以处理。上面列出的函数( A c c e p t E x除外)还有另一个常用的参数:
l p C o m p l e t i o n R O U T I N E。该参数指定的是一个可选的指针,指向一个完成例程函数,在重叠请求完成后调用。接下去,我们将探讨事件通知方法。在本章稍后,还会介绍如何使用可选的完成例程,代替事件,对完成的重叠请求加以处理。
1. 事件通知
重叠I / O的事件通知方法要求将Wi n 3 2事件对象与W S A O V E R L A P P E D结构关联在一起。若使用一个W S A O V E R L A P P E D结构,发出像W S A S e n d和W S A R e c v这样的I / O调用,它们会立即返回。
通常,大家会发现这些I / O 调用会以失败告终,返回S O C K E T _ E R R O R 。使用W S A G e t L a s t E r r o r函数,便可获得与错误状态有关的一个报告。这个错误状态意味着I / O操作正在进行。稍后的某个时间,我们的应用程序需要等候与W S A O V E R L A P P E D结构对应的事件对象,了解一个重叠I / O请示何时完成。W S A O V E R L A P P E D结构在一个重叠I / O请求的初始化,及其后续的完成之间,提供了一种沟通或通信机制。下面是这个结构的定义:

 typedef struct WSAOVERLAPPED
 {
  DWORD Internal;
  DWORD InternalHigh;
  DWORD Offset;
  DWORD OffsetHigh;
  WSAEVENT hEvent;
 }WSAOVERLAPPED,FAR *LPWSAOVERLPPED;
 
其中,I n t e r n a l、I n t e r n a l H i g h、O ff s e t和O ff s e t H i g h字段均由系统在内部使用,不应由应用程序直接进行处理或使用。而另一方面, h E v e n t字段有点儿特殊,它允许应用程序将一个事件对象句柄同一个套接字关联起来。大家可能会觉得奇怪,如何将一个事件对象句柄分配给该字段呢?正如我们早先在W S A E v e n t S e l e c t模型中讲述的那样,可用W S A C r e a t e E v e n t函数来创建一个事件对象句柄。一旦创建好一个事件句柄,简单地将重叠结构的h E v e n t字段分配给事件句柄,再使用重叠结构,调用一个Wi n s o c k函数即可,比如W S A S e n d或W S A R e c v。
一个重叠I / O请求最终完成后,我们的应用程序要负责取回重叠I / O操作的结果。一个重叠请求操作最终完成之后,在事件通知方法中, Wi n s o c k会更改与一个W S A O V E R L A P P E D结构对应的一个事件对象的事件传信状态,将其从“未传信”变成“已传信”。由于一个事件对象已分配给W S A O V E R L A P P E D结构,所以只需简单地调用W S AWa i t F o r M u l t i p l e E v e n t s函数,从而判断出一个重叠I / O调用在什么时候完成。该函数已在我们前面讲述WSAEventSelect I/O模型时介绍过了。W S AWa i t F o r M u l t i p l e E v e n t s函数会等候一段规定的时间,等待一个或多个事件对象进入“已传信”状态。在此再次提醒大家注意: W S AWa i t F o r M u l t i p l e E v e n t s函数一次
最多只能等待6 4个事件对象。发现一次重叠请求完成之后,接着需要调用W S A G e t O v e r l a p p e dR e s u l t(取得重叠结构)函数,判断那个重叠调用到底是成功,还是失败。该函数的定义如下:
 BOOL WSAGetOverlappedResult(
                SOCKET s,
                LPWSAOVERLAPPED lpOverlapped,
                LPWORD lpcbTransfer,
                BOOL  fWait,
                LPWORD lpdwFlags
               );
其中, s参数用于指定在重叠操作开始的时候,与之对应的那个套接字。l p O v e r l a p p e d参数是一个指针,对应于在重叠操作开始时,指定的那个W S A O V E R L A P P E D 结构。
l p c b Tr a n s f e r参数也是一个指针,对应一个D W O R D(双字)变量,负责接收一次重叠发送或接收操作实际传输的字节数。f Wa i t参数用于决定函数是否应该等待一次待决(未决)的重叠操作完成。若将f Wa i t设为T R U E,那么除非操作完成,否则函数不会返回;若设为FA L S E,而且操作仍然处于“待决”状态,那么W S A G e t O v e r l a p p e d R e s u l t函数会返回FA L S E值,同时返回一个W S A _ I O _ I N C O M P L E T E(I / O操作未完成)错误。但就我们目前的情况来说,由于需要等候重叠操作的一个已传信事件完成,所以该参数无论采用什么设置,都没有任何效果。
最后一个参数是l p d w F l a g s,它对应于一个指针,指向一个D W O R D(双字),负责接收结果标志(假如原先的重叠调用是用W S A R e c v或W S A R e c v F r o m函数发出的)。
如W S A G e t O v e r l a p p e d R e s u l t函数调用成功,返回值就是T R U E。这意味着我们的重叠I / O操作已成功完成,而且由l p c b Tr a n s f e r参数指向的值已进行了更新。若返回值是FA L S E,那么可能是由下述任何一种原因造成的:
■ 重叠I / O操作仍处在“待决”状态。
■ 重叠操作已经完成,但含有错误。
■ 重叠操作的完成状态不可判决,因为在提供给W S A G e t O v e r l a p p e d R e s u l t函数的一个或
多个参数中,存在着错误。

失败后,由l p c b Tr a n s f e r参数指向的值不会进行更新,而且我们的应用程序应调用W S A G e t L a s t E r r o r函数,调查到底是何种原因造成了调用失败。
在程序清单8 - 7中,我们向大家阐述了如何编制一个简单的服务器应用,令其在一个套接字上对重叠I / O操作进行管理,程序完全利用了前述的事件通知机制。对该程序采用的编程步骤总结如下:
1) 创建一个套接字,开始在指定的端口上监听连接请求。
2) 接受一个进入的连接请求。
3) 为接受的套接字新建一个W S A O V E R L A P P E D结构,并为该结构分配一个事件对象句柄。
也将事件对象句柄分配给一个事件数组,以便稍后由W S AWa i t F o r M u l t i p l e E v e n t s函数使用。
4) 在套接字上投递一个异步W S A R e c v请求,指定参数为W S A O V E R L A P P E D结构。
注意函数通常会以失败告终,返回S O C K E T _ E R R O R错误状态W S A _ I O _ P E N D I N G(I/O操作尚未完成)。
5) 使用步骤3 )的事件数组,调用W S AWa i t F o r M u l t i p l e E v e n t s函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件的“触发”)。
6) WSAWa i t F o r M u l t i p l e E v e n t s函数完成后,针对事件数组,调用W S A R e s e t E v e n t(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。
7) 使用W S A G e t O v e r l a p p e d R e s u l t函数,判断重叠调用的返回状态是什么。
8) 在套接字上投递另一个重叠W S A R e c v请求。
9) 重复步骤5 ) ~ 8 )。
这个例子极易扩展,提供对多个套接字的支持。方法是将代码的重叠I / O处理部分移至一个独立的线程内,让主应用程序线程为附加的连接请求提供服务。

转载于:https://www.cnblogs.com/zdzlovemonkey/p/3155484.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重叠IO模型之OverLapped完成例程模型WSACompletionRoutineServer VS2010 基础入门 客户端与服务器端 客户端向服务器端发送数据 可接收多个客户端 #include #include #pragma comment (lib, "ws2_32.lib") #define PORT 8088 #define MSG_SIZE 1024 SOCKET g_sConnect; bool g_bConnect = false; typedef struct { WSAOVERLAPPED overLap; WSABUF wsaBuf; char chMsg[MSG_SIZE]; DWORD nRecvNum; DWORD nFlags; SOCKET sClient; }PRE_IO_OPERATION_DATA, *LP_PER_IO_OPERATION_DATA; void CALLBACK CompletionRoutine(DWORD dwError, DWORD dwTrans, LPWSAOVERLAPPED lpOverlap, DWORD nFlags); DWORD WINAPI workThread(LPVOID lp) { LP_PER_IO_OPERATION_DATA lpData; while(TRUE) { if (g_bConnect) // 有新的连接 { // 为lpData分配空间并初始化 lpData = (LP_PER_IO_OPERATION_DATA)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PRE_IO_OPERATION_DATA)); lpData->wsaBuf.len = MSG_SIZE; lpData->wsaBuf.buf = lpData->chMsg; lpData->sClient = g_sConnect; WSARecv(lpData->sClient, &lpData->wsaBuf, 1, &lpData->nRecvNum, &lpData->nFlags, &lpData->overLap, CompletionRoutine); g_bConnect = false; // 处理完毕 } SleepEx(1000, TRUE); } return 0; } // 系统在WSARecv收到信息后,自动调用此函数,并传入参数--回调函数 void CALLBACK CompletionRoutine(DWORD dwError, DWORD dwTrans, LPWSAOVERLAPPED lpOverlap, DWORD nFlags) { LP_PER_IO_OPERATION_DATA lpData = (LP_PER_IO_OPERATION_DATA)lpOverlap; if (0 != dwError) // 接收失败 { printf("Socket %d Close!\n", lpData->sClient); closesocket(lpData->sClient); HeapFree(GetProcessHeap(), 0, lpData); } else // 接收成功 { lpData->chMsg[dwTrans] = '\0'; send(lpData->sClient, lpData->chMsg, dwTrans, 0); printf("Socket:%d MSG: %s \n", lpData->sClient, lpData->chMsg); memset(&lpData->overLap, 0, sizeof(WSAOVERLAPPED)); lpData->wsaBuf.len = MSG_SIZE; lpData->wsaBuf.buf = lpData->chMsg; // 继续接收来自客户端的数据 实现 WSARecv与CompletionRoutine循环 WSARecv(lpData->sClient, &lpData->wsaBuf,1, &lpData->nRecvNum, &lpData->nFlags, &lpData->overLap, CompletionRoutine); } } int main() { WSADATA wsaData; WSAStartup(0x0202, &wsaData); SOCKET sListen; sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addrListen; addrListen.sin_family = AF_INET; addrListen.sin_port = htons(PORT); addrListen.sin_addr.S_un.S_addr = htonl(ADDR_ANY); int nErrorCode = 0; nErrorCode = bind(sListen, (sockaddr*)&addrListen, sizeof(sockaddr)); nErrorCode = listen(sListen, 5); DWORD nThreadID; CreateThread(NULL, 0, workThread, NULL, 0, &nThreadID); sockaddr_in addrConnect; int nAddrLen = sizeof(sockaddr_in); printf("Server Started!\n"); while(TRUE) { g_sConnect= accept(sListen, (sockaddr*)&addrConnect, &nAddrLen); if (INVALID_SOCKET == g_sConnect) { return -1; } g_bConnect = true; // 连接成功 printf("Accept Client :%s -- PORT:%d\n", inet_ntoa(addrConnect.sin_addr), htons(addrConnect.sin_port)); } return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值