TCP流式套接字的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];    /* 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号客户端,就会看到如图所示:

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值