winsock select I/O模型初探

    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;
}

  




posted on 2013-05-22 00:13  三横一竖  阅读(243)  评论(0编辑  收藏  举报