现在的位置: 首页 > 综合 > 正文

TCP流式套接字的select模式编程

2013年11月16日 ⁄ 综合 ⁄ 共 6354字 ⁄ 字号 评论关闭

选择(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];    /* an array 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( "WSAStartup Failed,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( "listen Failed,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!I am 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 ;
}

两个客户端程序与上次测试的客户端一样,详见这里【TCP流式套接字的阻塞模式编程】http://blog.csdn.net/akof1314/archive/2010/05/13/5588265.aspx1

先启动服务器端,再启动1号客户端,接着启动2号客户端,就会看到如图所示:

抱歉!评论已关闭.