:select是一个计算机函数,位于头文件#include
应用:
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
int select (int maxfd + 1,fd_set *readset,fd_set *writeset,
fd_set *exceptset,const struct timeval * timeout);
参数1:文件描述符的数量,因为文件描述符从0开始,所以此处的值为最大的文件描述符加1。
参数2(fd_set *readset):struct fd_set指针,可读文件描述符集合,用于检查可读性
参数3(fd_set *writeset):struct fd_set指针,可写文件描述符集合,用于检查可写性
参数4(fd_set *exceptset):struct fd_set指针,异常文件描述符集合,用于检查异常
参数5(struct timeval * timeout):select函数超时时间
一个指向timeval结构的指针,用于决定select等待I/o的最长时间
struct fd_set可以理解为一个集合,这个集合存放文件描述符,fd_set可通过系统宏函数来进行处理
操作fd_set的系统宏函数:
FD_ZERO(fd_set *fdset) //清空fdset集合中所有的文件描述符
FD_SET(int fd, fd_set *fdset) //将文件描述符fd加入fdset集合中
FD_CLR(int fd, fd_set *fdset) //将文件描述符fd从fdset集合中清除
FD_ISSET(int fd, fdset *fdset) //检查fdset集合中的fd是否可读写,>0表示可读写。
struct timeval{
long tv_sec; // 秒
long tv_usec; // 微妙
}
select返回值:
如果有文件描述符的读写异常变化,select会返回一个大于0的值。
如果没有则在timeout的时间后select返回0,若发生错误返回负值。
可以传入NULL值,表示不关心任何文件的读/写/异常变化。
select()三个fd_set集合注意事项:
1.三个fd_set分别监视文件描述符的读写异常变化,当传入NULL值时,代表不关心当前装填类型的变化。如果三个指针都为NULL,我们就有了一个比sleep()函数更为精确的定时器(sleep()以毫秒为最小单位,这个以微秒为单位)。
select()参数5超时时间注意事项:
1.若将此参数置为NULL,就是将select置为阻塞状态,一直等到监视文件描述符集合中某个文件描述符发生变化为止
2.如将此参数设置为0秒0微秒,就变成一个非阻塞函数,不管文件描述符是否变化,都立即返回(有变化返回0,无变化返回正值)
3.若超时时间大于0,在超时时间到达之前检测到了就立即返回,超时时间到了也没检测到也返回。(注意:如果在超时时间到之前返回了,此时select的超时时间为超时时间减去检测所用的时间,如果我们想要保持超时时间不变,就必须在轮询中进行每次重新设置超时时间)
1、获取文件的flags,即open函数的第二个参数:
flags = fcntl(fd,F_GETFL,0);
2、设置文件的flags:
fcntl(fd,F_SETFL,flags);
3、增加文件的某个flags,比如文件是阻塞的,想设置成非阻塞(可以使用这种方法将套接字设为非阻塞的):
flags = fcntl(fd,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(fd,F_SETFL,flags);
4、取消文件的某个flags,比如文件是非阻塞的,想设置成为阻塞:
flags = fcntl(fd,F_GETFL,0);
flags &= ~O_NONBLOCK;
fcntl(fd,F_SETFL,flags);
SOL_SOCKET
-----------------------------------------------------
参数optname 宏的作用 对应参数optaval的类型
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSEADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
======================================================
IPPROTO_IP
------------------------------------------------------
IP_ADD_MEMBERSHIP 加入到组播组中 struct ip_mreq
IP_MULTICAST_IF 允许开启组播报文的接口 struct ip_mreq
IP_TOS 源于该套接字的每个IP包的Type-Of-Service(TOS 服务类型)字段。它被用来在网络上区分包的优先级> int
IP_MULTICAST_TTL 设置多播组数据的TTL值 int
--------------------------------------------------------
SOL_IP
----------------------------------------------------------
IP_PKTINFO 设置套接字传递pktinfo的辅助信息结构 int
-------------------------------------------------------------
例子:
option = 1;
ret = setsockopt(g_cw_ac_ipv4_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option)); //设置套接字端口复用
option = 1; //设置套接字传递pktinfo的辅助信息结构
ret = setsockopt(g_cw_ac_ipv4_socket, SOL_IP, IP_PKTINFO, (void *)&option, sizeof(option));
IP_PKTINFO:传递一条包含pktinfo结构(该结构提供一些来访包的相关信息)的IP_PKTINFO辅助信息。
这个选项只对数据报类的套接字有效。
struct in_pktinfo
{
unsigned int ipi_ifindex; /* 接口索引 */
struct in_addr ipi_spec_dst; /* 路由目的地址 */
struct in_addr ipi_addr; /* 头标识目的地址 */
};
ipi_ifindex指的是接收包的接口的唯一索引。ipi_spec_dst指的是路由表记录中的目的地址,而ipi_addr 指的是包头中的目的地址。如果给 sendmsg传递了IP_PKTINFO,那么外
发的包会通过在ipi_ifindex中指定的接口发送出去,同时把ipi_spec_dst设置为目的地址。
myip_setsockopt的代码实现中只是根据option_value是否为0,置或清inet->cmsg_flags的IP_CMSG_PKTINFO位。