socket I/O模型 之 select

来源:互联网 发布:免费cms系统哪个好 编辑:程序博客网 时间:2024/06/07 04:48

选择(select)模型是Winsock中最常见的 I/O模型。核心便是利用 select 函数,实现对 I/O的管理!利用 select 函数来判断某Socket上是否有数据可读,或者能否向一个套接字写入数据,防止程序在Socket处于阻塞模式中时,在一次 I/O 调用(如send或recv、accept等)过程中,被迫进入“锁定”状态;同时防止在套接字处于非阻塞模式中时,产生WSAEWOULDBLOCK错误。

select函数原型:

int  select(
   
int  nfds,   //传入参数,忽略
   fd_set FAR *  readfds,     //检查可读性
   fd_set FAR *  writefds,   //检查可写性
   fd_set FAR * exceptfds,     //例外数据
    const  struct  timeval FAR*  timeout //本次select调用最长的等待时间
);



函数返回值,select()函数调用后,返回处于就绪状态并且已经包含在fd_set结构中的套接口描述符,也就是说,它要修改集合,删除那些不能进行指定操作的套接口。但如果超时则返回0;如果发生错误,则返回SOCKET_ERROR,应用程序可通过WSAGetLastError()获取错误代码。
其中fd_set是一个结构类型说明符,代表着一系列特定套接口的集合,它的定义如下:

typedef  struct  fd_set{
        u_int fd_count
;               /* how many are SET? */
        SOCKET  fd_array
[ FD_SETSIZE];   /* anarray of SOCKETs */
}  fd_set;



timeval是一个结构类型,它的定义如下:

struct  timeval{
        
long     tv_sec;         /* seconds */
        
long     tv_usec;         /* and microseconds */
};



若将超时值设置为(0 , 0),表明 select 会立即返回,出于对性能方面的考虑,应避免这样的设置。

以下为测试select()函数的程序,一个服务器端两个客户端

 

下面是服务器端程序:

#define  FD_SETSIZE500
#include  < WINSOCK2.H>
#pragma  comment( lib,"ws2_32")
#include  < stdio.h>
int  main()
{
    printf
("服务器端程序....\n");
   
//------①加载----------
   WSADATA wsaData;
   
if  ( WSAStartup( MAKEWORD(2,2),& wsaData)!=0)
   
{
        printf
("WSAStartupFailed,Error=【%d】\n", WSAGetLastError());
        
return  1;
   
}
   
else
        printf
("①加载成功\n");
   
//-------②创建流式套接字------------
   SOCKET s= socket( AF_INET, SOCK_STREAM,0);
   
if  ( s== INVALID_SOCKET)
   
{
        printf
("socket()Failed,Error=【%d】\n", WSAGetLastError());
        
return  1;
   
}
   
else
        printf
("②已创建监听套接口:【%d】\n", s);
   
//将套接口s置于”非阻塞模式“
   u_long u1=1;
    ioctlsocket
( s, FIONBIO,( u_long*)& u1);
   
//-----------③绑定本地地址---------------------
    struct  sockaddr_in Sadd;
    Sadd.sin_family
= AF_INET;
    Sadd.sin_port
= htons(5555);
    Sadd.sin_addr.S_un.S_addr
= inet_addr("192.168.31.1");
   
if  ( bind( s,( sockaddr*)& Sadd,sizeof( Sadd))== SOCKET_ERROR)
   
{
        printf
("bind()Failed,Error=【%d】\n", WSAGetLastError());
        
return  1;
   
}
   
else
        printf
("③绑定成功,本地IP地址:【%s】,端口号:【%d】\n", inet_ntoa( Sadd.sin_addr), ntohs( Sadd.sin_port));
   
//--------------④进入监听状态-----------------
    if  ( listen( s,3)== SOCKET_ERROR)
   
{
        printf
("listenFailed,Error=【%d】\n", WSAGetLastError());
        
return  1;
   
}
   
else
        printf
("④进入监听状态\n");
   
//--------------⑤select-------------------
    //准备工作
    int  x=1;
    timeval tv
;
    tv.tv_sec
=20;
    tv.tv_usec
=0;
    fd_set socket_jh01
;
    FD_ZERO
(& socket_jh01);
    FD_SET
( s,& socket_jh01);
    fd_set socket_jh02
;
    FD_ZERO
(& socket_jh02);
   
while  (TRUE)
   
{
        socket_jh02
= socket_jh01;
        
int  sock_sum= select(0,& socket_jh02,NULL,NULL,& tv);
        
//------情况一  成功
       if  ( sock_sum>0)
        
{
            
for  (int  i=0;i<(int) socket_jh02.fd_count; i++)
            
{
               
if  ( socket_jh02.fd_array[ i]== s)
               
{
                   
if  ( socket_jh01.fd_count< FD_SETSIZE)
                   
{
                       sockaddr_in Cadd
;
                       
int  Cadd_len=sizeof( Cadd);
                       SOCKET sNew
= accept( s,( sockaddr*)& Cadd,& Cadd_len);
                       FD_SET
( sNew,& socket_jh01);
                       printf
("接受一个客户端连接,对方地址:【%s】,端口号:【%d】\n", inet_ntoa( Cadd.sin_addr), ntohs( Cadd.sin_port));
                       printf
("分配给该客户端的套接口为:%d\n\n", sNew);
                   
}
                   
else
                   
{
                       printf
("连接数量太多\n");
                       
continue;
                   
}
               
}
               
else
               
{
                   
char  cbuf[256];
                   memset
( cbuf,0,256);
                   
int  cRecv;
                   cRecv
= recv( socket_jh02.fd_array[ i], cbuf,256,0);
                   
if  ( cRecv== SOCKET_ERROR)
                   
{
                       printf
("可能客户端%d非法关闭!!", socket_jh02.fd_array[ i]);
                       printf
("或者调用recv() Failed,Error=【%d】\n", WSAGetLastError());
                       closesocket
( socket_jh02.fd_array[ i]);
                       FD_CLR
(socket_jh02.fd_array[ i],& socket_jh01);
                   
}
                   
else  if  ( cRecv>0)
                   
{
                       printf
("接收到来至【%d】的数据:%s\n", socket_jh02.fd_array[ i], cbuf);
                       
int  isend;
                       
char  Sbuf[]="Hello client!Iam server";
                       isend
= send( socket_jh02.fd_array[ i], Sbuf,sizeof( Sbuf),0);
                       
if  ( isend== SOCKET_ERROR)
                       
{
                           printf
("send()Failed,Error=【%d】\n", WSAGetLastError());
                          
break;
                       
}
                       
else  if  ( isend<=0)
                       
{
                           printf
("消息发送失败!!\n");
                          
break;
                       
}
                       
else
                           printf
("给客户【%d】信息已发送,信息长度%d字节\n\n", socket_jh02.fd_array[ i], isend);
                   
}
                   
else
                   
{
                       printf
("客户端【%d】不再发送数据,正常关闭连接,为客户端连接创建的套接口将关闭!!\n", socket_jh02.fd_array[ i]);
                       closesocket
( socket_jh02.fd_array[ i]);
                       FD_CLR
(socket_jh02.fd_array[ i],& socket_jh01);
                   
}
               
}
            
}//end for
       }//end sock_sum
       //------------情况二  超时
       else  if  ( sock_sum==0)
        
{
            printf
("第【%d】次超时\n", x);
            
if  ( x<3)
            
{
                x
++;
               
continue;
            
}
            
else
            
{
                printf
("超过等待限制,退出程序\n");
               
break;
            
}
        
}
        
//--------------情况三  失败
       else
        
{
            printf
("select()Failed,Error=【%d】\n", WSAGetLastError());
            
break;
        
}
   
}//while end
   closesocket( s);
    printf
("退出");
    WSACleanup
();
   
return  0;

}

0 0