您的位置:首页 > 理论基础 > 计算机网络

Overlapped I/O模型--事件通知【摘录自《Windows网络编程》】

2016-01-25 16:34 615 查看
原文:http://www.cnblogs.com/NeuqUstcIim/archive/2008/08/16/1269479.html
 
重叠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请求最终完成后,我们的应用程序要负责取回重叠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调用在什么时候完成。

   注意: W S AWa i t F o r M u l t i p l e E v e n t s返回只是说明重叠IO操作完成,但是是成功的完成还是失败的完成还要调用W S A G e t O v e r l a p p e dR e s u l t(取得重叠结构)函数

    如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参数指向的值已进行了更新。

 

   我们向大家阐述了如何编制一个简单的服务器应用,令其在一个套接字上对重叠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 )。

点击(此处)折叠或打开

#include "stdafx.h"

 #include <winsock2.h>

 #define DATA_BUFSIZE 4096

 #pragma comment(lib, "ws2_32.lib")

 int _tmain(int argc, _TCHAR* argv[])

 {

     DWORD EventTotal = 0,RecvBytes = 0, Flags = 0;

     char buffer[DATA_BUFSIZE];

     WSABUF DataBuf;

     WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

     WSAOVERLAPPED AcceptOverlapped;

     SOCKET Listen,Accept;

     //step1:

     //start Winsock and set up a listening socket

     WSADATA wsaData;

     WSAStartup(MAKEWORD(2,2), &wsaData);

     ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     u_short port = 27015;

     char* ip;

     sockaddr_in service;

     service.sin_family = AF_INET;

     service.sin_port = htons(port);

     hostent* thisHost;

     thisHost = gethostbyname("");

     ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);

     service.sin_addr.s_addr = inet_addr(ip);

     //-----------------------------------------

     // Bind the listening socket to the local IP address

     // and port number

     bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));

     //-----------------------------------------

     // Set the socket to listen for incoming

     // connection requests

     listen(ListenSocket, 1);

     printf("Listening\n");

     //step2:

     Accept=accept(Listen,NULL,NULL);

     //step3:

     //set up an overlapped structure

     EventArray[EventTotal]=WSACreateEvent();//先存到事件数组中

     ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));

     AcceptOverlapped.hEvent = EventArray[EventTotal];

     DataBuf.len = DATA_BUFSIZE;

     DataBuf.buf =buffer;

     EventTotal++;

     //step4:

     //投递WSARecv准备在Accept套接字上接收数据

     WSARecv(Accept,&DataBuf,1,&RecvBytes,&Flag,&AcceptOverlapped,NULL);

     while (TRUE){

         //step5:

         //等待overlapped IO调用的完成

         DWORD Index;

         Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);

         //step6:

         // Reset the signaled event

         WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

         //step7:

         // Determine the status of the overlapped event

         WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE, &Flags);

         // If the connection has been closed, close the accepted socket

         if (BytesTransferred == 0) {

             printf("Closing Socket %d\n", AcceptSocket);

             closesocket(AcceptSocket);

             WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

             return;

         }

         // If data has been received,do something with received data

         //DataBuf contains the received data

         //step8:

         //post another WSARecv () request on the socket

         Flag=0;

         ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));

         AcceptOverlapped.hEvent=EventArray[Index - WSA_WAIT_EVENT_0];//该事件对象已经被复位

         DataBuf.len = DATA_BUFSIZE;

         DataBuf.buf =buffer;

         WSARecv(Accept,&DataBuf,1,&RecvBytes,&Flag,&AcceptOverlapped,NULL);

     }

     return 0;

 }

改进后的程序(并非实际可以运行的程序,只是为了理清思路):

点击(此处)折叠或打开

