前述:

linux下的I/O复用模型目前很多都已经不用select函数了,而是用epoll,但是为什么还需要了解select编程呢,其实是从两个方面考虑的:一是为了通过select去理解epoll,而是非阻塞的connect函数也会用到select函数。

函数原型:

SELECT(2)                  Linux Programmer's Manual                 SELECT(2)NAMEselect,  pselect,  FD_CLR,  FD_ISSET, FD_SET, FD_ZERO - synchronous I/OmultiplexingSYNOPSIS/* 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);void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);

相关的函数比较多,先来说说select函数的参数:

(1) timeout:超时时间

struct timeval {

long tv_sec; //秒

long tv_usec; //微妙

}

这个参数有三种可能

a) 永久等待下去,将该参数设置为空指针

b) 等待固定一段时间,指针不为空且设置的秒或者毫秒值不为0,如果超时,select函数会返回0

c) 根本不等待,指针不为空且设置的秒或者毫秒值均为0

(2) 中间的三个参数readfds、writefds、exceptfds指定我们要让内核检测读、写和异常条件的描述符,如果我们对某一个的条件不感兴趣,就可以把它设置空指针。

目前支持的异常条件只有两个(下面的异常条件可以暂时不用理解):

a) 某个套接字的外带数据的到达

b) 某个已置为分组模式的伪终端等信息

select使用描述符集,通常是一个整数数组,其中每个整数中的每一位对应一个描述符。所以fd_set类型本质是其实是一个数组类型。

举个例子,以下代码用于定义一个fd_set类型的变量,然后设置需要让select检测的描述符1、4、5:

fd_set rset;

FD_ZERO(&rset); //初始化set,位的值为0

FD_SET(1, &rest); //设置,位的值为1

FD_SET(4, &rest);

FD_SET(5, &rest);

(3) nfds参数是指定待检测的描述符的个数,它的值是待检测的最大描述符加1,举个例子,需要检测的描述符为1、4、5、11,那么nfds的值就为12。

select函数返回值:

返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1

我们使用FD_ISSET宏来检测fd_set数据类型中的描述符,描述符集内任何与为就绪对应的位返回均清成0。为此,每次重新调用select函数时,我们都得再次把所有的描述符集内所关心的位置为1.

描述符就绪条件:


这里要注意的是,即使select返回套接字可读,也需要使用getsockopt检查是否有错误发生,因为如图示,有待处理的错误也是返回可读就绪。

最后,可以通过一个支持connect超时的函数来理解下select函数,或者查阅《unix网络编程》·卷1·第6章:I/O复用:

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>  int connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t addrlen, int nsec)
{  int flags, n, error;  socklen_t len;  fd_set rset, wset;  struct timeval tval;  /* 调用fcntl把套接字设置为非阻塞 */  flags = fcntl(sockfd, F_GETFL, 0);  fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);  /* 发起非阻塞connect。期望的错误是EINPROGRESS,表示连接建立已经启动但是尚未完成  */  error = 0;  if ( (n = connect(sockfd, addr, addrlen)) < 0)  if (errno != EINPROGRESS)  return(-1);  /* 如果非阻塞connect返回0,那么连接已经建立。当服务器处于客户端所在主机时这种情况可能发生 */  if (n == 0)  goto done; /* connect completed immediately */  /* 调用select等待套接字变为可读或可写,如果select返回0,那么表示超时 */  FD_ZERO(&rset);  FD_SET(sockfd, &rset);  wset = rset;  tval.tv_sec = nsec;  tval.tv_usec = 0;  if ( (n = select(sockfd+1, &rset, &wset, NULL, nsec ? &tval : NULL)) == 0) {  close(sockfd); /* timeout */  errno = ETIMEDOUT;  return(-1);  }  /* 检查可读或可写条件,调用getsockopt取得套接字的待处理错误,如果建立成功,该值将为0 */  if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {  len = sizeof(error);  if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)  return(-1); /* Solaris pending error */  } else {  perror("select error: sockfd not set");  exit(1);  }  /* 恢复套接字的文件状态标志并返回 */
done:  fcntl(sockfd, F_SETFL, flags); /* restore file status flags */  if (error) {  close(sockfd); /* just in case */  errno = error;  return(-1);  }  return(0);
}  int main()
{  // socket    struct sockaddr_in servaddr;      short port = 9999;      int sockfd = socket(AF_INET, SOCK_STREAM, 0);      servaddr.sin_family = AF_INET;      servaddr.sin_addr.s_addr = inet_addr("113.107.231.211");      servaddr.sin_port = htons(port);  int timeout = 3;  if (connect_nonb(sockfd, (sockaddr *) &servaddr, sizeof(sockaddr_in), 2) < 0) {    perror("connect fail: ");  return(-1);  }  printf("connect success!\n");  return 0;
}  

