I/O复用之Select

select主要通过维护一个套接字队列,来完成单线程的IO复用,利用以下几个函数实现

NAME
       select,  pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous
       I/O multiplexing

函数声明

SYNOPSIS
       /* According to POSIX.1-2001 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds, 
                   fd_set *exceptfds, struct timeval *timeout);

       int pselect(int nfds, fd_set *readfds, fd_set *writefds, 
                       fd_set *exceptfds, const struct timespec *timeout,
                        const sigset_t *sigmask);
       //用来删除描述词组set中指定描述fd 的位置为0   
       void FD_CLR(int fd, fd_set *set); 

       //用来测试描述词组set中相关fd 的位是否为真
       int  FD_ISSET(int fd, fd_set *set);

       //向描述词组set中添加描述符,设置描述词组set中相关fd的位
       void FD_SET(int fd, fd_set *set);

       // 用来清除描述词组set的全部位。
       void FD_ZERO(fd_set *set);  

nfds:监视对象文件描述符数量。

readfds: 将所有关注“是否存在待读取数据”的文件描述符注册到fd_set变量,并传递其地址值。

writefds: 将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set变量,并传递其地址值。

exceptfds: 将所有关注“是否发生异常”的文件描述符注册到fd_set变量,并传递其地址值。

timeout: 调用select后,为防止陷入无限阻塞状态,传递超时信息。

在select函数中将超时参数 timeout设置为0的时候,表示不等待,检查描述符后立即返回,这种立即返回被称为轮询,即非阻塞式IO.

timeout为空表示是阻塞式IO,直到有描述符准备好才返回.或者指定一个时间段再返回.

返回0表示超时,出错返回-1,有就绪运算符返回数目

fd_set

select的描述符个数受限制可以通过修改FD_SETSIZE来改变描述符集的大小,每次修改之后需要配对内核进行重新编译,一般最大为1024

select记录描述符的数据结构是描述符集,实际上就是一个整数数组,假设32位整数,每一位代表一个描述符.这样的结构相当于一个bitmap.

typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

由于描述符是按位进行操作的,直接操作描述符集较为复杂,因此通过宏定义FD_SET、FD_CLR、FD_ZERO、FD_ISSET来设置描述符集。

举例

fd_set fd_sock;//套接字描述符的集合
SOCKADDR_IN addSrv;
addSrv.sin_family=AF_INET;
addSrv.sin_port=htons(this->port);
addSrv.sin_addr.Sun.S_addr=htonl(INADDR_ANY);
sockSrv=WSASocket(AF_INET,SOCK_STREAM,0);//
if(-1==bind(sockSrv,(SOCKADDR*)addrSrv,sizeof(addrSrv)))
{
    cout<<"绑定失败"<<endl;
    closesocket(sockSrv);
	 WSACleanup();
}
if(-1==listen(sockSrv,5))
{
    cout<<"监听失败"<<endl;
}
else{
   cout<<"等待客户端连接"<<endl;
}

while(true)
{
    // 初始化fdsock,将监听套接字加入到套接字集合
    FD_ZERO(&fd_sock);
    //监听套接字加入描述符集合
    
FD_SET(sockSrv, fd_sock);
    if(select(0,&fd_sock,NULL,NULL,NULL)>0)//返回的是变化套接字的个数
    {
    	for(int i=0;i<fd_sock.fd_count;i++)
     	{
           	if(FD_ISSET(fd_sock.fd_array[i],&fd_sock))
            	{
    			//监听套接字活跃
                   	if(fd_sock.fd_array[i]==sockSrv)
                    	{
                             newsock=accept(sockSrv,(SOCKADDR*)&clientAddr,&len);
                             if(newsock==INVALID_SOCKET)
                             {
                                 printf("服务器端:accept函数调用失败\n");
				                 for(int j = 0;j < m_dlg->fdsock.fd_count;j++)
				                 {
						             closesocket(m_dlg->fdsock.fd_array[j]);
					             }
								
					             WSACleanup();	                                  
                             }              
                     	}
                      	else//其他套接字 表示客户端发来数据
                       	{
                        		int size = recv(fdsock.fd_array[i], recv_Buff, sizeof(recv_Buff), 0);
                            	if(size < 0)
					            {
						            printf("服务器端:接收信息失败\n");
					            }
					            else if(size == 0)
					            {
						            printf("服务器端:客户端对方已关闭\n");
					            }
					            else	
					            {    
     					            send(fdsock.fd_array[i],send_Buf,sizeof(send_Buf),0);
    					            break;
         				        }
             			        // 关闭套接字
					            closesocket(m_dlg->fdsock.fd_array[i]);
     				            //清除已关闭套接字   
					            FD_CLR(m_dlg->fdsock.fd_array[i], &(m_dlg->fdsock));                     
                        }           
             	}//有活跃套接字  
      	}//遍历fd_set队列
    }//select活跃
}//while

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值