zoukankan      html  css  js  c++  java
  • Socket I/O multiplexing

    网络应用中I/O多路复用的应用场景

    1. 客户端处理多个描述符(通常是交替输入+网络套接字,可以及时获取服务端发送的FIN包)
    2. 客户端同时处理多个套接字
    3. TCP服务端处理监听套接字和连接套接字
    4. 服务端同时处理TCP和UDP
    5. 服务端处理多个服务以及多种协议

    I/O模型

    阻塞I/O

    最常见的I/O模型。默认情况下,socket是阻塞的。使用UDP数据包套接字为例,在数据就绪之前,recvfrom一直处于等待状态。
    a

    非阻塞I/O

    当I/O操作不能完成时,内核不会让进程睡眠,而是返回错误。进程采用轮询方式对非阻塞描述符循环采取操作。
    a

    I/O多路复用

    使用select/poll/epoll系统调用,当描述符准备就绪时,系统调用返回,并执行I/O操作。
    a

    信号驱动I/O

    进程设置了信号处理程序后立即返回,当描述符就绪时,可以执行I/O操作时,内核发送SIGIO信号通知进程。
    a

    异步I/O

    POSIX标准中定义了异步I/O。通常的做法是通知内核开始执行I/O操作,全部完成后发送通知。aio_或lio_设置通知的方式。
    a
    五种模型的比较
    a
    同步I/O:进程在I/O完成前会被阻塞
    异步I/O:进程不会阻塞
    因此,前四种模型属于同步I/O

    select()方法

    #include <sys/select.h>
    #include <sys/time.h>
    int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
    

    最后一个参数timeout指定了等待时间。总共有三种方式,永久等待(直到有描述符就绪);等待(不超过)一段时间;不等待。如果进程捕捉到信号,那么前两种等待会被信号处理程序中断,会返回EINTR。有些系统的select调用返回时,会改变timeout参数的值,表示剩余的等待时间,为了可移植性,POSIX将timeout设为const。当select被中断时,为了获取剩余等待时间,只能在调用前和返回后两次获取系统时间,但是时间可能被更改。
    中间的三个参数readset, writeset, exceptset指定了描述符集合,分别对read,write,excpetion进行测试。select调用修改了三个描述符集合,那些已经就绪的描述符在描述符集合中被置位。每次调用select前都要重新设置描述符集合。第一个缺点是为了检测哪些描述符已经就绪,每次需要遍历fd_set。

    void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */ 
    void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */
    void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */
    int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */
    

    maxfdp1参数指定了测试描述符的数量,它的值是被测试的最大描述符的值加1,相当于可接受从0开始的描述符个数。FD_SETSIZE是fd_set结构中描述符的数量,一般为1024,即select调用能够测试的最大描述符的值是1024,这也是select调用的第二个缺点。普通应用程序不会使用如此多数量的描述符,maxfdp1这个参数的存在可以使内核不从进程拷贝不必要的那部分fd_set,相当于提高效率。每次调用都需要拷贝描述符集合到内核空间是select的第三个缺点。
    函数返回值表示所有被置位的数量,如果一个就绪的描述符在超过一个集合中被关注,计相应的次数而不是1次。0代表没有描述符就绪,-1代表错误,通常是被捕获的信号中断。

    描述符就绪条件

    1. 读就绪:
      • 套接字接收缓冲区中数据字节数在低水位线(默认为1)之上。此时read返回值大于0
      • 读方向连接关闭(如TCP收到FIN包)。此时read返回0
      • 对于监听套接字,已完成的连接数非0。此时accept不会阻塞
      • 套接字发生错误。read返回-1
    2. 写就绪:
      • 套接字发送缓冲区可用空间大小在低水位线之上(默认2048)并且套接字已经建立连接(TCP)或不需要连接(UDP)
      • 写方向套接字关闭,write操作会产生SIGPIPE信号
      • 非阻塞的connect完成连接或失败
      • 套接字产生错误,write返回-1
      • 当套接字产生错误时,同时标记为可读/可写
    3. 套接字异常:带外数据

    shutdown()

    客户端发送FIN包仅仅表示不会再发送数据,而读数据可能还没结束。所以需要shutsown函数。

    1. close()减少描述符的引用计数,只有引用计数为0才真正关闭套接字。shutdown()直接开始四次挥手
    2. close()关闭套接字的读写方向
    #include <sys/socket.h>
    int shutdown(int sockfd, int howto);
    /* SHUT_RD(0):关闭读方向
     * SHUT_WR(1):关闭写方向
     * SHUT_RDWR(2):关闭两个方向,相当于调用两次shutdown()
     */
    
  • 相关阅读:
    【SCOI 2011】 糖果
    【POJ 3159】 Candies
    【POJ 1716】 Integer Intervals
    【POJ 2983】 Is the information reliable?
    【POJ 1364】 King
    【POJ 1201】 Intervals
    【POJ 1804】 Brainman
    6月10日省中提高组题解
    【POJ 3352】 Road Construction
    【POJ 1144】 Network
  • 原文地址:https://www.cnblogs.com/zyfgs2012/p/4154240.html
Copyright © 2011-2022 走看看