select函数可在socket编程中使用,
是一种异步IO
选
择
模型,可以解决套接字(
listen,
bind,
accept,
recv这
几个函数会死锁
)死锁,
以下笔记仅为个人学习中的记录,有错请提出,3Q
█ select 的函数原型如下:
int
select
(
(
__in
int nfds, //忽略,保持与Berkeley套接字兼容
__in_out
fd_set*
readfds, //检查可读性,是一个指向fd_set结构体的指针
__in_out
fd_set*
writefds, //检查可写性,是一个指向fd_set结构体的指针
__in_out
fd_set*
exceptfds, //例外数据,是一个指向fd_set结构体的指针
__in const struct timeval* timeout, //时间结构体指针
);
在三个参数中(
readfds 、
writefds 和
exceptfds ),任何两个都可以是空值( NULL);
但是,至少有一个不能为空值!在任何不为空的集合中,必须包含至少一个套接字句柄;
否则, select 函数便没有任何东西可以等待。最后一个参数 timeout 对应的是一个指针,它指向一个timeval 结构,
用于决定select 最多等待 I/O操作完成多久的时间。如 timeout 是一个空指针,那么 select 调用会无限
期地“锁定”或停顿下去,直到至少有一个描述符符合指定的条件后结束。
select 成功完成后,会在 fdset 结构中,返回刚好有未完成的 I/O操作的所有套接字句柄的总量。
若超过 timeval 设定的时间,便会返回0。若 select 调用失败,都会返回 SOCKET_ERROR,
fd_set 结构的定义如下:
typedef struct
fd_set
{
{
u_int
fd_count;
SOCKET
fd_array[
FD_SETSIZE];//#define FD_SETSIZE 64 , fd_set 结构中最多只能监视64个套接字。
}
fd_set;
fd_set 结构中最多只能监视64个套接字。
fd_set代表着一系列特定套接字的集合。其中,
readfds 集合包括符合下述任何一个条件的套接字:
● 有数据可以读入。
● 连接已经关闭、重设或中止。
● 假如已调用了
listen,而且一个连接正在建立,那么
accept函数调用会成功。
writefds 集合包括符合下述任何一个条件的套接字:
● 有数据可以发出。
● 如果已完成了对一个非锁定连接调用的处理,连接就会成功。
exceptfds 集合包括符合下述任何一个条件的套接字:
● 假如已完成了对一个非锁定连接调用的处理,连接尝试就会失败。
● 有带外(Out-of-band,OOB)数据可供读取。
一下是部分核心源代码:
}
套接字编写(基于TCP协议)
用
select 对套接字进行监视之前,必须将套接字句柄分配给一个
fdset的结构集合,
之后再来调用 select,便可知道一个套接字上是否正在发生上述的 I/O 活动。
Winsock 提供了下列宏操作,可用来针对 I/O活动,对 fdset 进行处理与检查:
●
FD_CLR(s, *set):从set中删除套接字s。
●
FD_ISSET(s, *set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
●
FD_SET(s, *set):将套接字s加入集合set。
●
FD_ZERO( * set):将set初始化成空集合。
可完成用 select 操作一个或多个套接字句柄的全过程:
1) 使用FDZERO宏,初始化一个fdset对象;
2) 使用FDSET宏,将套接字句柄加入到fdset集合中;
3) 调用 select 函数,等待其返回……select 完成后,会返回在所有 fdset 集合中设置的套接字句柄总数,
并对每个集合进行相应的更新。
4) 根据 select的返回值和 FDISSET宏,对 fdset 集合进行检查。
5) 知道了每个集合中“待决”的 I/O操作之后,对 I/O进行处理,
然后返回步骤1 ),继续进行 select 处理。
一下是部分核心源代码:
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut, BOOL bRead)
{ fd_set fdset;
{ fd_set fdset;
timeval tv;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset);
nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
tv.tv_sec = 0;
tv.tv_usec = nTimeOut;
int iRet = 0;
if ( bRead ) {
iRet = select(0, &fdset, NULL , NULL, &tv);
}else{
iRet = select(0, NULL , &fdset, NULL, &tv);
}
if(iRet <= 0) {
return FALSE;
} else if (FD_ISSET(hSocket, &fdset)){
return TRUE;
}
return FALSE;
}
套接字编写(基于TCP协议)
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );//加载套接字库
if ( err != 0 ) //加载失败
{
return 0;
}
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1)//加载套接字的版本不符合
{
WSACleanup( );//释放加载资源
return 0;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字
SOCKADDR_IN addrSrv;
addrSrv.sin_addr .S_un .S_addr =htonl(INADDR_ANY);
addrSrv.sin_family =AF_INET;
addrSrv.sin_port =htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//绑定套接字
listen(sockSrv,5);//监听套接字
SOCKADDR_IN addrCilent;
int len=sizeof(SOCKADDR);
while(1)
{
if( SOCKET_Select) //调用SOCKET_Select 函数
{
if( SOCKET_Select) //调用SOCKET_Select 函数
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrCilent,&len);//接受客服端
char sendBuf[100];
sprintf(sendBuf,"Welcome %s to
http://www.Alan.com",inet_ntoa(addrCilent.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf)+1,0);//发送数据
char recvBuf[100];
recv(sockConn,recvBuf,100,0);//接受数据
printf("%s\n",recvBuf);
}
}
closesocket(sockConn);//关闭套接字
}
WSACleanup();
//释放套接字库资源