Windows操作系统I/O模型—笔记1(select (选择模型))

来源:互联网 发布:数据中心网络设计方案 编辑:程序博客网 时间:2024/06/05 19:24


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)数据可供读取。

用 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;
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 函数
                { 
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(); //释放套接字库资源
原创粉丝点击