zoukankan      html  css  js  c++  java
  • Windows网络编程笔记6 --- WinSock I/O 控制方法

      Windows提供了两种方式“套接字模式”和“套接字I/O模型”,可对一个套接字上的I/O行为加以控制。套接字模式用于决定在随一个套接字调用时,那些 Winsock函数的行为。其中的模型包括括select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、OverlappedI/O(重叠式I/O)以及Completionport(完成端口)等等。

      所有Windows平台都支持套接字以锁定非锁定方式工作。在锁定模式下,在I/O操作完成前,执行操作的Winsock函数(比如send和recv)会一直等候下去,不会立即返回程序(将控制权交还给程序)。而在非锁定模式下,Winsock函数无论如何都会立即返回。

      锁定模式使用线程同步机制完成数据的I/O,扩展性不好,尤其是有多个套接字时。

      非锁定模式的使用需要函数 ioctlsocket(),使用如下:

    SOCKET s;
    unsigned long ub =1;
    int nRet;
    s = socketAF_INET,SOCK_STREAM,0);
    nRet = ioctlsocket(s,FIOBIO,(unsigned long *)&ub);//设置非锁定模式
    if(nRet == SOCKET_ERROR)
    {
        //进入非锁定模式失败
    }

    套接字I/O模型

    1、select模型

    //函数原型
    int select(
        int nfds,    //与早期程序兼容,可忽略
        fd_set FAR * readfds,// 可读性
        fd_set FAR * writefds,// 可写性
        fd_set FAR * exceptfds,// 例外数据
        const struct timeval FAR * timeout//超时时间
        );

    参数readfds 表示以下几种情况

            有数据可读入、连接已经关闭(重设或者终止)、如果已经listen,并且正在建立连接,那么accept函数会返回成功。

    参数writefds表示以下几种情况

            有数据可发出、如果已完成对一个非锁定连接调用的处理,连接就会成功。

    参数exceptfds表示如下

            如果已完成对一个非锁定连接调用的处理,连接尝试就会失败。有带外(Out-of-band,OOB)数据可供读取。

    其中fd_set结构如下:

    typedef struct fd_set {
            u_int fd_count;               /* how many are SET? */
            SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
    } fd_set;

    超时时间

    struct timeval {
            long    tv_sec;         /* seconds */long    tv_usec;        /* and microseconds */毫秒
    };

      用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部读、写以及例外fd_set结构。将一个套接字分配给任何一个集合后,再来调用select,便可知道一个套接字上是否正在发生上述的I/O活动。

    下面是对fd_set进行操作的一些宏

      FD_CLR(s,*set):从set中删除套接字s。

      FD_ISSET(s,*set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。

      FD_SET(s,*set):将套接字s加入集合set。

      FD_ZERO(*set):将set初始化成空集合。

    用select操作一个或多个套接字句柄的全过程:

      1)使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。

      2)使用FD_SET宏,将套接字句柄分配给自己感兴趣的每个fd_set。

      3)调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,并对每个集合进行相应的更新。

      4)根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。

      5)知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1),继续进行select处理。

    简单过程如下

       SOCKET s;
        fd_set fdread;
        int ret;
        //创建
        //bind()
        //accept()
        //开始
        while (1)
        {
            FD_ZERO(&fdread);//初始化
            FD_SET(s,&fdread);//添加
            if ((ret = select(0,&fdread,NULL,NULL,NULL)) == SOCKET_ERROR)
            {
                //添加失败
            }
            if (ret > 0)
            {
                if (FD_ISSET(s,&fdread))
                {
                    //已经是集合的一部分,正在读取数据
                }        
            }
            //其他操作
        }

    2、WSAAsyncSelect模型

    WSAAsyncSelect 的使用必须要在窗口应用程序中使用,在回调函数中实现处理。

    int WSAAsyncSelect(
      _In_  SOCKET s,//关心的socket
      _In_  HWND hWnd,//窗口句柄
      _In_  unsigned int wMsg,//消息
      _In_  long lEvent//事件类型
    );

    事件类型很多,如图

     WSAAsyncSelect模式实现,这个实现起来挺简单的

      1 // WSAAsyncSelect模式实现,这个实现起来挺简单的。
      2 #include <winsock2.h>  
      3 #include <tchar.h>  
      4 #define PORT      7890  
      5 #define MSGSIZE   1024  
      6 #define WM_SOCKET WM_USER+1  
      7 #pragma comment(lib, "ws2_32.lib")  
      8 
      9 
     10 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
     11 int WINAPI WinMain(    HINSTANCE hInstance, 
     12                      HINSTANCE hPrevInstance, 
     13                      LPSTR lpCmdLine, 
     14                      int nShowCmd 
     15     )
     16 {
     17 
     18 
     19     static TCHAR szAppName[] = TEXT ("WSAAsyncSelect Test") ;
     20     HWND         hwnd ;
     21     MSG          msg ;
     22     WNDCLASS     wndclass ;
     23 
     24     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     25     wndclass.lpfnWndProc   = WndProc;
     26     wndclass.cbClsExtra    = 0 ;
     27     wndclass.cbWndExtra    = 0 ;
     28     wndclass.hInstance     = hInstance ;
     29     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     30     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     31     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     32     wndclass.lpszMenuName  = NULL ;
     33     wndclass.lpszClassName = szAppName ;
     34 
     35     if (!RegisterClass (&wndclass))
     36     {
     37         MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
     38             szAppName, MB_ICONERROR) ;
     39         return 0 ;
     40     }
     41 
     42     hwnd = CreateWindow (szAppName, TEXT ("WSAAsyncSelect"),
     43         WS_OVERLAPPEDWINDOW,
     44         CW_USEDEFAULT, CW_USEDEFAULT,
     45         CW_USEDEFAULT, CW_USEDEFAULT,
     46         NULL, NULL, hInstance, NULL) ;
     47 
     48     ShowWindow (hwnd, nShowCmd) ;
     49     UpdateWindow (hwnd) ;
     50 
     51     while (GetMessage (&msg, NULL, 0, 0))
     52     {
     53         TranslateMessage (&msg) ;
     54         DispatchMessage (&msg) ;
     55     }
     56     return msg.wParam ;
     57 }
     58 
     59 
     60 
     61 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
     62 {  
     63     WSADATA       wsd;  
     64     static SOCKET sListen;  
     65     SOCKET        sClient;  
     66     SOCKADDR_IN   local, client;  
     67     int           ret, iAddrSize = sizeof(client);  
     68     char          szMessage[MSGSIZE] ;  
     69     switch (message)  
     70     {  
     71     case WM_CREATE:  
     72         // 初始化
     73         WSAStartup(0x0202, &wsd);  
     74 
     75         // 创建socket 
     76         sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
     77 
     78         // 绑定  
     79         local.sin_addr.S_un.S_addr = inet_addr("192.168.0.87");
     80         local.sin_family = AF_INET;  
     81         local.sin_port = htons(PORT);  
     82         bind(sListen, (struct sockaddr *)&local, sizeof(local));  
     83 
     84         // 监听 
     85         listen(sListen, 3);  
     86         // 设置WSAAsyncSelect模式  
     87         WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);  
     88         return 0;  
     89     case WM_DESTROY:  
     90         closesocket(sListen);  
     91         WSACleanup();  
     92         PostQuitMessage(0);  
     93         return 0;  
     94 
     95     case WM_SOCKET:  
     96         if (WSAGETSELECTERROR(lParam))//使用宏 WSAGETSELECTERROR 判断lParam高字节是否有错误
     97         {  
     98             closesocket(wParam);  
     99             break;  
    100         }  
    101 
    102         switch (WSAGETSELECTEVENT(lParam))//使用宏WSAGETSELECTEVENT判断lParam低字节是什么操作
    103         {  
    104         case FD_ACCEPT:  
    105             // 从客户端接收连接  
    106             sClient = accept(wParam, (struct sockaddr *)&client, &iAddrSize);  
    107 
    108             // 设置事件监听
    109             WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);  
    110             break;  
    111         case FD_READ:  //
    112             ret = recv(wParam, szMessage, MSGSIZE, 0);  
    113             if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)  
    114             {  
    115                 closesocket(wParam);  
    116             }  
    117             else  
    118             {  
    119                 szMessage[ret] = '';  
    120                 MessageBox(hwnd,"接收成功",NULL,MB_OK);
    121                 send(wParam, szMessage, strlen(szMessage), 0);  
    122             }  
    123             break;  
    124         case FD_WRITE://
    125             send(wParam, szMessage, strlen(szMessage), 0);  
    126             break;
    127         case FD_CLOSE:  //关闭
    128             closesocket(wParam);        
    129             break;  
    130         }  
    131         return 0;  
    132     }  
    133      
    134     return DefWindowProc(hwnd, message, wParam, lParam);  
    135 }  
    View Code

    3、WSAEventSelect 模型

      这个模型和WSAAsyncSelect的区别是,它能将网络事件投递给一个事件对象句柄,而不是投递到一个窗口程序。

     因此使用的时候首先就要生成一个事件对象,函数如下

    WSAEVENT WSACreateEvent(void);//创建一个事件对象句柄

    //创建WSAEventSelect模型
    int WSAEventSelect(
      _In_  SOCKET s,//关心的套接字
      _In_  WSAEVENT hEventObject,//事件对象
      _In_  long lNetworkEvents//感兴趣的网络事件类型,FD_READ,FD_WRITE一类的
    );

      为WSAEventSelect创建的事件拥有两种工作状态,以及两种工作模式。其中,两种工作状态分别是“已传信”(signaled)和“未传信”(nonsignaled)。工作模式则包括“人工重设”(manual reset)和“自动重设”(auto reset)。WSACreateEvent最开始在一种未传信的工作状态中,并用一种人工重设模式,来创建事件句柄。随着网络事件触发了与一个套接字关联在一起的事件对象,工作状态便会从“未传信”转变成“已传信”。由于事件对象是在一种人工重设模式中创建的,所以在完成了一个I/O请求的处理之后,我们的应用程序需要负责将工作状态从已传信更改为未传信。

      几个函数

    //重置使用WSAResetEvent函数。
    BOOL WSAResetEvent(
      _In_  WSAEVENT hEvent
    );
    //如果不再使用,就是放资源
    BOOL WSACloseEvent(
      _In_  WSAEVENT hEvent
    );
    
    //创建过之后要监听事件,这时使用WSAWaitForMultipleEvents函数等待
    DWORD WSAWaitForMultipleEvents(
      DWORD cEvents,//事件对象数量
      const WSAEVENT *lphEvents,//所有的套接字事件,最多64个
      BOOL fWaitAll,//是否等待全部完成才返回,还是大于一个时就返回
     DWORD dwTimeout,//超时返回
      BOOL fAlertable//在这个模型中忽略,为false,在重叠I/O时使用
    );

    知道网络事件引起的套接字后,可以查询发生了什么类型的网络事件

    //知道网络事件引起的套接字后,可以查询发生了什么类型的网络事件
    int WSAEnumNetworkEvents(
      _In_   SOCKET s,
      _In_   WSAEVENT hEventObject,//需要重设的事件对象,
      _Out_  LPWSANETWORKEVENTS lpNetworkEvents//网络事件
    );
    //参数三结构
    typedef struct _WSANETWORKEVENTS {
           long lNetworkEvents;//网络事件类型
           int iErrorCode[FD_MAX_EVENTS];//错误代码
    } WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;

    WSAWaitForMultipleEvents 使用举例

      1 //WSAWaitForMultipleEvents 使用举例
      2 #include <winsock2.h>
      3 #include <Ws2tcpip.h>
      4 #include <stdio.h>
      5 
      6 #pragma comment(lib, "Ws2_32.lib")
      7 #define DATA_BUFSIZE 4096
      8 
      9 int main()
     10 {
     11     //-----------------------------------------
     12     WSADATA wsaData = { 0 };
     13     int iResult = 0;
     14     BOOL bResult = TRUE;
     15 
     16     WSABUF DataBuf;
     17     char buffer[DATA_BUFSIZE];
     18 
     19     DWORD EventTotal = 0;
     20     DWORD RecvBytes = 0;
     21     DWORD Flags = 0;
     22     DWORD BytesTransferred = 0;
     23 
     24     WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
     25     WSAOVERLAPPED AcceptOverlapped;
     26     SOCKET ListenSocket = INVALID_SOCKET;
     27     SOCKET AcceptSocket = INVALID_SOCKET;
     28 
     29     DWORD Index;
     30 
     31     //-----------------------------------------
     32     // 初始化
     33     iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
     34     if (iResult != 0) {
     35         wprintf(L"WSAStartup failed: %d
    ", iResult);
     36         return 1;
     37     }
     38     //-----------------------------------------
     39     //创建监听套接字
     40     ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     41     if (ListenSocket == INVALID_SOCKET) {
     42         wprintf(L"socket failed with error = %d
    ", WSAGetLastError());
     43         WSACleanup();
     44         return 1;
     45     }
     46 
     47     u_short port = 7890;
     48     char *ip;
     49     sockaddr_in service;
     50     service.sin_family = AF_INET;
     51     service.sin_port = htons(port);
     52     hostent *thisHost;
     53 
     54     thisHost = gethostbyname("");
     55     if (thisHost == NULL) {
     56         wprintf(L"gethostbyname failed with error = %d
    ", WSAGetLastError());
     57         closesocket(ListenSocket);
     58         WSACleanup();
     59         return 1;
     60     }
     61 
     62     ip = inet_ntoa(*(struct in_addr *) *thisHost->h_addr_list);
     63 
     64     service.sin_addr.s_addr = inet_addr(ip);
     65 
     66     //-----------------------------------------
     67     // 绑定
     68     iResult = bind(ListenSocket, (SOCKADDR *) & service, sizeof (SOCKADDR));
     69     if (iResult != 0) {
     70         wprintf(L"bind failed with error = %d
    ", WSAGetLastError());
     71         closesocket(ListenSocket);
     72         WSACleanup();
     73         return 1;
     74     }
     75     //-----------------------------------------
     76     // 监听
     77     iResult = listen(ListenSocket, 1);
     78     if (iResult != 0) {
     79         wprintf(L"listen failed with error = %d
    ", WSAGetLastError());
     80         closesocket(ListenSocket);
     81         WSACleanup();
     82         return 1;
     83     }
     84     wprintf(L"Listening...
    ");
     85 
     86     //-----------------------------------------
     87     // 接受连接
     88     AcceptSocket = accept(ListenSocket, NULL, NULL);
     89     if (AcceptSocket == INVALID_SOCKET) {
     90         wprintf(L"accept failed with error = %d
    ", WSAGetLastError());
     91         closesocket(ListenSocket);
     92         WSACleanup();
     93         return 1;
     94     }
     95     wprintf(L"Client Accepted...
    ");
     96 
     97     //-----------------------------------------
     98     // 创建事件,并初始化Overlapped结构.
     99     EventArray[EventTotal] = WSACreateEvent();
    100     if (EventArray[EventTotal] == WSA_INVALID_EVENT) {
    101         wprintf(L"WSACreateEvent failed with error = %d
    ", WSAGetLastError());
    102         closesocket(AcceptSocket);
    103         closesocket(ListenSocket);
    104         WSACleanup();
    105         return 1;
    106     }
    107 
    108     ZeroMemory(&AcceptOverlapped, sizeof (WSAOVERLAPPED));
    109     AcceptOverlapped.hEvent = EventArray[EventTotal];
    110 
    111     DataBuf.len = DATA_BUFSIZE;
    112     DataBuf.buf = buffer;
    113 
    114     EventTotal++;
    115 
    116     //-----------------------------------------
    117     // 接受数据
    118     if (WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL) ==
    119         SOCKET_ERROR) {
    120             iResult = WSAGetLastError();
    121             if (iResult != WSA_IO_PENDING)
    122                 wprintf(L"WSARecv failed with error = %d
    ", iResult);
    123     }
    124     //-----------------------------------------
    125     // 开始在Socket上操作
    126     while (1) {
    127 
    128         //-----------------------------------------
    129         // 等待重叠I/O完成
    130         Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    131 
    132         //-----------------------------------------
    133         // 重置事件
    134         bResult = WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
    135         if (bResult == FALSE) {
    136             wprintf(L"WSAResetEvent failed with error = %d
    ", WSAGetLastError());
    137         }
    138         //-----------------------------------------
    139         // 确定重叠I/O事件标志
    140         bResult =
    141             WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE,
    142             &Flags);
    143         if (bResult == FALSE) {
    144             wprintf(L"WSAGetOverlappedResult failed with error = %d
    ", WSAGetLastError());
    145         }
    146         //-----------------------------------------
    147         // 如果连接关闭,就关闭Socket
    148         if (BytesTransferred == 0) {
    149             wprintf(L"Closing accept Socket %d
    ", AcceptSocket);
    150             closesocket(ListenSocket);
    151             closesocket(AcceptSocket);
    152             WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
    153             WSACleanup();
    154             return 1;
    155         }
    156         //-----------------------------------------
    157         // 如果接收到数据就在数据发送到客户端
    158         iResult =
    159             WSASend(AcceptSocket, &DataBuf, 1, &RecvBytes, Flags, &AcceptOverlapped, NULL);
    160         if (iResult != 0) {
    161             wprintf(L"WSASend failed with error = %d
    ", WSAGetLastError());
    162         }
    163         //-----------------------------------------         
    164         //重置Overlapped结构
    165         Flags = 0;
    166         ZeroMemory(&AcceptOverlapped, sizeof (WSAOVERLAPPED));
    167 
    168         AcceptOverlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];
    169 
    170         //-----------------------------------------
    171         // 重置缓冲区
    172         DataBuf.len = DATA_BUFSIZE;
    173         DataBuf.buf = buffer;
    174     }
    175 
    176     closesocket(ListenSocket);
    177     closesocket(AcceptSocket);
    178     WSACleanup();
    179     return 0;
    180 }
    View Code

    WSAEnumNetworkEvent 使用举例

     1 //WSAEnumNetworkEvent 使用举例
     2 #include <windows.h>
     3 #include <winsock2.h>
     4 #include <Ws2tcpip.h>
     5 #include <stdio.h>
     6 #pragma comment(lib, "Ws2_32.lib")
     7 int main()
     8 {
     9     //-------------------------
    10     WSADATA wsaData;
    11     int iResult;
    12 
    13     SOCKET SocketArray[WSA_MAXIMUM_WAIT_EVENTS], ListenSocket;
    14     WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
    15     WSANETWORKEVENTS NetworkEvents;
    16     sockaddr_in InetAddr;
    17     DWORD EventTotal = 0;
    18     DWORD Index;
    19     DWORD i;
    20 
    21     HANDLE NewEvent = NULL; 
    22 
    23     // 初始化
    24     iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    25     if (iResult != 0) 
    26     {
    27         wprintf(L"WSAStartup failed with error: %d
    ", iResult);
    28         return 1;
    29     }
    30 
    31     //创建
    32     ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    33     if (ListenSocket == INVALID_SOCKET) 
    34     {
    35         wprintf(L"socket function failed with error: %d
    ", WSAGetLastError() );
    36         return 1;
    37     }
    38 
    39     InetAddr.sin_family = AF_INET;
    40     InetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    41     InetAddr.sin_port = htons(7890);
    42 
    43     //绑定
    44     iResult = bind(ListenSocket, (SOCKADDR *) & InetAddr, sizeof (InetAddr));
    45     if (iResult != 0) 
    46     {
    47         wprintf(L"bind failed with error: %d
    ", WSAGetLastError() );
    48         return 1;
    49     }
    50 
    51     // 创建事件
    52     NewEvent = WSACreateEvent();
    53     if (NewEvent == NULL)
    54     {
    55         wprintf(L"WSACreateEvent failed with error: %d
    ", GetLastError() );
    56         return 1;
    57     }    
    58     // 将事件类型绑定到套接字上
    59     iResult = WSAEventSelect(ListenSocket, NewEvent, FD_ACCEPT | FD_CLOSE);
    60     if (iResult != 0) 
    61     {
    62         wprintf(L"WSAEventSelect failed with error: %d
    ", WSAGetLastError() );
    63         return 1;
    64     }
    65 
    66     // 监听
    67     iResult = listen(ListenSocket, 10);
    68     if (iResult != 0)
    69     {
    70         wprintf(L"listen failed with error: %d
    ", WSAGetLastError() );
    71         return 1;
    72     }
    73     // 添加事件
    74     SocketArray[EventTotal] = ListenSocket;
    75     EventArray[EventTotal] = NewEvent;
    76     EventTotal++;
    77 
    78     // 等待所有套接字上的事件发生
    79     Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    80     Index = Index - WSA_WAIT_EVENT_0;
    81 
    82     //枚举事件类型,直到失败
    83     for (i = Index; i < EventTotal; i++)
    84     {
    85         Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
    86         if ((Index != WSA_WAIT_FAILED) && (Index != WSA_WAIT_TIMEOUT)) 
    87         {
    88             WSAEnumNetworkEvents(SocketArray[i], EventArray[i], &NetworkEvents);
    89         }
    90     }
    91 
    92     //...
    93     return 0;
    94 }
    View Code

    4、重叠I/O模式

      重叠I./O模式只支持Winsock2 ,可以使用WSARecv和WSASend实现

      关键字  WSA_FLAG_OVERLAPPED

      函数WSASocket在创建时可以手动指定模式

    SOCKET  s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);

    而函数socket默认就是重叠I/O模式。

    typedef struct _WSAOVERLAPPED {
      ULONG_PTR Internal;
      ULONG_PTR InternalHigh;
      union {
        struct {
          DWORD Offset;
          DWORD OffsetHigh;
        };
        PVOID  Pointer;
      };
      HANDLE    hEvent;//
    } WSAOVERLAPPED, *LPWSAOVERLAPPED;

      前几个参数有系统维护,只有hEvent字段有点儿特殊,它允许应用程序将一个事件对象句柄同一个套接字关联起来。

    //结构Overlapped
    BOOL WSAAPI WSAGetOverlappedResult(
      _In_   SOCKET s,
      _In_   LPWSAOVERLAPPED lpOverlapped,
      _Out_  LPDWORD lpcbTransfer,//一次收发的实际字节数
      _In_   BOOL fWait,//在重叠I/O模式中无效
      _Out_  LPDWORD lpdwFlags//接收结果标志
    );

    对重叠I/O操作进行管理过程

      1)创建一个套接字,开始在指定的端口上监听连接请求。

      2)接受一个进入的连接请求。

      3)为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将事件对象句柄分配给一个事件数组,以便稍后由WSAWaitForMultipleEvents函数使用。

      4)在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。注意函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDING(I/O操作尚未完成)。

      5)使用步骤3)的事件数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件的“触发”)。

      6)WSAWaitForMultipleEvents函数完成后,针对事件数组,调用WSAResetEvent(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。

      7)使用WSAGetOverlappedResult函数,判断重叠调用的返回状态是什么。

      8)在套接字上投递另一个重叠WSARecv请求。

      9)重复步骤5-8)。

    5、完成端口模型

    适合在管理成千上万的套接字时使用。

    使用这种模型之前,首先要创建一个I/O完成端口对象,用它面向任意数量的套接字句柄,管理多个I/O请求。要做到这一点,需要调用CreateCompletionPort函数。函数有两种功能:

      1. 用于创建一个完成端口对象。
      2. 将一个句柄同完成端口关联到一起。

    //创建一个完成端口对象
    HANDLE CreateIoCompletionPort(
        HANDLE FileHandle,  //关联的文件句柄,可能为套接字句柄
    HANDLE ExistingCompletionPort,//已经存在的完成端口
    LONG_PTR CompletionKey,// 传送给处理函数的参数
        DWORD NumberOfConcurrentThreads//有多少个线程在访问这个消息队列
        );

    参数ExistingCompletionPort表示已经存在的完成端口。如果为NULL,则为新建一个IOCP(I/O完成端口)。

    创建过程

    HANDLE  completionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

    //获取排队完成的状态
    BOOL  GetQueuedCompletionStatus(
        __in  HANDLE CompletionPort,//完成端口
        __out LPDWORD lpNumberOfBytesTransferred,//实际接受的数据
        __out PULONG_PTR lpCompletionKey,//“单句柄数据”
        __out LPOVERLAPPED *lpOverlapped,//重叠I/O结构
        __in  DWORD dwMilliseconds//超时时间
    );

    一旦所有套接字句柄都已关闭,便需在完成端口上,终止所有工作者线程的运行,此时调用函数PostQueuedCompletionStatus向所有线程发送终止命令。

    BOOL PostQueuedCompletionStatus(
        __in     HANDLE CompletionPort,//完成端口句柄
        __in     DWORD dwNumberOfBytesTransferred,//实际接受数据
        __in     ULONG_PTR dwCompletionKey,//完成端口关键字“单句柄数据”
        __in_opt LPOVERLAPPED lpOverlapped//重叠I/O结构
        );

    使用过程

        1、创建一个完成端口,第四个参数保持为0,指定在完成端口上,每个处理器一次只允许执行一个工作者线程

        2、判断系统内到底安装了几个处理器

        3、创建工作者线程,根据步骤2得到的处理器信息,在完成端口上,为已完成的I/O请求提供服务

        4、准备好一个监听套接字

        5、使用accept函数,接受进入的请求

        6、创建一个数据结构,用于容纳“单句柄数据”,同时在结构中存入接受的套接字句柄。

        7、调用CreateCompletionPort,将之accept返回的新套接字同完成端口关联到一起

        8、开始在已接受的连接上进行I/O操作

        9、重复5到8,直到服务器终止。

    这些操作对套接字I/O至关重要,需要理解

  • 相关阅读:
    区间筛——模板
    I NEED A OFFER!
    dp入门(A)
    java如何创建类
    二维数组
    一维数组
    循环练习记录
    根据浏览器屏幕分辨率不同使用不同的css样式——响应式
    jquery 淡入淡出动画效果例子
    toggle()在新闻热点上的运用
  • 原文地址:https://www.cnblogs.com/songliquan/p/3445767.html
Copyright © 2011-2022 走看看