参考:《unix网络编程》·卷1

TCP/IP编程之select函数详解相关推荐

  1. TCP/IP编程之accept函数详解

    accept函数由TCP服务器调用,用于从已完成连接队列返回下一个已完成连接.如果已完成连接队列为空,那么进程被投入睡眠(假定套接字默为默认的阻塞方式) 函数原型: ACCEPT(2) Linux P ...

  2. 网络套接字编程之IO模型详解

    网络套接字编程之IO模型详解 本文主要参考自<UNIX网络编程>(第1卷)(套接口API第3版) Unix下可用的五种I/O模型有: 阻塞式I/O 非阻塞式I/O I/O复用(select ...

  3. TCP/IP 协议栈及 OSI 参考模型详解

    TCP/IP 协议栈及 OSI 参考模型详解 转载地址:http://www.codeceo.com/article/tcp-ip-osi-model.html OSI参考模型 OSI RM:开放系统 ...

  4. Linux I/O复用之select函数详解

    http://blog.csdn.net/y396397735/article/details/55004775 select函数的功能和调用顺序 使用select函数时统一监视多个文件描述符的:  ...

  5. 并发编程应用场景_linux网络编程之select函数的并发限制和poll函数应用举例

    一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置, ...

  6. select()函数详解

    elect()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet().accept().recv()或recvfr ...

  7. select函数详解及使用案例

    1.select函数原型 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval ...

  8. select函数详解及实例分析

    Select函数在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect. accept.recv或recvfrom这样的阻塞 ...

  9. TCP/IP 协议栈及 OSI 参考模型详解--云平台技术栈04

    导读:之前发布了云平台技术栈(ps:点击可查看),本文主要说一下其中的tcp/ip和网络! 来源:王东裕 http://wangdy.blog.51cto.com/3845563/1588379 OS ...

最新文章

  1. python中sorted函数的用法_Python3 中sorted() 函数的用法
  2. Flink学习笔记:搭建Flink on Yarn环境并运行Flink应用
  3. tps波动很大的原因_期货揭秘:价格的波动规律(2)
  4. oracle 修索引改空间_oracle 修改索引现有表空间
  5. Java_基础_fail-fast
  6. 九大知识领域与五大过程组
  7. 《社会企业开展应聘文职人员培训规范》团体标准在新华书店上架
  8. springboot自定义启动logo
  9. N1刷入Armbian(Debian11 bullseye)笔记
  10. 最喜欢的动画片《长江7号爱地球》
  11. 计算机程序前端和后端指什么,什么是前端和后端
  12. Python标准库time
  13. Effie: 一款属于程序工作者的写作软件
  14. 区块链,屌丝逆袭的最后机会
  15. 华为广告ADS广告位测试ID
  16. 计算机科学导论第五章计算机组成 学习笔记+习题答案
  17. MTO 和MaTO MMDTLZ
  18. 量子通信需要量子计算机吗,【光明日报】关于量子通信,这些问题你困惑过吗...
  19. 0R电阻和1R电阻的用处
  20. 安卓手机主题软件_安卓手机直播软件:绝影Live

热门文章

  1. 如何充分利用您的Kwikset Kevo Smart Lock
  2. centos7 创建、删除目录软连接
  3. IDEA中设置注释模板的方法
  4. 推荐系统经典-BPR算法
  5. JavaScript内置对象2
  6. linux 安全狗 加入云服务,云服务器如何安装安全狗
  7. 第二届美团网络安全高校挑战赛M-Champion,正式启动报名!
  8. 利用telnet命令发送HTTP请求报文
  9. unity学习笔记~材质金属颜色对照表
  10. DolphinScheduler 3.1.0 海豚集群运维使用问题记录