socket I/O模型 之 select

選擇(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_SETSIZE 500
#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;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章