Socket I/O模型之选择(select)

来源:互联网 发布:linux卸载mysql 编辑:程序博客网 时间:2024/05/21 18:37
在windows平台构建网络应用,必须了解socket I/O模型。windows提供了选择(select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I /O(overlapped I/O)和完成端口(completion port)。

一、客户端代码
C++代码  收藏代码
  1. #include "stdafx.h"  
  2. #include <WINSOCK2.H>  
  3. #include <stdio.h>  
  4. #pragma comment(lib, "ws2_32.lib")  
  5. #define SERVER_ADDRESS  "192.168.10.56"  
  6. #define PORT  5150  
  7. #define MSGSIZE  1024  
  8. int main(int argc, char* argv[])  
  9. {  
  10.     WSADATA wsaData;  
  11.     SOCKET sClient;  
  12.     SOCKADDR_IN server;  
  13.     char szMessage[MSGSIZE];  
  14.     int ret;  
  15.     // Initialize windows socket library  
  16.     WSAStartup(0x0202, &wsaData);  
  17.     // Create client socket  
  18.     sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  19.     // Connect to server  
  20.     memset(&server, 0, sizeof(SOCKADDR_IN));  
  21.     server.sin_family = AF_INET;  
  22.     server.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDRESS);  
  23.     server.sin_port = htons(PORT);  
  24.     connect(sClient, (sockaddr*)&server, sizeof(SOCKADDR_IN));  
  25.     while (TRUE)   
  26.     {  
  27.         printf("Send:");  
  28.         gets(szMessage);  
  29.         // Send message  
  30.         send(sClient, szMessage, strlen(szMessage), 0);  
  31.         // Receive message  
  32.         ret = recv(sClient, szMessage, MSGSIZE, 0);  
  33.         szMessage[ret] = '\0';  
  34.         printf("Received [%d bytes]: '%s'\n", ret, szMessage);  
  35.     }  
  36.     // Clean up  
  37.     closesocket(sClient);  
  38.     WSACleanup();  
  39.     return 0;  
  40. }  

这是一个最简单的客户端代码,负责发送数据,然后接受返回。

二、使用select模型的服务器
C++代码  收藏代码
  1. // write by larry  
  2. // 2009-8-20  
  3. // This is server using select model.  
  4. #include "stdafx.h"  
  5. #include <winsock.h>  
  6. #include <stdio.h>  
  7. #define PORT  5150  
  8. #define MSGSIZE  1024  
  9. #pragma comment(lib, "ws2_32.lib")  
  10. int g_iTotalConn = 0;  
  11. SOCKET g_CliSocketArr[FD_SETSIZE];  
  12. DWORD WINAPI WorkerThread(LPVOID lpParam);  
  13. int main(int argc, char* argv[])  
  14. {  
  15.     WSADATA wsaData;  
  16.     SOCKET sListen, sClient;  
  17.     SOCKADDR_IN local, client;  
  18.     int iAddrSize = sizeof(SOCKADDR_IN);  
  19.     DWORD dwThreadId;  
  20.     // Initialize windows socket library  
  21.     WSAStartup(0x0202, &wsaData);  
  22.     // Create listening socket  
  23.     sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  24.     // Bind  
  25.     local.sin_family = AF_INET;  
  26.     local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  27.     local.sin_port = htons(PORT);  
  28.     bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));  
  29.     // Listen  
  30.     listen(sListen, 3);  
  31.     // Create worker thread  
  32.     CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);  
  33.     while (TRUE)   
  34.     {  
  35.         // Accept a connection  
  36.         sClient = accept(sListen, (sockaddr*)&client, &iAddrSize);  
  37.         printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));  
  38.         // Add socket to g_CliSocketArr  
  39.         g_CliSocketArr[g_iTotalConn++] = sClient;  
  40.     }  
  41.     return 0;  
  42. }  
  43. DWORD WINAPI WorkerThread(LPVOID lpParam)  
  44. {  
  45.     int i;  
  46.     fd_set fdread;  
  47.     int ret;  
  48.     struct timeval tv = {1, 0};  
  49.     char szMessage[MSGSIZE];  
  50.     while (TRUE)   
  51.     {  
  52.         FD_ZERO(&fdread);  
  53.         for (i = 0; i < g_iTotalConn; i++)   
  54.         {  
  55.             FD_SET(g_CliSocketArr[i], &fdread);  
  56.         }  
  57.         // We only care read event  
  58.         ret = select(0, &fdread, NULL, NULL, &tv);  
  59.         if (ret == 0)   
  60.         {  
  61.             // Time expired  
  62.             continue;  
  63.         }  
  64.         for (i = 0; i < g_iTotalConn; i++)   
  65.         {  
  66.             if (FD_ISSET(g_CliSocketArr[i], &fdread))   
  67.             {  
  68.                 // A read event happened on g_CliSocketArr  
  69.                 ret = recv(g_CliSocketArr[i], szMessage, MSGSIZE, 0);  
  70.                 if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))   
  71.                 {  
  72.                     // Client socket closed  
  73.                     printf("Client socket %d closed.\n", g_CliSocketArr[i]);  
  74.                     closesocket(g_CliSocketArr[i]);  
  75.                     if (i < g_iTotalConn-1)   
  76.                     {  
  77.                         g_CliSocketArr[i--] = g_CliSocketArr[--g_iTotalConn];  
  78.                     }  
  79.                 }   
  80.                 else   
  81.                 {  
  82.                     // We reveived a message from client  
  83.                     szMessage[ret] = '\0';  
  84.                     send(g_CliSocketArr[i], szMessage, strlen(szMessage), 0);  
  85.                 }  
  86.             }  
  87.         }  
  88.     }  
  89. }  

这是异步模型中最简单的一种,服务器端的几个主要流程如下:

1.创建监听套接字,绑定,监听;
2.创建工作者线程;
3.创建一个套接字数组,用来存放当前所有活动的客户端套接字,每accept一个连接就更新一次数组;
4.接受客户端的连接。