#define LISTEN_PORT 5000

 #define DATA_BUFSIZE 8192

 #define POST_RECV 0X01

 #define POST_SEND 0X02

 int main( )

 {

     LPPER_HANDLE_DATA lpPerHandleData;

     SOCKET hListenSocket;

     SOCKET hClientSocket;

     SOCKADDR_IN ClientAddr;

     int nAddrLen;

     HANDLE hThread;

     // Start WinSock and create a listen socket.

     listen(hListenSocket, 5);

     for (; ;)

     {

         nAddrLen = sizeof(SOCKADDR);

         hClientSocket = accept(hListenSocket, (LPSOCKADDR)&ClientAddr, &nAddrLen);

         lpPerHandleData = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));

         lpPerHandleData->hSocket = hClientSocket;

         // 注意这里将连接的客户端的IP地址,保存到了lpPerHandleData字段中了

         strcpy(lpPerHandleData->szClientIP, inet_ntoa(ClientAddr.sin_addr));

         // 为本次客户请求产生子线程

         hThread = CreateThread(

             NULL,

             0,

             OverlappedThread,

             lpPerHandleData, // 将lpPerHandleData传给子线程

             0,

             NULL

             );

         CloseHandle(hThread);

     }

     return 0;

 }

 DWORD WINAPI OverlappedThread(LPVOID lpParam)

 {

     LPPER_HANDLE_DATA lpPerHandleData = (LPPER_HANDLE_DATA)lpParam;

     WSAOVERLAPPED Overlapped;

     WSABUF wsaBuf;

     WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];//事件对象数组

     DWORD dwEventTotal = 0, // 程序中事件的总数

     char Buffer[DATA_BUFSIZE];

     BOOL bResult;

     int nResult;

     EventArray[dwEventTotal] = WSACreateEvent();

     ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

     Overlapped.hEvent = EventArray[dwEventTotal]; // 关联事件

     ZeroMemory(Buffer, DATA_BUFSIZE);

     wsaBuf.buf = Buffer;

     wsaBuf.len = sizeof(Buffer);

     

     lpPerHandleData->nOperateType = POST_RECV; // 记录本次操作是Recv(..)

     dwEventTotal ++; // 总数加一

     dwFlags = 0;

     nResult = WSARecv(

         lpPerHandleData->hSocket, // Receive socket

         &wsaBuf, // 指向WSABUF结构的指针

         1, // WSABUF数组的个数

         &dwNumOfBytesRecved, // 存放当WSARecv完成后所接收到的字节数,实际接收到的字节数

         &dwFlags, // A pointer to flags

         &Overlapped, // A pointer to a WSAOVERLAPPED structure

         NULL // A pointer to the completion routine,this is NULL

         );

     if ( nResult == SOCKET_ERROR && GetLastError() != WSA_IO_PENDING)

     {

         printf("WSARecv(..) failed.\n");

         free(lpPerHandleData);

         closesocket(lpPerHandleData->hSocket;

          WSACloseEvent(EventArray[dwEventTotal]);

         return -1;

     }

     while (TRUE)

     {

         DWORD dwIndex;

         dwIndex = WSAWaitForMultipleEvents(dwEventTotal, EventArray ,

             FALSE ,WSA_INFINITE,FALSE);

        WSAResetEvent(EventArray[dwIndex– WSA_WAIT_EVENT_0]);

         bResult = WSAGetOverlappedResult(

             lpPerHandleData->hSocket,

             &Overlapped,

             &dwBytesTransferred, // 当一个同步I/O完成后,接收到的字节数

             TRUE, // 等待I/O操作的完成

             &dwFlags

             );

         if (bResult == FALSE && WSAGetLastError() != WSA_IO_INCOMPLETE)

         {

             printf("WSAGetOverlappdResult(..) failed.\n");

             free(lpPerHandleData);

             return 0; // 错误退出

         }

         if (dwBytesTransferred == 0)

         {

             printf("客户端已退出,将断开与之的连接!\n");

             closesocket(lpPerHandleData->hSocket);

             free(lpPerHandleData);

             break;

         }

         // 在这里将接收到的数据进行处理

         printf("Received from IP: %s.\ndata: %s\n", lpPerHandleData->szClientIP, wsaBuf.buf);

         // 发送另外一个请求操作

         ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

         lpPerHandleData->nOperateType = POST_RECV;

         dwFlags = 0;

         nResult = WSARecv();

         if (){}

     }

     return 1;

 }

最后的一个改进版本,看上去是个不错的版本,相对来说算是比较实用的。但是使用了过多的全局变量,代码是C风格的,不可取。

点击(此处)折叠或打开

#include <winsock2.h>

 #include <stdio.h>

 #define PORT 5150

 #define MSGSIZE 1024

 #pragma comment(lib, "ws2_32.lib")

 typedef struct

 {

 WSAOVERLAPPED overlap;

 WSABUF Buffer;

 char szMessage[MSGSIZE];

 DWORD NumberOfBytesRecvd;

 DWORD Flags;

 }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

 int g_iTotalConn = 0;

 SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];

 WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];

 LPPER_IO_OPERATION_DATA g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS];

 DWORD WINAPI WorkerThread(LPVOID);

 void Cleanup(int);

 int main()

 {

 WSADATA wsaData;

 SOCKET sListen, sClient;

 SOCKADDR_IN local, client;

 DWORD dwThreadId;

 int iaddrSize = sizeof(SOCKADDR_IN);

 // Initialize Windows Socket library

 WSAStartup(0x0202, &wsaData);

 // Create listening socket

 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

 // Bind

 local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

 local.sin_family = AF_INET;

 local.sin_port = htons(PORT);

 bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

 // Listen

 listen(sListen, 3);

//网上的代码如此,好奇怪,为什么是在连接还没开始的时候就创建一个线程,而不是像上面的程序一样,accept一个

//connection创建一个线程,并将从connection 的socket中获取的信息当作形参传递给workThread?

//这里是只创建一个线程为重叠socket I/O操作服务

 // Create worker thread

 CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);

 while (TRUE)

 {

     // Accept a connection

     sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);

         //这很好,在accept函数中的后两个形参中获取client的相关信息,并直接在server端的控制台中显示出来

     printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

     g_CliSocketArr[g_iTotalConn] = sClient;//添加到socket数组中

    

     // Allocate a PER_IO_OPERATION_DATA structure

     g_pPerIODataArr[g_iTotalConn] = ER(LPPER_IO_OPATION_DATA)HeapAlloc(

              GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PER_IO_OPERATION_DATA));

     g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;

     g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->eszMssage;

     g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();

     // Launch an asynchronous operation

     WSARecv(

       g_CliSocketArr[g_iTotalConn],

       &g_pPerIODataArr[g_iTotalConn]->Buffer,

       1,

       &g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd,

       &g_pPerIODataArr[g_iTotalConn]->Flags,

       &g_pPerIODataArr[g_iTotalConn]->overlap,

       NULL);

    

     g_iTotalConn++;

 }

 closesocket(sListen);

 WSACleanup();

 return 0;

 }

 DWORD WINAPI WorkerThread(LPVOID lpParam)

 {

 int ret, index;

 DWORD cbTransferred;

 while (TRUE)

 {

     ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);

     if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)

     {

       continue;

     }

     index = ret - WSA_WAIT_EVENT_0;

     WSAResetEvent(g_CliEventArr[index]);

     WSAGetOverlappedResult(

       g_CliSocketArr[index],

       &g_pPerIODataArr[index]->overlap,

       &cbTransferred,

       TRUE,

       &g_pPerIODataArr[g_iTotalConn]->Flags);

     if (cbTransferred == 0)

     {

       // The connection was closed by client

       Cleanup(index);

     }

     else

     {

       // g_pPerIODataArr[index]->szMessage contains the received data

       g_pPerIODataArr[index]->szMessage[cbTransferred] = '\0';

       send(g_CliSocketArr[index], g_pPerIODataArr[index]->szMessage,\

         cbTransferred, 0);

       // Launch another asynchronous operation

       WSARecv(

         g_CliSocketArr[index],

         &g_pPerIODataArr[index]->Buffer,

         1,

         &g_pPerIODataArr[index]->NumberOfBytesRecvd,

         &g_pPerIODataArr[index]->Flags,

         &g_pPerIODataArr[index]->overlap,

         NULL);

     }

 }

 return 0;

 }

 void Cleanup(int index)

 {

 closesocket(g_CliSocketArr[index]);

 WSACloseEvent(g_CliEventArr[index]);

 HeapFree(GetProcessHeap(), 0, g_pPerIODataArr[index]);

 if (index < g_iTotalConn - 1)

 {

     g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];

     g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];

     g_pPerIODataArr[index] = g_pPerIODataArr[g_iTotalConn - 1];

 }

 g_pPerIODataArr[--g_iTotalConn] = NULL;

 }

    这个模型与上述其他模型不同的是它使用Winsock2提供的异步I/O函数WSARecv。在调用WSARecv时,指定一个WSAOVERLAPPED结构,这个调用不是阻塞的,也就是说,它会立刻返回。一旦有数据到达的时候,被指定的WSAOVERLAPPED结构中的hEvent被Signaled。由于下面这个语句g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent;

    使得与该套接字相关联的WSAEVENT对象也被Signaled,所以WSAWaitForMultipleEvents的调用操作成功返回。我们现在应该做的就是与调用WSARecv相同的WSAOVERLAPPED结构为参数调用WSAGetOverlappedResult,从而得到本次I/O传送的字节数等相关信息。在取得接收的数据后,把数据原封不动的发送到客户端,然后重新激活一个WSARecv异步操作。

 


<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>

阅读(523) | 评论(0) | 转发(0) |

0
上一篇:Linux C++中需要的头文件

下一篇:【转】Linux AIO机制

相关热门文章
Serv-u的ODBC数据库做法(完整...

10种启动故障解决方法(转)...

Auto CAD 2007中文版 迅雷免费...

关于自动弹出网页的解决方法...

出现“high definition audio...

linux dhcp peizhi roc

关于Unix文件的软链接

求教这个命令什么意思,我是新...

sed -e "/grep/d" 是什么意思...

谁能够帮我解决LINUX 2.6 10...

给主人留下些什么吧!~~

评论热议
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: