13、SOCKET-IO复用技术 - select、poll、epoll

目录

  • 13、SOCKET-IO复用技术
    • 1、五种I/O模型
    • 2、阻塞I/O模型
    • 3、非阻塞I/O模型
    • 4、I/O复用模型
    • 5、信号驱动I/O模型
    • 6、异步I/O模型
    • 7、I/O复用
    • 8、shutdown函数
    • 9、select函数
    • 10、poll函数
    • 11、epoll函数
      • 11-1、epoll_create
      • 11-2、epoll_ctl
      • 11-3、epoll_wait
      • 11-4、Epoll工作模式
      • 11-5、epoll示例
      • 12、总结

13、SOCKET-IO复用技术

1、五种I/O模型

  • 阻塞I/O
  • 非阻塞I/O
  • I/O复用(select和poll)
  • 信号驱动I/O
  • 异步I/O

2、阻塞I/O模型

  • 最流行的I/O模型是阻塞I/O模型,缺省时,所有的套接口都是阻塞的

13、SOCKET-IO复用技术 - select、poll、epoll_第1张图片

3、非阻塞I/O模型

  • 当我们把一个套接口设置为非阻塞方式时,即通知内核:当请求的I/O操作非得让进程睡眠不能完成时,不要让进程睡眠,而应返回一个错误

13、SOCKET-IO复用技术 - select、poll、epoll_第2张图片

应用程序连续不断地查询内核,看看某操作是否准备好,这对cpu时间是极大的浪费,一般只在专门提供某种功能的系统中才会用到

4、I/O复用模型

  • 有了I/O复用,我们就可以调用select或poll,在这两个系统调用的某一个上阻塞,而不是真正阻塞于真正的I/O系统调用

13、SOCKET-IO复用技术 - select、poll、epoll_第3张图片

5、信号驱动I/O模型

  • 我们也可以用信号,让内核在描述字准备好时用信号SIGIO通知我们,我们将此方法称为信号驱动I/O

13、SOCKET-IO复用技术 - select、poll、epoll_第4张图片

6、异步I/O模型

  • 异步I/O是Posix.1的1993版本中的新内容,我们让内核启动操作,并在整个操作完成后通知我们

13、SOCKET-IO复用技术 - select、poll、epoll_第5张图片

7、I/O复用

  • 如果一个或多个I/O条件满足(例如:输入已准备好被读,或者描述字可以承接更多输出的时候)我们就能够被通知到,这样的能力被称为I/O复用,是由函数select和poll支持的

I/O复用网络应用场合

  • 当客户处理多个描述字
  • 一个客户同时处理多个套接口
  • 如果一个tcp服务器既要处理监听套接口,又要处理连接套接口
  • 如果一个服务器既要处理TCP,又要处理UDP

8、shutdown函数

  • 功能:关闭套接字两端或一端的socket
#include 

int shutdown(int sockfd,int howto);
  • 参数:

    • SHUT_RD:关闭连接的读这一半,不再接收套接口中的数据且现留在套接口接收缓冲区中的数据都作废
    • SHUT_WR:关闭连接的写这一半,在TCP场合下,这称为为半关闭。当前留在套接口发送缓冲区中的数据都被发送,后跟正常的tcp连接终止序列
    • SHUT_RDWR 连接的读这一半和写这一半都关闭
  • 返回值:成功:0,失败:错误代码

shutdown与close的区别

  • 终止网络连接的正常方法是调用close,但close有两个限制可由函数shutdown来避免。
  • close将描述字的访问计数减1,仅在此计数为0时才关闭套接口;用shutdown我们可以激发TCP的正常连接终止序列,而不管访问计数
  • Close终止了数据传送的两个方向:读和写。由于TCP连接是全双工的,有很多时候我们要通知另一端我们已完成了数据发送,即使一端仍有许多数据要发送也是如此。

9、select函数

  • 这个函数允许进程指示内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某指定的时间后才唤醒进程
  • 功能:提供了即时响应多个套接的读写事件
#include 
#include 

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *except,const struct timeval *timeout);

struct timeval(
  long tv_sec;  //秒
  long tv_usec;//微秒
);
  • 参数
    • maxfdp1:等待最大套接字值加1,(等待套接字的数量)
    • readset:要检查读事件的容器
    • writeset:要检查写事件的容器
    • timeout:超时时间
  • 返回值:
    • 返回触发套件接字的个数

timeout参数

  • 永远等待下去:仅在有一个描述字准备好I/O时才返回,为此,我们将timeout设置为空指针
  • 等待固定时间:在有一个描述字准备好I/O是返回,但不超过由timeout参数所指timeval结构中指定的秒数和微秒数
  • 根本不等待:检查描述字后立即返回,这称为轮询。定时器的值必须为0

fd_set参数

  • select使用描述字集,它一般是一个整数数组,每个数中的每一位对应一个描述字。
  • 使用fd_set数据类型来表示这个描述字集,我们不用去关心具体的实现细节。

操作fd_set的四个宏

  • void FD_ZERO(fd_set *fdset); //清空描述字集合
  • void FD_SET(int fd, fd_set *fdset); //添加一个描述字到集合中
  • void FD_CLR(int fd, fd_set *fdset); //从集合中删除一个描述字
  • int FD_ISSET(int fd, fd_set *fdset);//描述字是否在该集合中

select函数返回值

  • 当返回时,结果指示哪些描述字已准备好。
  • 返回时,我们用宏FD_ISSET来测试结构fd_set中的描述字。描述字集中任何与没有准备好的描述字相对应的位返回时清成0。为此,每次调用select时,我们都得将所有描述字集中关心的都置为1
  • 如果在任何描述字准备好之前定时器时间到,则返回0
  • 返回-1表示有错。

select缺点

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小了,默认是1024

select示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ERR_EXIT(M)\
do\
{\
	perror(M);\
	exit(1);\
}while(0);

int main(int argc,char *argv[])
{
   
    int sockfd = socket(PF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
        ERR_EXIT("socket");
    int on = 1;
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) == -1)
        ERR_EXIT("setsockopt");
    struct sockaddr_in sockaddr; 
    bzero(&sockaddr,sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //inet_pton(AF_INET,"0.0.0.0",&sockaddr.sin_addr);
    sockaddr.sin_port = htons(5566);
    if(bind(sockfd,(struct sockaddr *)&sockaddr,sizeof(sockaddr)) == -1)
        ERR_EXIT("bind");
    if(listen(sockfd,5) == -1)
        ERR_EXIT("listen");
    
    int maxfd = sockfd;
    int client[FD_SETSIZE];
    
    fd_set rset;
    fd_set allset;
    FD_ZERO(&rset);
    FD_ZERO(&allset);
    FD_SET(sockfd,&allset);
    int nready;
    int conn;
    int i;
    for(i = 0;i < FD_SETSIZE;i++)
        client[i

你可能感兴趣的:(Linux网络编程)