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(  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  < 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,  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 完成例程模型 了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值