winsock重叠IO模型

来源:互联网 发布:js生态圈 编辑:程序博客网 时间:2024/03/28 17:51
[cpp] view plaincopy
  1. //服务端  
  2. /* 
  3. 。在程序清单7-8 中,我们向大家展示了如何构建一个简单 
  4. 的服务器应用,令其采用前述的方法,通过完成例程,来实现对一个套接字请求的管理。该程序的编 
  5. 码主要按下述步骤进行: 
  6.  
  7. ① 新建一个套接字,开始在指定端口上,监听一个进入的连接。 
  8.  
  9. ② 接受一个进入的连接请求。 
  10.  
  11. ③ 为接受的套接字创建一个WSAOVERLAPPED 结构。 
  12.  
  13. ④ 在套接字上投递一个异步WSARecv 请求,方法是将WSAOVERLAPPED 指定成为参数,同时提供 
  14. 一个完成例程。 
  15.  
  16. ⑤ 在将fAlertable 参数设为TRUE 的前提下,调用WSAWaitForMultipleEvents,并等待一个重 
  17. 叠I/O 请求完成。重叠请求完成后,完成例程会自动执行,而且WSAWaitForMultipleEvents 会返回一 
  18. 个WSA_IO_COMPLETION。在完成例程内,可随一个完成例程一道,投递另一个重叠WSARecv 请求。 
  19.  
  20. ⑥ 检查WSAWaitForMultipleEvents 是否返回WSA_IO_COMPLETION。 
  21. ⑦ 重复第⑤步和第⑥步。 
  22.  
  23. 程序清单7-8 采用完成例程的简单重叠I/O 处理示例 
  24. // 说明:该例介绍了如何使用重叠I/O 模型开发简单的回显服务器应用,该例也是简单的控制 
  25. 台程序, 
  26.  
  27.   */  
  28. // 它会在连接从服务器建立和移除时打印消息。该应用会在5150 端口监听和接收进入的TCP 连  
  29. //接,  
  30. // 收到客户端发送的数据时,会把收到的数据按照原来的格式发送回去  
  31.   
  32.   
  33.   
  34. // 编译:cl -o overlap overlap.cpp ws2_32.lib  
  35. #include <winsock2.h>  
  36. #include <windows.h>  
  37. #include <stdio.h>  
  38.   
  39.   
  40. #pragma comment(lib,"ws2_32.lib")   
  41.   
  42. #define PORT 5150  
  43. #define DATA_BUFSIZE 8192  
  44.   
  45.   
  46. typedef struct _SOCKET_INFORMATION {  
  47.     CHAR Buffer[DATA_BUFSIZE];     
  48.     WSABUF DataBuf;  
  49.     SOCKET Socket;  
  50.     WSAOVERLAPPED Overlapped;  
  51.     DWORD BytesSEND;  
  52.     DWORD BytesRECV;  
  53. } SOCKET_INFORMATION, * LPSOCKET_INFORMATION;  
  54.   
  55.   
  56.   
  57.   
  58. DWORD EventTotal = 0;   //事件对象总数  
  59.   
  60. DWORD WINAPI ProcessIO(LPVOID lpParameter);  
  61.   
  62. WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];  
  63.   
  64. LPSOCKET_INFORMATION SocketArray[WSA_MAXIMUM_WAIT_EVENTS];     
  65.   
  66. CRITICAL_SECTION CriticalSection;  
  67.   
  68.   
  69. int main(void)  
  70. {  
  71.     WSADATA wsaData;  
  72.   
  73.     SOCKET ListenSocket, AcceptSocket;  
  74.   
  75.     SOCKADDR_IN InternetAddr;  
  76.   
  77.     DWORD Flags;  
  78.   
  79.     DWORD ThreadId;  
  80.   
  81.     DWORD RecvBytes;  
  82.   
  83.     INT Ret;  
  84.   
  85.     InitializeCriticalSection(&CriticalSection);   //初始化临界区  
  86.   
  87.     printf("主函数开始执行\n") ;  
  88.   
  89.     if ((Ret = WSAStartup(0x0202,&wsaData)) != 0)  
  90.     {  
  91.         printf("WSAStartup failed with error %d\n", Ret);  
  92.         WSACleanup();  
  93.         return 0 ;  
  94.     }  
  95.     if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,  
  96.         WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)  //创建监听端口  
  97.     {  
  98.         printf("Failed to get a socket %d\n", WSAGetLastError());  
  99.         return 0 ;  
  100.     }  
  101.       
  102.     InternetAddr.sin_family = AF_INET;  
  103.     InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  104.     InternetAddr.sin_port = htons(PORT);  
  105.   
  106.     if (bind(ListenSocket, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) ==  
  107.         SOCKET_ERROR)  //绑定端口  
  108.     {  
  109.         printf("bind() failed with error %d\n", WSAGetLastError());  
  110.         return 0 ;  
  111.     }  
  112.     if (listen(ListenSocket, 5))  
  113.     {  
  114.         printf("listen() failed with error %d\n", WSAGetLastError());  
  115.         return  0 ;  
  116.     }  
  117.   
  118.   
  119.     // 建立连接的监听套接字  
  120.     if ((AcceptSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,  
  121.         WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)   //这里创建了一个异步的  
  122.     {  
  123.         printf("Failed to get a socket %d\n", WSAGetLastError());  
  124.         return  0 ;  
  125.     }  
  126.   
  127.     if ((EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT)  //第一个事件对象  
  128.     {  
  129.         printf("WSACreateEvent failed with error %d\n", WSAGetLastError());  
  130.         return  0 ;  
  131.     }  
  132.     // 创建服务重叠请求的线程  
  133.     printf("创建线程\n") ;  
  134.     if (CreateThread(NULL, 0, ProcessIO, NULL, 0, &ThreadId) == NULL)  //为什么不叫ThreadIO,也叫ProcessIO咧?  
  135.     {  
  136.         printf("CreateThread failed with error %d\n", GetLastError());  
  137.         return  0 ;  
  138.     }  
  139.   
  140.     EventTotal = 1;  //突然间冒出来  
  141.   
  142.   
  143.     int i = 0 ;  
  144.     while(TRUE)  
  145.     {  
  146.         // 接收进入的连接  
  147.           
  148.         printf("Main第%d次循环\n",i+1) ;  
  149.   
  150.         i++ ;  
  151.   
  152.         printf("等待连接,兼进入临界区\n") ;  
  153.         if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) == INVALID_SOCKET)  
  154.         {  
  155.             printf("accept failed with error %d\n", WSAGetLastError());  
  156.             return 0 ;  
  157.         }  
  158.   
  159.   
  160.           
  161.         EnterCriticalSection(&CriticalSection);   //临界区??  
  162.   
  163.   
  164.   
  165.   
  166.         // 创建同接收的套接字相关的套接字信息结构体  
  167.         if ((SocketArray[EventTotal] = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR,  
  168.             sizeof(SOCKET_INFORMATION))) == NULL)  
  169.         {  
  170.             printf("GlobalAlloc() failed with error %d\n", GetLastError());  
  171.             return 0 ;  
  172.         }  
  173.         // 填充接收套接字的细节  
  174.         SocketArray[EventTotal]->Socket = AcceptSocket;  //新套接字的?  
  175.   
  176.         ZeroMemory(&(SocketArray[EventTotal]->Overlapped), sizeof(OVERLAPPED));   
  177.   
  178.         SocketArray[EventTotal]->BytesSEND = 0;  
  179.   
  180.         SocketArray[EventTotal]->BytesRECV = 0;  
  181.   
  182.         SocketArray[EventTotal]->DataBuf.len = DATA_BUFSIZE;  
  183.   
  184.         SocketArray[EventTotal]->DataBuf.buf = SocketArray[EventTotal]->Buffer;  
  185.   
  186.         if ((SocketArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] =  
  187.             WSACreateEvent()) == WSA_INVALID_EVENT)    //将第几个事件对象赋给  
  188.         {  
  189.             printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());  
  190.             return  0;  
  191.         }  
  192.         // 发布WSARecv 请求开始在套接字上接收数据  
  193.         Flags = 0;  
  194.   
  195.         if (WSARecv(SocketArray[EventTotal]->Socket,  
  196.             &(SocketArray[EventTotal]->DataBuf), 1, &RecvBytes, &Flags,  
  197.             &(SocketArray[EventTotal]->Overlapped), NULL) == SOCKET_ERROR)  
  198.         {  
  199.             if (WSAGetLastError() != ERROR_IO_PENDING)  
  200.             {  
  201.                 printf("WSARecv() failed with error %d\n", WSAGetLastError());  
  202.                 return 0;  
  203.             }  
  204.         }  
  205.   
  206.         EventTotal++;  
  207.   
  208.         printf("离开临界区\n") ;  
  209.         LeaveCriticalSection(&CriticalSection);  
  210.   
  211.         // 信号通知事件数组中的第一个事件,让工作线程服务事件数组中的其它事件  
  212.         if (WSASetEvent(EventArray[0]) == FALSE)  
  213.         {  
  214.             printf("WSASetEvent failed with error %d\n", WSAGetLastError());  
  215.             return  0 ;  
  216.         }  
  217.   
  218.   
  219.   
  220.     }//while  
  221.   
  222.   
  223.     return 0 ;  
  224. }  
  225.   
  226. DWORD WINAPI ProcessIO(LPVOID lpParameter)  
  227. {  
  228.     DWORD Index;  
  229.       
  230.     DWORD Flags;  
  231.       
  232.     LPSOCKET_INFORMATION SI;  
  233.       
  234.     DWORD BytesTransferred;  
  235.       
  236.     DWORD i;  
  237.       
  238.     DWORD RecvBytes, SendBytes;  
  239.       
  240.     printf("进入ProcessIO\n") ;  
  241.       
  242.     int j = 0 ;  
  243.     // 处理异步WSASend、WSARecv 请求  
  244.     while(TRUE)  
  245.     {  
  246.           
  247.         printf("ProcessIO第%d次循环\n",j+1) ;  
  248.         j++ ;  
  249.           
  250.           
  251.         if ((Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE,  
  252.             WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED)  
  253.         {  
  254.             printf("WSAWaitForMultipleEvents failed %d\n", WSAGetLastError());  
  255.             return 0;  
  256.         }  
  257.         // 如果触发的事件为零,然后会在我们的监听套接字上进行连接尝试  
  258.         if ((Index - WSA_WAIT_EVENT_0) == 0)  //如果是0的话,就直接跳到下一个循环?什么回事?  
  259.         {  
  260.             WSAResetEvent(EventArray[0]);  
  261.             continue;  
  262.         }  
  263.           
  264.         SI = SocketArray[Index - WSA_WAIT_EVENT_0];  
  265.           
  266.         WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);  
  267.           
  268.         if (WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred,  
  269.             FALSE, &Flags) == FALSE || BytesTransferred == 0)  
  270.         {  
  271.             printf("Closing socket %d\n", SI->Socket);  
  272.             if (closesocket(SI->Socket) == SOCKET_ERROR)  
  273.             {  
  274.                 printf("closesocket() failed with error %d\n", WSAGetLastError());  
  275.             }  
  276.             GlobalFree(SI);  
  277.               
  278.             WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);  
  279.               
  280.             // 通过删除事件句柄和套接字信息结构体来清除SocketArray 和EventArray  
  281.               
  282.               
  283.             EnterCriticalSection(&CriticalSection);  //临界区。。  
  284.               
  285.               
  286.               
  287.               
  288.             if ((Index - WSA_WAIT_EVENT_0) + 1 != EventTotal)  
  289.                 for (i = Index - WSA_WAIT_EVENT_0; i < EventTotal; i++)  
  290.                 {  
  291.                     EventArray[i] = EventArray[i + 1];  
  292.                     SocketArray[i] = SocketArray[i + 1];  
  293.                 }  
  294.                   
  295.                 EventTotal--;  
  296.                   
  297.                 LeaveCriticalSection(&CriticalSection);  //离开临界区  
  298.                 continue;  
  299.         }  
  300.         // 查看BytesRECV 域是否为0,然后这意味着WSARecv 调用刚刚完成,因此可以用从已完  
  301.         //成  
  302.         // 的WSARecv 调用中获得的BytesTransferred 值来更新 BytesRECV 域  
  303.         if (SI->BytesRECV == 0)  
  304.         {  
  305.             SI->BytesRECV = BytesTransferred;  
  306.             SI->BytesSEND = 0;  
  307.         }  
  308.         else  
  309.               
  310.         {  
  311.             SI->BytesSEND += BytesTransferred;  
  312.         }  
  313.           
  314.         if (SI->BytesRECV > SI->BytesSEND)  
  315.         {  
  316.             // 发布另一个WSASend()请求,因为WSASend()不能确保发送所以请求的字节,继续  
  317.             //发布  
  318.             // WSASend()调用直到所以收到的字节被发送  
  319.             ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));  
  320.             SI->Overlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];  
  321.             SI->DataBuf.buf = SI->Buffer + SI->BytesSEND;  
  322.             SI->DataBuf.len = SI->BytesRECV - SI->BytesSEND;  
  323.             if (WSASend(SI->Socket, &(SI->DataBuf), 1, &SendBytes, 0,  
  324.                 &(SI->Overlapped), NULL) == SOCKET_ERROR)  //这里发出去  
  325.             {  
  326.                 if (WSAGetLastError() != ERROR_IO_PENDING)  
  327.                 {  
  328.                     printf("WSASend() failed with error %d\n", WSAGetLastError());  
  329.                     return 0;  
  330.                 }  
  331.             }  
  332.         }  
  333.         else  
  334.         {  
  335.             SI->BytesRECV = 0;  
  336.             // 现在不在需要发布WSARecv 请求来发送其余的数据  
  337.             Flags = 0;  
  338.               
  339.             ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED));  
  340.               
  341.             SI->Overlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];  
  342.               
  343.             SI->DataBuf.len = DATA_BUFSIZE;  
  344.               
  345.             SI->DataBuf.buf = SI->Buffer;  
  346.               
  347.             if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &RecvBytes, &Flags,  
  348.                 &(SI->Overlapped), NULL) == SOCKET_ERROR)  //接收事件  
  349.             {  
  350.                 if (WSAGetLastError() != ERROR_IO_PENDING)  
  351.                 {  
  352.                     printf("WSARecv() failed with error %d\n", WSAGetLastError());  
  353.                     return 0;  
  354.                 }  
  355.             }  
  356.             else  
  357.             {  
  358.             //  printf("接收: %s",SI->DataBuf ) ; 一次是接收不完的  
  359.             }  
  360.         }  
  361.         }  
  362.           
  363.         printf("离开ProcessIO\n") ;  
  364. }  


 

 

 

