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) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    AFHTTPRequestOperationManager的那些事
    iOS缩放图片
    iOS截图
    网络服务
    Six
    正文处理命令及tar命令
    用户,群组和权限 四
    用户,群组和权限 三
    用户,群组和权限 二
    用户,群组和权限 一
  • 原文地址:https://www.cnblogs.com/black/p/5171806.html
Copyright © 2011-2022 走看看