Socket I/O 模型 学习(Cpp2010-8-10)-www.myext.cn,E...

来源:百度文库 编辑:神马文学网 时间:2024/04/30 12:13:26

Socket I/O 模型 学习

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

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

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

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

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

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

  1 #include "stdafx.h"
 2 #include 
 3 #include 
 4 #include 
 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( 2, 2 );
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 
  2 #include 
  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, 0, 0))
 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 完成例程模型 了。