[cpp] view plaincopy
  1. //客户端  
  2. /* 
  3.  
  4. 程序清单6-2 是客户端代码,客户端建立一个套接字,并对投入应用的服务器名进行解析,然后 
  5. 与服务器建立连接。连接一旦建成,就可发送大量的消息了。每次发送数据之后,客户端都会等待服 
  6. 务器发回的回应。客户端把得自套接字的数据打印出来。 
  7. 回应客户端和服务器不能完全说明TCP 协议的流式传输。这是因为读取操作是在写操作之后进行 
  8. 的,至少客户端这一端是这样的。当然,对服务器来说,还有另一种方式。因此,服务器每次调用读 
  9. 取函数,一般都会返回客户端发出的整条消息。但不要误会,如果客户端的消息大到超过了TCP 的最 
  10. 大传输单元,在线上,它会被分成几个小的数据包,这种情况下,接收端需要多次执行接收调用,才 
  11. 能收完整条消息。为了更好地说明流式传输,运行客户端和服务器时带上-O 选项即可。这样,客户端 
  12. 便只管发送数据,接收端只管读取数据。 
  13.  
  14.            
  15.              
  16. 服务器如下执行: 
  17. server -p:5150 -o 
  18. 而客户端如下执行: 
  19. client -p:5150 -s:IP -n:10 -o 
  20. 大家最可能见到的是客户端进行了10 次send 调用,而服务器在一次或两次recv 调用中,就读 
  21. 取了10 条消息。 
  22.  
  23.  
  24. */  
  25.   
  26.   
  27.   
  28. //程序清单6-2 回应客户端代码  
  29.   
  30.   
  31. // 说明:回显客户端,连接TCP 服务器,发送数据,并且读服务器返回的数据  
  32. // 编译命令:cl -o Client Client.c ws2_32.lib  
  33. //  
  34. // 命令行参数:  
  35. // client [-p:x] [-s:IP] [-n:x] [-o]  
  36. // -p:x 发送的远程端口  
  37. // -s:IP 服务器IP 地址或主机名  
  38. // -n:x 发送消息次数  
  39. // -o 只发送消息,不接收  
  40. //  
  41.   
  42. #include <winsock2.h>  
  43. #include <stdio.h>  
  44. #include <stdlib.h>  
  45.   
  46. #pragma  comment(lib, "Ws2_32.lib ")  
  47.   
  48. #define DEFAULT_COUNT 10  
  49.   
  50. #define DEFAULT_PORT 5150  
  51.   
  52. #define DEFAULT_BUFFER 2048  
  53.   
  54. #define DEFAULT_MESSAGE "This is a test of the emergency \  
  55. broadcasting system"  
  56.   
  57.   
  58. char szServer[128], // 连接的服务器  
  59.   
  60. szMessage[1024]; // 发送给服务器的消息  
  61.   
  62. int iPort = DEFAULT_PORT; // 连接到服务器的端口  
  63.   
  64. DWORD dwCount = DEFAULT_COUNT; // 发送消息次数  
  65.   
  66. BOOL bSendOnly = FALSE; // 只发送数据,不接收  
  67.   
  68.   
  69.  void usage() ;  
  70.  void ValidateArgs(int argc, char **argv) ;  
  71.   
  72. // 函数:main  
  73. // 说明:执行主线程,初始化Winsock,解释命令行参数,创建套接字,连接服务器,然后发送  
  74. //和接  
  75. // 收数据  
  76. int main(int argc, char **argv)  
  77. {  
  78.     WSADATA wsd;  
  79.     SOCKET sClient;  
  80.     char szBuffer[DEFAULT_BUFFER];  
  81.     int ret,i;  
  82.   
  83.     struct sockaddr_in server;  
  84.   
  85.     struct hostent *host = NULL;  
  86.   
  87.     // 解释命令行并且载入Winsock  
  88.     ValidateArgs(argc, argv);  
  89.   
  90.     if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)  
  91.     {  
  92.         printf("Failed to load Winsock library!\n");  
  93.         return 1;  
  94.     }  
  95.     strcpy(szMessage, DEFAULT_MESSAGE);  
  96.     // 创建套接字,并且尝试连接服务器  
  97.     sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  98.     if (sClient == INVALID_SOCKET)  
  99.     {  
  100.         printf("socket() failed: %d\n", WSAGetLastError());  
  101.         return 1;  
  102.     }  
  103.     server.sin_family = AF_INET;  
  104.     server.sin_port = htons(iPort);  
  105.     server.sin_addr.s_addr = inet_addr(szServer);  
  106.       
  107.         // 如果提供的服务器地址不是形如"aaa.bbb.ccc.ddd",则为主机名,尝试解析它  
  108.         if (server.sin_addr.s_addr == INADDR_NONE)  
  109.         {  
  110.             host = gethostbyname(szServer);  
  111.             if (host == NULL)  
  112.             {  
  113.                 printf("Unable to resolve server: %s\n", szServer);  
  114.                 return 1;  
  115.             }  
  116.   
  117.             CopyMemory(&server.sin_addr, host->h_addr_list[0],  
  118.                 host->h_length);  
  119.         }  
  120.         if (connect(sClient, (struct sockaddr *)&server,sizeof(server)) == SOCKET_ERROR)  
  121.         {  
  122.             printf("connect() failed: %d\n", WSAGetLastError());  
  123.             return 1;  
  124.         }  
  125.   
  126.         // 发送和接收数据  
  127.         for(i = 0; i < dwCount; i++)  
  128.         {  
  129.             ret = send(sClient, szMessage, strlen(szMessage), 0);  
  130.             if (ret == 0)  
  131.                 break;  
  132.             else if (ret == SOCKET_ERROR)  
  133.             {  
  134.                 printf("send() failed: %d\n", WSAGetLastError());  
  135.                 break;  
  136.             }  
  137.             printf("Send %d bytes\n", ret);  
  138.             if (!bSendOnly)  
  139.             {  
  140.                 ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);  
  141.                 if (ret == 0) // Graceful close  
  142.                     break;  
  143.                 else if (ret == SOCKET_ERROR)  
  144.                 {  
  145.                     printf("recv() failed: %d\n", WSAGetLastError());  
  146.                     break;  
  147.                 }  
  148.                 szBuffer[ret] = '\0';  
  149.                 printf("RECV [%d bytes]: '%s'\n", ret, szBuffer);  
  150.             }  
  151.         }  
  152.         closesocket(sClient);  
  153.         WSACleanup();  
  154.         return 0;  
  155. }  
  156.   
  157.   
  158.   
  159.   
  160.  void usage()  
  161. {  
  162.     printf("usage: client [-p:x] [-s:IP] [-n:x] [-o]\n\n");  
  163.     printf(" -p:x Remote port to send to\n");  
  164.     printf(" -s:IP Server's IP address or hostname\n");  
  165.     printf(" -n:x Number of times to send message\n");  
  166.     printf(" -o Send messages only; don't receive\n");  
  167.     ExitProcess(1);  
  168. }  
  169.   
  170.   
  171. // 函数:ValidateArgs  
  172. // 说明:解释命令行参数,设置全局变量  
  173. void ValidateArgs(int argc, char **argv)  
  174. {  
  175.     int i;  
  176.     for(i = 1; i < argc; i++)  
  177.     {  
  178.         if ((argv[i][0] == '-') || (argv[i][0] == '/'))  
  179.         {  
  180.             switch (tolower(argv[i][1]))  
  181.             {  
  182.             case 'p'// Remote port  
  183.                 if (strlen(argv[i]) > 3)  
  184.                     iPort = atoi(&argv[i][3]);  
  185.                 break;  
  186.                   
  187.             case 's'// Server  
  188.                 if (strlen(argv[i]) > 3)  
  189.                     strcpy(szServer, &argv[i][3]);  
  190.                 break;  
  191.             case 'n'// Number of times to send message  
  192.                 if (strlen(argv[i]) > 3)  
  193.                     dwCount = atol(&argv[i][3]);  
  194.                 break;  
  195.             case 'o'// Only send message; don't receive  
  196.                 bSendOnly = TRUE;  
  197.                 break;  
  198.             default:  
  199.                 usage();  
  200.                 break;  
  201.             }  
  202.         }  
  203.     }  
  204. }