您的位置:首页 > 其它

winsock中select模型实战

2011-09-21 10:35 295 查看
选择(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 ;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: