zoukankan      html  css  js  c++  java
  • Socket I/O 模型 学习

    我们为什么要使用Socket I/O模型呢?还得从Socket的阻塞和非阻塞说起。

    在网上看过一篇讲解I/O模型的文章,它举过一个例子觉得挺好,那就是收信的例子。

    比如:老周在等待他女儿从美国寄过来的信件,老周住三楼,信箱在一楼。有以下几种情况:

    第一:老周一直守在信箱旁边,直到收到信件为止。这样太费精力。这就好比是阻塞套接字。

    第二:老周到信箱那里看一下,发现还没有来,就马上回家了。这就好比非阻塞套接字。

    第三:老周先打个电话到一楼管理员问一下自己的信件是否到了,如果到了才下楼去取信件。当然这样浪费电话费,但是值得的。这可以比作Socket的Select I/O模型。

     
     1 #include "stdafx.h"
     
    2 #include <iostream>
     
    3 #include <winsock2.h>
     
    4 #include <windows.h>
     
    5 
     
    6 #define TRACE ATLTrace //必须要加上这个宏定义,否则在WIN32的控制台程序中是不能直接用的
     
    7 
     
    8 #define InternetAddr "127.0.0.1"
     
    9 #define iPort 5055
    10 
    11 #pragma comment(lib, "ws2_32.lib")
    12 
    13 int _tmain(int argc, _TCHAR* argv[])
    14 {
    15     WSADATA wsa;
    16     WORD wVersionRequested;
    17     int err;
    18 
    19    wVersionRequested = MAKEWORD( 22 );
    20     err = WSAStartup( wVersionRequested, &wsa);
    21     if ( err != 0 ) {
    22     //Tell the user that we could not find a usable 
    23     //WinSock DLL.     
    24     TRACE("你忘记添加WinSock DLL了\n");
    25     WSACleanup();
    26     return 1;
    27      }
    28 
    29    // Create a SOCKET for listening for  incoming connection requests
    30     SOCKET fdServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    31    
    32     sockaddr_in server;
    33 
    34  server.sin_family = AF_INET;
    35  server.sin_addr.s_addr = inet_addr(InternetAddr);
    36  server.sin_port = htons(iPort);
    37  //Bind the socket.
    38     int ret = bind(fdServer, (sockaddr*)&server, sizeof(server));
    39     ret = listen(fdServer, 4);
    40 
    41     SOCKET AcceptSocket;
    42     fd_set     fdread;
    43  timeval    tv;
    44  int nSize;
    45    //其实也算是轮训,那么对阻塞socket用select和对使用非阻塞socket的优点在哪?
    46   //可能的优点就是避免在非阻塞套接字里重复检查WSAEWOULDBLOCK错误。
    47   while(1)
    48   {
    49                  
    50          FD_ZERO(&fdread);//初始化fd_set
    51          FD_SET(fdServer, &fdread);//分配套接字句柄到相应的fd_set
    52                              
    53         tv.tv_sec = 2;//这里我们打算让select等待两秒后返回,避免被锁死,也避免马上返回
    54         tv.tv_usec = 0;
    55                                                  
    56         select(0&fdread, NULL, NULL, &tv);
    57                                                          
    58         nSize = sizeof(server);
    59         //先判断fdServer是否还在fd_set内来判断是否可以读,这样就避免因为 accept在等待
    60         //时造成的阻塞
    61         if (FD_ISSET(fdServer, &fdread))
    62             //如果套接字句柄还在fd_set里,说明客户端已经有connect的请求发过来了,
    63             //马上可以accept成功
    64          {
    65              AcceptSocket = accept(fdServer,( sockaddr*&server, &nSize);
    66              break;
    67            }                                             
    68         else
    69         //还没有客户端的connect请求,我们可以去做别的事,避免像没有用select方式
    70         //的阻塞套接字程序被锁死的情况,如果没用select,当程序运行到accept的时候客户
    71         //端恰好没有connect请求,那么程序就会被锁死,做不了任何事情
    72             {
    73             //do something
    74                MessageBox(NULL, "waiting""recv", MB_ICONINFORMATION);
    75         //别的事做完后,继续去检查是否有客户端连接请求
    76             }
    77    }
    78 
    79    char buffer[128];
    80       ZeroMemory(buffer, 128);
    81 
    82          ret = recv(AcceptSocket,buffer,128,0);//这里同样可以用select,用法和上面一样
    83 
    84          MessageBox(NULL, buffer, "recv", MB_ICONINFORMATION);
    85 
    86         closesocket(AcceptSocket);
    87         WSACleanup();
    88         return 0;
    89 }

    第四:老周告诉一楼管理员,如果有他的信件就通知老周。这可以比做Socket的WSAAsynSelect模型 

      
      1 #include <winsock.h>
      
    2 #include <tchar.h>
      
    3 
      
    4 #define PORT         5150
      
    5 #define MSGSIZE      1024
      
    6 #define WM_SOCKET WM_USER+0
      
    7 
      
    8 #pragma comment(lib, "ws2_32.lib")
      
    9 
     
    10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
     
    11 
     
    12 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
     
    13 {
     
    14      static TCHAR szAppName[] = _T("AsyncSelect Model");
     
    15      HWND            hwnd ;
     
    16      MSG             msg ;
     
    17      WNDCLASS        wndclass ;
     
    18 
     
    19      wndclass.style            = CS_HREDRAW | CS_VREDRAW ;
     
    20      wndclass.lpfnWndProc      = WndProc ;
     
    21      wndclass.cbClsExtra       = 0 ;
     
    22      wndclass.cbWndExtra       = 0 ;
     
    23      wndclass.hInstance        = hInstance ;
     
    24      wndclass.hIcon            = LoadIcon (NULL, IDI_APPLICATION) ;
     
    25      wndclass.hCursor          = LoadCursor (NULL, IDC_ARROW) ;
     
    26      wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     
    27      wndclass.lpszMenuName     = NULL ;
     
    28      wndclass.lpszClassName = szAppName ;
     
    29 
     
    30      if (!RegisterClass(&wndclass))
     
    31      {
     
    32        MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ;
     
    33        return 0 ;
     
    34      }
     
    35 
     
    36      hwnd = CreateWindow (szAppName,                     // window class name
     37                           TEXT ("AsyncSelect Model"), // window caption
     38                           WS_OVERLAPPEDWINDOW,           // window style
     39                           CW_USEDEFAULT,                 // initial x position
     40                           CW_USEDEFAULT,                 // initial y position
     41                           CW_USEDEFAULT,                 // initial x size
     42                           CW_USEDEFAULT,                 // initial y size
     43                           NULL,                          // parent window handle
     44                           NULL,                          // window menu handle
     45                           hInstance,                     // program instance handle
     46                           NULL) ;                        // creation parameters
     47 
     
    48      ShowWindow(hwnd, iCmdShow);
     
    49      UpdateWindow(hwnd);
     
    50 
     
    51      while (GetMessage(&msg, NULL, 00))
     
    52      {
     
    53        TranslateMessage(&msg) ;
     
    54        DispatchMessage(&msg) ;
     
    55      }
     
    56   
     
    57      return msg.wParam;
     
    58 }
     
    59 
     
    60 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     
    61 {
     
    62      WSADATA          wsd;
     
    63      static SOCKET sListen;
     
    64      SOCKET           sClient;
     
    65      SOCKADDR_IN      local, client;
     
    66      int              ret, iAddrSize = sizeof(client);
     
    67      char             szMessage[MSGSIZE];
     
    68 
     
    69      switch (message)
     
    70      {
     
    71 case WM_CREATE:
     
    72        // Initialize Windows Socket library
     73      WSAStartup(0x0202&wsd);
     
    74   
     
    75      // Create listening socket
     76        sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     
    77     
     
    78      // Bind
     79        local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
     
    80      local.sin_family = AF_INET;
     
    81      local.sin_port = htons(PORT);
     
    82      bind(sListen, (struct sockaddr *)&local, sizeof(local));
     
    83   
     
    84      // Listen
     85        listen(sListen, 3);
     
    86 
     
    87        // Associate listening socket with FD_ACCEPT event
     88      WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);
     
    89      return 0;
     
    90 
     
    91      case WM_DESTROY:
     
    92        closesocket(sListen);
     
    93        WSACleanup();
     
    94        PostQuitMessage(0);
     
    95        return 0;
     
    96   
     
    97      case WM_SOCKET:
     
    98        if (WSAGETSELECTERROR(lParam))//lParam的高字节包含了可能出现的任何的错误代码
     99        {
    100          closesocket(wParam);
    101          break;
    102        }
    103     
    104        switch (WSAGETSELECTEVENT(lParam)) //lParam的低字节指定已经发生的网络事件
    105        {
    106        case FD_ACCEPT:
    107          // Accept a connection from client
    108          sClient = accept(wParam, (struct sockaddr *)&client, &iAddrSize);
    109       
    110          // Associate client socket with FD_READ and FD_CLOSE event
    111          WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_CLOSE);
    112          break;
    113 
    114        case FD_READ:
    115          ret = recv(wParam, szMessage, MSGSIZE, 0);
    116 
    117          if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
    118          {
    119            closesocket(wParam);
    120          }
    121          else
    122          {
    123            szMessage[ret] = '\0';
    124            send(wParam, szMessage, strlen(szMessage), 0);
    125          }
    126          break;
    127       
    128        case FD_CLOSE:
    129          closesocket(wParam);      
    130          break;
    131        }
    132        return 0;
    133      }
    134   
    135      return DefWindowProc(hwnd, message, wParam, lParam);
    136 }

     第五:老周要一楼管理员发个短信到他们家里去。相当如WSAEventSelect模型,其实WSAEventSelect模型类似WSAAsynSelect模型,但最主要的区别是网络事件发生时会被发送到一个事件对象句柄,而不是发送到一个窗口。这样可能更加的好,对于服务器端的程序来说。

     
     1 SOCKET       Socket[WSA_MAXIMUM_WAIT_EVENTS];
     
    2 WSAEVENT   Event[WSA_MAXINUM_WAIT_EVENTS];
     
    3 SOCKET    Accept, Listen;
     
    5 DWORD     EventTotal = 0;
     
    6 DWORD     Index;
     
    7 
     
    8 //Set up a TCP socket for listening on port 5150
     9 Listen = socket(PF_INET,SOCK_STREAM,0);
    10 
    11 InternetAddr.sin_family      = AF_INET;
    12 InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    13 InternetAddr.sin_port        = htons(5150);
    14 
    15 bind(Listen,(PSOCKADDR) &InternetAddr,sizeof(InternetAddr));
    16 
    17 NewEvent = WSACreateEvent();
    18 
    19 WSAEventSelect(Listen,NewEvnet,FD_ACCEPT|FD_CLOSE);
    20 
    21 listen(Listen,5);
    22 
    23 Socket[EventTotal] = Listen;
    24 Event[EventTotal] = NewEvent;
    25 EventTotal++;
    26 
    27 while (TRUE)
    28 {
    29     //Wait for network events on all sockets
    30     Index = WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);
    31 
    32     WSAEnumNewWorkEvents(SocketArray[Index-WSA_WAIT_EVENT_0],
    33         EventArray[Index-WSA_WAIT_EVENT_0],
    34         &NetworkEvents);
    35     //Check for FD_ACCEPT messages
    36     if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
    37     {
    38         if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] !=0)
    39         {
    40             //Error
    41             break;
    42         }
    43         //Accept a new connection and add it to the socket and event lists
    44         Accept = accept(SocketArray[Index-WSA_WAIT_EVENT_0],NULL,NULL);
    45 
    46         //We cannot process more than WSA_MAXIMUM_WAIT_EVENTS sockets ,
    47         //so close the accepted socket
    48         if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
    49         {
    50             printf("..");
    51             closesocket (Accept);
    52             break;
    53         }
    54         NewEvent = WSACreateEvent();
    55 
    56         WSAEventSelect(Accept,NewEvent,FD_READ|FD_WRITE|FD_CLOSE);
    57 
    58         Event[EventTotal] = NewEvent;
    59         Socket[EventTotal]= Accept;
    60         EventTotal++;
    61         prinrt("Socket %d connect\n",Accept);
    62     }
    63     //Process FD_READ notification
    64     if (NetworkEvents.lNetwoAD)rkEvents & FD_RE
    65     {
    66         if (NetworkEvents.iErrorCode[FD_READ_BIT !=0])
    67         {
    68             //Error
    69             break;
    70         }
    71 
    72         //Read data from the socket
    73         recv(Socket[Index-WSA_WAIT_EVENT_0],buffer,sizeof(buffer),0);
    74     }
    75     //process FD_WRITE notitication
    76     if (NetworkEvents.lNetworkEvents & FD_WRITE)
    77     {
    78         if (NetworkEvents.iErrorCode[FD_WRITE_BIT] !=0)
    79         {
    80             //Error
    81             break;
    82         }
    83         send(Socket[Index-WSA_WAIT_EVENT_0],buffer,sizeof(buffer),0);
    84     }
    85     if (NetworkEvents.lNetworkEvents & FD_CLOSE)
    86     {
    87         if(NetworkEvents.iErrorCode[FD_CLOSE_BIT] !=0)
    88         {
    89             //Error
    90             break;
    91         }
    92         closesocket (Socket[Index-WSA_WAIT_EVENT_0]);
    93         //Remove socket and associated event from the Socket and Event arrays and
    94         //decrement eventTotal
    95         CompressArrays(Event,Socket,& EventTotal);
    96     }
    97 }

    第六:老周可以要求一楼管理员把信件送到他们家去,好比Overlapped I/O 事件通知模型。

    第七:老周还可以要求不仅送信件,还可以要求管理员帮他把信封打开,读给老周听(假设老周为文盲,管理员也够累的),这就是Overlapped I/O 完成例程模型 了。

  • 相关阅读:
    Ubuntu 配置IP地址方法
    ubuntu server 16.04安装GPU服务器
    Ubuntu 自动获取ip地址
    Typedef 用法
    linux mount命令详解(iso文件挂载)
    specrate 与specspeed 的区别
    SPEC CPU 使用简介
    编译错误you should not run configure as root (set FORCE_UNSAFE_CONFIGURE=1 in environment to bypass this check)
    SPEC CPU 2006编译perl 出错:undefined reference to `pow'
    'gets' undeclared here (not in a function)
  • 原文地址:https://www.cnblogs.com/pbreak/p/1788589.html
Copyright © 2011-2022 走看看