windows对socket封装得有点异类,其典型的I/O模型有blocking,select,WSAAsyncSelect,WSAEventSelect,Overlapped,complete port,从本期开始逐步对各种I/O模型进行分析。
select 模型是继承了berkeley的接口,在微软的MSDN中描述如下:
The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.
int select(
int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
const struct timeval* timeout
);
Parameters
nfds
[in] Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
readfds
[in, out] Optional pointer to a set of sockets to be checked for readability.
writefds
[in, out] Optional pointer to a set of sockets to be checked for writability.
exceptfds
[in, out] Optional pointer to a set of sockets to be checked for errors.
timeout
[in] Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.
显然select模型是一种轮询的机制,在处理数据之前需要将感兴趣的socket添加到相应的集合中。它的优点是能够在一个线程中监听多个端口,由于引入了时间的参数,不会导致某个socket在没有事件到来时,长时间处于阻塞状态,但缺点也是明显的:一个是效率上的,轮询需要遍历socket集合,消耗大量的cpu时间,二是并发量上,监听的端口数量有限,winsock2默认是64个套接字,当然可以通过重定义宏FD_SIZE来修改最大监听数目,但是系统的性能会受到影响(还未测试,后续会补上)。综上可以看出select模型在服务器端用途非常有限,可能是当时为了解决阻塞问题临时提供的一套解决方案吧。
下面提供一套客户端调用select模型的代码:
client端代码:
1 #include "stdafx.h" 2 #include <winsock2.h> 3 #pragma comment(lib,"WS2_32.lib") 4 5 #define DEF_INVALID_SOCKET 0XFFFFFF 6 #define DEF_MAX_CONNECT 10 7 #define FALSE 0 8 #define TRUE 1 9 10 BOOL CreateContext() 11 { 12 WSADATA wsaData; 13 14 if (WSAStartup(MAKEWORD(2,1),&wsaData)) //call Windows Sockets DLL 15 { 16 printf("Winsock can't initiate,error code: %d !\n",WSAGetLastError ()); 17 WSACleanup(); 18 return FALSE; 19 } 20 21 return TRUE; 22 } 23 24 int main(int argc, char* argv[]) 25 { 26 printf("Hello World!\n"); 27 SOCKET lszSocket[DEF_MAX_CONNECT] = {0XFFFFFF}; 28 printf("my client program! \n"); 29 30 if(!CreateContext()) 31 { 32 return 0; 33 } 34 35 for(int i = 0;i < DEF_MAX_CONNECT;i++) 36 { 37 lszSocket[i] = socket(AF_INET,SOCK_STREAM,0); 38 if( 0 > lszSocket[i]) 39 { 40 printf("create socket error! The %d-th£¬error code: %d \n",i+1,WSAGetLastError ()); 41 return 1; 42 } 43 } 44 45 //bind socket 46 for( i = 0;i < DEF_MAX_CONNECT;i++) 47 { 48 sockaddr_in lstLocalAddr; 49 lstLocalAddr.sin_family = AF_INET; 50 lstLocalAddr.sin_port = htons(0); 51 lstLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); 52 if(SOCKET_ERROR == bind(lszSocket[i],(SOCKADDR*)&lstLocalAddr,sizeof(lstLocalAddr))) 53 { 54 printf("bind socket error! The %d-th£¬error code: %d \n",i+1,WSAGetLastError()); 55 56 return 1; 57 } 58 else 59 { 60 sockaddr_in lstServerAddr; 61 lstServerAddr.sin_family = AF_INET; 62 lstServerAddr.sin_port = htons(5700); 63 lstServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 64 if(0 > connect(lszSocket[i],(sockaddr*)&lstServerAddr,sizeof(lstServerAddr))) 65 { 66 printf("connect socket error!The %d-th£¬error code: %d \n",i+1,WSAGetLastError()); 67 } 68 } 69 } 70 71 int liRet = 0; 72 fd_set lstReadFd; 73 timeval lstTime; 74 lstTime.tv_sec = 0; 75 lstTime.tv_usec = 10; 76 while(1) 77 { 78 FD_ZERO(&lstReadFd); 79 for(int i = 0;i < DEF_MAX_CONNECT;i++) 80 { 81 FD_SET(lszSocket[i],&lstReadFd); 82 } 83 84 liRet = select(DEF_MAX_CONNECT +1,&lstReadFd,NULL,NULL,&lstTime); 85 switch(liRet) 86 { 87 case -1: 88 printf("select socket error! error code: %d \n",WSAGetLastError()); 89 return 1; 90 break; 91 case 0: 92 break; 93 default: 94 for( i = 0;i < DEF_MAX_CONNECT;i++) 95 { 96 if(FD_ISSET(lszSocket[i],&lstReadFd)) 97 { 98 char lszRecvBuff[512] = {0}; 99 int liRcvLen = recv(lszSocket[i],lszRecvBuff,512,0); 100 if(0 > liRcvLen) 101 { 102 printf(" %d socket,receive failed! \n",i+ 1); 103 } 104 printf("%d socket receive : %s \n", i+ 1, lszRecvBuff); 105 } 106 } 107 } 108 } 109 110 for( i = 0;i < DEF_MAX_CONNECT;i++) 111 { 112 closesocket(lszSocket[i]); 113 } 114 115 //free resource 116 WSACleanup(); 117 return 1; 118 }
服务端的代码相对简单,粘贴如下:
#include "stdafx.h" #include <winsock2.h> #pragma comment(lib,"WS2_32.lib") int main(int argc, char* argv[]) { printf("Hello World!\n"); WSADATA wsaData; if (WSAStartup(MAKEWORD(2,1),&wsaData)) { printf("Winsock can't initiate, error code: %d !\n",WSAGetLastError()); WSACleanup(); return 1; } SOCKET lServerSocket = socket(AF_INET,SOCK_STREAM,0); sockaddr_in lstAddr; lstAddr.sin_family = AF_INET; lstAddr.sin_port = htons(5700); lstAddr.sin_addr.s_addr = htonl(INADDR_ANY); if(0 > bind(lServerSocket,(sockaddr*) &lstAddr,sizeof(lstAddr))) { printf("bind socket error ,code : %d \n",WSAGetLastError()); } if( 0 != listen(lServerSocket,1000)) { printf("listen socket error ,code : %d \n",WSAGetLastError()); } while(1) { sockaddr_in lClientAddr; printf("Begin accept! \n"); char lszSndBuff[1024]; SOCKET lRecvSocket; int liLen = sizeof(lClientAddr); if(INVALID_SOCKET == (lRecvSocket = accept(lServerSocket,(sockaddr*)&lClientAddr,&liLen))) { printf("receive error : %d \n",WSAGetLastError()); } printf("client addr: %s ,port: %d \n",inet_ntoa(lClientAddr.sin_addr),ntohs(lClientAddr.sin_port)); sprintf(lszSndBuff,"hello, %d \n",ntohs(lClientAddr.sin_port)); int liSndLen = send(lRecvSocket,lszSndBuff,strlen(lszSndBuff),0); if(liSndLen <= 0) { printf("snd %d hello failed ! code: %d \n",ntohs(lClientAddr.sin_port),WSAGetLastError()); } Sleep(300); } return 0; }