套接字的Select模型,能够使Windows Sockets应用程序同时对多个套接字进行管理。调用select()函数检查当前各个套接字的状态,并且根据套接字函数的返回值判断套接字的可读可写性。然后调用相应的windows sockets API,完成数据的发送、接收。
select函数:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
<span style="white-space:pre"> </span>//int maxfdp windows平台下没用,可忽略
<span style="white-space:pre"> </span>//fd_set *readfds 可读性套接字集合指针
<span style="white-space:pre"> </span>//fd_set *writefds可写性套接字集合指针
<span style="white-space:pre"> </span>//fd_set *errorfds检查错误套接字集合指针
<span style="white-space:pre"> </span>//timeval *timeout select函数的等待时间
1.fd_set
fd_set是一个管理多个套接字的结构体。在改结构体中,fd_count字段指明套接字的数量,fd_array字段保存fd_count个套接字。fd_set最多可以管理64个套接字。在程序中使用该结构表示一系列特定套接字的集合。例如,准备接收数据的套接字集合,称为可读性集合。准备发送数据的套接字集合,称为可写性集合。fd_set结构声明如下:
#define FD_SETSIZE 64
typedef struct fd_set {
u_int fd_count;
Socket fd_array[FD_SETSIZE];
} fd_set;
2.timeval
timeval结构体用于定义select()函数的等待时间。tv_sec 字段指定等待秒数,tv_usec字段指定等待毫秒数。
3.宏
为了方便开发者的使用,windows sockets提供了下列宏,可用来针对I/O活动,对fd_set结构进行处理与检查。
FD_CLR( s , * set ) ; 从set集合中删除s套接字;
FD_ISSET( s , * set ) ; 检查s是否为set集合的一名成员。如果s是set集合的一名成员则返回TRUE;
FD_SET( s , * set ) ;将套接字s加入set集合;
FD_ZERO( * set ) ;将set集合初始化为空集合。
select同时处理两个可读套接字代码:
void CALLBACK EXPORT RT_Recv(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
struct sockaddr_in Comsockaddr;
int Number,sa;
char buffer[50];
memset(buffer,0,50);
memset(&Comsockaddr,0, sizeof(struct sockaddr_in));
sa=sizeof(struct sockaddr_in);
fd_set read;
timeval TimeOut;
TimeOut.tv_sec =1;
TimeOut.tv_usec = 0; //select()函数的等待时间
while(1)
{
FD_ZERO(&read);
FD_SET(recv_socket1, &read);//可读套接字1
FD_SET(recv_socket2, &read);//可读套接字2
if(select(1, &read, NULL, NULL, &TimeOut)!=1) return;//无信号则返回,上层函数会再一次调用RT_Recv函数
if (FD_ISSET(recv_socket1,&read))
{
Number = recvfrom(recv_socket1,buffer,sizeof(buffer),0,(struct sockaddr FAR*)&Comsockaddr,&sa);
if (Number>0)
{
AfxMessageBox("recv_socket1 receive data !");
}
}
if (FD_ISSET(recv_socket2,&read))
{
Number = recvfrom(recv_socket2,buffer,sizeof(buffer),0,(struct sockaddr FAR*)&Comsockaddr,&sa);
if (Number>0)
{
AfxMessageBox("recv_socket2 receive data !");
}
}
}
return; //上次函数会再一次调用RT_Recv函数
}