zoukankan      html  css  js  c++  java
  • Overlapped I/O模型--事件通知【摘录自《Windows网络编程》】

     
    重叠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 )。


    点击(此处)折叠或打开

    1. #include "stdafx.h"
    2.  #include <winsock2.h>

    3.  #define DATA_BUFSIZE 4096

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

    5.  int _tmain(int argc, _TCHAR* argv[])
    6.  {
    7.      DWORD EventTotal = 0,RecvBytes = 0, Flags = 0;
    8.      char buffer[DATA_BUFSIZE];

    9.      WSABUF DataBuf;
    10.      WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
    11.      WSAOVERLAPPED AcceptOverlapped;
    12.      SOCKET Listen,Accept;

    13.      //step1:
    14.      //start Winsock and set up a listening socket
    15.      WSADATA wsaData;
    16.      WSAStartup(MAKEWORD(2,2), &wsaData);

    17.      ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    18.      u_short port = 27015;
    19.      char* ip;
    20.      sockaddr_in service;
    21.      service.sin_family = AF_INET;
    22.      service.sin_port = htons(port);
    23.      hostent* thisHost;
    24.      thisHost = gethostbyname("");
    25.      ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);

    26.      service.sin_addr.s_addr = inet_addr(ip);

    27.      //-----------------------------------------
    28.      // Bind the listening socket to the local IP address
    29.      // and port number
    30.      bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));

    31.      //-----------------------------------------
    32.      // Set the socket to listen for incoming
    33.      // connection requests
    34.      listen(ListenSocket, 1);
    35.      printf("Listening ");

    36.      //step2:
    37.      Accept=accept(Listen,NULL,NULL);

    38.      //step3:
    39.      //set up an overlapped structure
    40.      EventArray[EventTotal]=WSACreateEvent();//先存到事件数组中
    41.      ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));
    42.      AcceptOverlapped.hEvent = EventArray[EventTotal];

    43.      DataBuf.len = DATA_BUFSIZE;
    44.      DataBuf.buf =buffer;

    45.      EventTotal++;

    46.      //step4:
    47.      //投递WSARecv准备在Accept套接字上接收数据
    48.      WSARecv(Accept,&DataBuf,1,&RecvBytes,&Flag,&AcceptOverlapped,NULL);

    49.      while (TRUE){
    50.          //step5:
    51.          //等待overlapped IO调用的完成
    52.          DWORD Index;
    53.          Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);

    54.          //step6:
    55.          // Reset the signaled event
    56.          WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

    57.          //step7:
    58.          // Determine the status of the overlapped event
    59.          WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE, &Flags);


    60.          // If the connection has been closed, close the accepted socket
    61.          if (BytesTransferred == 0) {
    62.              printf("Closing Socket %d ", AcceptSocket);
    63.              closesocket(AcceptSocket);
    64.              WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
    65.              return;
    66.          }

    67.          // If data has been received,do something with received data
    68.          //DataBuf contains the received data

    69.          //step8:
    70.          //post another WSARecv () request on the socket
    71.          Flag=0;
    72.          ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));
    73.          AcceptOverlapped.hEvent=EventArray[Index - WSA_WAIT_EVENT_0];//该事件对象已经被复位

    74.          DataBuf.len = DATA_BUFSIZE;
    75.          DataBuf.buf =buffer;

    76.          WSARecv(Accept,&DataBuf,1,&RecvBytes,&Flag,&AcceptOverlapped,NULL);
    77.      }
    78.      return 0;
    79.  }

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

    点击(此处)折叠或打开

    1. #define LISTEN_PORT 5000
    2.  #define DATA_BUFSIZE 8192
    3.  #define POST_RECV 0X01
    4.  #define POST_SEND 0X02

    5.  int main( )
    6.  {
    7.      LPPER_HANDLE_DATA lpPerHandleData;
    8.      SOCKET hListenSocket;
    9.      SOCKET hClientSocket;
    10.      SOCKADDR_IN ClientAddr;
    11.      int nAddrLen;
    12.      HANDLE hThread;

    13.      // Start WinSock and create a listen socket.

    14.      listen(hListenSocket, 5);
    15.      for (; ;)
    16.      {
    17.          nAddrLen = sizeof(SOCKADDR);
    18.          hClientSocket = accept(hListenSocket, (LPSOCKADDR)&ClientAddr, &nAddrLen);

    19.          lpPerHandleData = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));
    20.          lpPerHandleData->hSocket = hClientSocket;
    21.          // 注意这里将连接的客户端的IP地址,保存到了lpPerHandleData字段中了
    22.          strcpy(lpPerHandleData->szClientIP, inet_ntoa(ClientAddr.sin_addr));

    23.          // 为本次客户请求产生子线程
    24.          hThread = CreateThread(
    25.              NULL,
    26.              0,
    27.              OverlappedThread,
    28.              lpPerHandleData, // 将lpPerHandleData传给子线程
    29.              0,
    30.              NULL
    31.              );
    32.          CloseHandle(hThread);
    33.      }
    34.      return 0;
    35.  }

    36.  DWORD WINAPI OverlappedThread(LPVOID lpParam)
    37.  {
    38.      LPPER_HANDLE_DATA lpPerHandleData = (LPPER_HANDLE_DATA)lpParam;
    39.      WSAOVERLAPPED Overlapped;
    40.      WSABUF wsaBuf;
    41.      WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];//事件对象数组

    42.      DWORD dwEventTotal = 0, // 程序中事件的总数
    43.      char Buffer[DATA_BUFSIZE];
    44.      BOOL bResult;
    45.      int nResult;

    46.      EventArray[dwEventTotal] = WSACreateEvent();

    47.      ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

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

    49.      ZeroMemory(Buffer, DATA_BUFSIZE);
    50.      wsaBuf.buf = Buffer;
    51.      wsaBuf.len = sizeof(Buffer);
    52.      
    53.      lpPerHandleData->nOperateType = POST_RECV; // 记录本次操作是Recv(..)

    54.      dwEventTotal ++; // 总数加一
    55.      dwFlags = 0;

    56.      nResult = WSARecv(
    57.          lpPerHandleData->hSocket, // Receive socket
    58.          &wsaBuf, // 指向WSABUF结构的指针
    59.          1, // WSABUF数组的个数
    60.          &dwNumOfBytesRecved, // 存放当WSARecv完成后所接收到的字节数,实际接收到的字节数
    61.          &dwFlags, // A pointer to flags
    62.          &Overlapped, // A pointer to a WSAOVERLAPPED structure
    63.          NULL // A pointer to the completion routine,this is NULL
    64.          );

    65.      if ( nResult == SOCKET_ERROR && GetLastError() != WSA_IO_PENDING)
    66.      {
    67.          printf("WSARecv(..) failed. ");
    68.          free(lpPerHandleData);

    69.          closesocket(lpPerHandleData->hSocket;
    70.           WSACloseEvent(EventArray[dwEventTotal]);
    71.          return -1;
    72.      }

    73.      while (TRUE)
    74.      {
    75.          DWORD dwIndex;

    76.          dwIndex = WSAWaitForMultipleEvents(dwEventTotal, EventArray ,
    77.              FALSE ,WSA_INFINITE,FALSE);

    78.         WSAResetEvent(EventArray[dwIndex– WSA_WAIT_EVENT_0]);


    79.          bResult = WSAGetOverlappedResult(
    80.              lpPerHandleData->hSocket,
    81.              &Overlapped,
    82.              &dwBytesTransferred, // 当一个同步I/O完成后,接收到的字节数
    83.              TRUE, // 等待I/O操作的完成
    84.              &dwFlags
    85.              );
    86.          if (bResult == FALSE && WSAGetLastError() != WSA_IO_INCOMPLETE)
    87.          {
    88.              printf("WSAGetOverlappdResult(..) failed. ");
    89.              free(lpPerHandleData);
    90.              return 0; // 错误退出
    91.          }

    92.          if (dwBytesTransferred == 0)
    93.          {
    94.              printf("客户端已退出,将断开与之的连接! ");
    95.              closesocket(lpPerHandleData->hSocket);
    96.              free(lpPerHandleData);
    97.              break;
    98.          }

    99.          // 在这里将接收到的数据进行处理
    100.          printf("Received from IP: %s. data: %s ", lpPerHandleData->szClientIP, wsaBuf.buf);

    101.          // 发送另外一个请求操作
    102.          ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));
    103.          lpPerHandleData->nOperateType = POST_RECV;

    104.          dwFlags = 0;
    105.          nResult = WSARecv();
    106.          if (){}

    107.      }

    108.      return 1;
    109.  }

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

    点击(此处)折叠或打开

    1. #include <winsock2.h>
    2.  #include <stdio.h>

    3.  #define PORT 5150
    4.  #define MSGSIZE 1024

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

    6.  typedef struct
    7.  {
    8.  WSAOVERLAPPED overlap;
    9.  WSABUF Buffer;
    10.  char szMessage[MSGSIZE];
    11.  DWORD NumberOfBytesRecvd;
    12.  DWORD Flags;
    13.  }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

    14.  int g_iTotalConn = 0;
    15.  SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
    16.  WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];
    17.  LPPER_IO_OPERATION_DATA g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS];

    18.  DWORD WINAPI WorkerThread(LPVOID);

    19.  void Cleanup(int);

    20.  int main()
    21.  {
    22.  WSADATA wsaData;
    23.  SOCKET sListen, sClient;
    24.  SOCKADDR_IN local, client;
    25.  DWORD dwThreadId;
    26.  int iaddrSize = sizeof(SOCKADDR_IN);
    27.  // Initialize Windows Socket library
    28.  WSAStartup(0x0202, &wsaData);
    29.  // Create listening socket
    30.  sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    31.  // Bind
    32.  local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    33.  local.sin_family = AF_INET;
    34.  local.sin_port = htons(PORT);
    35.  bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
    36.  // Listen
    37.  listen(sListen, 3);


    38. //网上的代码如此,好奇怪,为什么是在连接还没开始的时候就创建一个线程,而不是像上面的程序一样,accept一个
    39. //connection创建一个线程,并将从connection 的socket中获取的信息当作形参传递给workThread?
    40. //这里是只创建一个线程为重叠socket I/O操作服务
    41.  // Create worker thread
    42.  CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
    43.  while (TRUE)
    44.  {
    45.      // Accept a connection
    46.      sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);

    47.          //这很好,在accept函数中的后两个形参中获取client的相关信息,并直接在server端的控制台中显示出来
    48.      printf("Accepted client:%s:%d ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    49.      g_CliSocketArr[g_iTotalConn] = sClient;//添加到socket数组中
    50.     
    51.      // Allocate a PER_IO_OPERATION_DATA structure
    52.      g_pPerIODataArr[g_iTotalConn] = ER(LPPER_IO_OPATION_DATA)HeapAlloc(
    53.               GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PER_IO_OPERATION_DATA));
    54.      g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;
    55.      g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->eszMssage;
    56.      g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();
    57.      // Launch an asynchronous operation
    58.      WSARecv(
    59.        g_CliSocketArr[g_iTotalConn],
    60.        &g_pPerIODataArr[g_iTotalConn]->Buffer,
    61.        1,
    62.        &g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd,
    63.        &g_pPerIODataArr[g_iTotalConn]->Flags,
    64.        &g_pPerIODataArr[g_iTotalConn]->overlap,
    65.        NULL);
    66.     
    67.      g_iTotalConn++;
    68.  }

    69.  closesocket(sListen);
    70.  WSACleanup();
    71.  return 0;
    72.  }
    73.  DWORD WINAPI WorkerThread(LPVOID lpParam)
    74.  {
    75.  int ret, index;
    76.  DWORD cbTransferred;
    77.  while (TRUE)
    78.  {
    79.      ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
    80.      if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
    81.      {
    82.        continue;
    83.      }
    84.      index = ret - WSA_WAIT_EVENT_0;
    85.      WSAResetEvent(g_CliEventArr[index]);
    86.      WSAGetOverlappedResult(
    87.        g_CliSocketArr[index],
    88.        &g_pPerIODataArr[index]->overlap,
    89.        &cbTransferred,
    90.        TRUE,
    91.        &g_pPerIODataArr[g_iTotalConn]->Flags);
    92.      if (cbTransferred == 0)
    93.      {
    94.        // The connection was closed by client
    95.        Cleanup(index);
    96.      }
    97.      else
    98.      {
    99.        // g_pPerIODataArr[index]->szMessage contains the received data
    100.        g_pPerIODataArr[index]->szMessage[cbTransferred] = '0';
    101.        send(g_CliSocketArr[index], g_pPerIODataArr[index]->szMessage,
    102.          cbTransferred, 0);
    103.        // Launch another asynchronous operation
    104.        WSARecv(
    105.          g_CliSocketArr[index],
    106.          &g_pPerIODataArr[index]->Buffer,
    107.          1,
    108.          &g_pPerIODataArr[index]->NumberOfBytesRecvd,
    109.          &g_pPerIODataArr[index]->Flags,
    110.          &g_pPerIODataArr[index]->overlap,
    111.          NULL);
    112.      }
    113.  }
    114.  return 0;
    115.  }
    116.  void Cleanup(int index)
    117.  {
    118.  closesocket(g_CliSocketArr[index]);
    119.  WSACloseEvent(g_CliEventArr[index]);
    120.  HeapFree(GetProcessHeap(), 0, g_pPerIODataArr[index]);
    121.  if (index < g_iTotalConn - 1)
    122.  {
    123.      g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
    124.      g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
    125.      g_pPerIODataArr[index] = g_pPerIODataArr[g_iTotalConn - 1];
    126.  }
    127.  g_pPerIODataArr[--g_iTotalConn] = NULL;
    128.  }

        这个模型与上述其他模型不同的是它使用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异步操作。

     

    阅读(523) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    web前端的发展态势
    AngularJs 简单入门
    css代码优化篇
    git提交报错:Please make sure you have the correct access rights and the repository exists.
    Activiti工作流框架学习
    遍历map集合的4种方法
    js设置日期、月份增加减少
    Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    webservice_rest接口_学习笔记
    相互匹配两个list集合+动态匹配${}参数
  • 原文地址:https://www.cnblogs.com/black/p/5171806.html
Copyright © 2011-2022 走看看