Chinaunix首页 | 论坛 | 博客
  • 博客访问: 46977
  • 博文数量: 10
  • 博客积分: 346
  • 博客等级: 入伍新兵
  • 技术积分: 100
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-08 12:37
文章分类
文章存档

2014年(1)

2010年(9)

分类: LINUX

2014-02-18 09:56:42

原文地址:unix 网络编程 select 讲解 作者:borgfish

最近听公司的一个分享,讲到socket通信,听到select一词,不是很明白,花了一下午的时间学习了一下,现在总结如下:

1.  首先要将用于监听的socket设置为非阻塞的端口,这里我们就会用到setsockopt()函数:
     int setsockopt(int s, int level, int optname, void* optval, socklen_t* optlen);
     这里我们要涉及到一个结构:
     struct timeval
     {
             time_t tv_sec;
             time_t tv_usec;
     };
     这里第一个域的单位为秒,第二个域的单位为微秒。
     struct timeval tv_out;
     tv_out.tv_sec = 1;
     tv_out.tv_usec = 0;
     填充这个结构后,我们就可以以如下的方式调用这个函数:
     setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));(具体参数可以man一下,或查看MSDN)
     这样我们就设定了recv()函数的超时机制,当超过tv_out设定的时间而没有数据到来时recv()就会返回0值。

   2. 我们要介绍的是多路复用机制,也就是同时监听多个套接字连接。

select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 
每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他 
文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 
当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执 
行了select()的进程哪一Socket或文件可读。


有时,select()也被用来当作延时函数使用。sleep()延时会释放cpu,用select的话,可以在占用cpu的情况下,延时


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


参数:


nfds    


需要检查的文件描述字个数(即检查到fd_set 的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset, writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查 fd_set的所有1024位。


readset   


     用来检查可读性的一组文件描述字。


writeset


     用来检查可写性的一组文件描述字。


exceptset


     用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)


timeout


有三种可能:


1.        timeout="NULL"(阻塞:直到有一个fd位被置为1函数才返回)


2.        timeout所指向的结构设为非零时间(等待固定时间:有一个fd位被置为1或者时间耗尽,函数均返回)


3.        timeout所指向的结构,时间设为0(非阻塞:函数检查完每个fd后立即返回)


select函数作用:在timeout时间内,不断测试不超过nfds 的所有fd,对于每一个接受到外部事件的fd, 将其在fd_set中的位置置1, 其余的没有接收到外部条件的fd位置置0,通外接下来的FD_ISSET进行测试,找到满足条件的所有fd。所以每次在调用select函数之前,要重新对fd_set进行赋值。


select函数返回值:     

如果在timeout时间内,有fd满足条件,返回对应位仍然为1的fd的总数。

如果timeout时间用完了,也没有fd接收到外部事件,则返回0

出错的情况返回负数。


四个宏来操作: 完全一点 从accept开始.

    fd_set set;

    FD_ZERO(&set);       /* 将set清零使集合中不含任何fd*/

    FD_SET(fd, &set);    /* 将fd加入set集合 */

    FD_CLR(fd, &set);    /* 将fd从set集合中清除 */

    FD_ISSET(fd, &set); /* 测试fd是否在set集合中*/

过去,一个fd_set通常只能包含<32的fd(文件描述 字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件中定义常量 FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。根据fd_set的位矢量实 现,我们可以重新理解操作fd_set的四个宏:

    fd_set set;

FD_ZERO(&set);      /*将set的所有位置0,如set在内存中占8位则将set置为

00000000*/


FD_SET(0, &set);    /* 将set的第0位置1,如set原来是00000000,则现在变为10000000,这样fd==1的文件描述字就被加进set中了 */


FD_CLR(4, &set);    /*将set的第4位置0,如set原来是10001000,则现在变为10000000,这样fd==4的文件描述字就被从set中清除了 */



FD_ISSET(5, &set); /* 测试set的第5位是否为1,如果set原来是10000100,则返回非零,表明fd==5的文件描述字在set中;否则返回0*/

阅读(919) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~