非阻塞之socket

创建TCP应用程序使用socket编程时,常用的操作读、写、接收连接、连接,使用这些操作时,有阻塞、非阻塞之分。对于服务端程序,非阻塞模式是最常用的 方法,通常配合多路复用技术select、epoll、poll等来使用

读操作


下面列出了读操作的阻塞和非阻塞模式:

  • 阻塞

    read()总是在接收缓冲区有数据时就立即返回,不是等到应用程序给定的数据充满才返回。当接收缓冲区为空时,阻塞模式会等待

  • 非阻塞

    当接收缓冲区为空时,非阻塞模式立即返回-1,并有 EWOULDBLOCK 或 EAGAIN 错误,所以处理这类错误时,在下一个tick可以继续调用 read()

写操作


下面列出写读操作的阻塞和非阻塞模式:

  • 阻塞

    阻塞模式下,write 只有在发送缓冲区足以容纳应用程序的输出字节时才返回,在写的过程中,对方主动关闭了套接字,这个时候 write 调用会立即返回, 并通过返回值告诉应用程序实际写入的字节数,如果再次对这样的套接字进行 write 操作,就会返回失败。失败是通过返回值 -1 来通知到应用程序的

  • 非阻塞

    在非阻塞的模式下,则是能写入多少就写入多少,并返回实际写入的字节数

accept


监听套接字的阻塞和非阻塞模式:

  • 阻塞

    阻塞模式下直到有完成的连接,才返回,对于单线程的服务端程序这个方案不能接受,当配合select、epoll网络模型使用时,在监听套接字上有可读事件发生时, 并没有马上调用 accept。由于客户端发生了 RST 分节,该连接被接收端内核从自己的已完成队列中删除了,此时再调用 accept, 由于没有已完成连接(假设没有其他已完成连接),accept 一直阻塞,更为严重的是,该线程再也没有机会对其他 I/O 事件进行分发, 相当于该服务器无法对新连接和其他 I/O 进行服务

  • 非阻塞

    将监听套接字设置为非阻塞模式,通常配合select、epoll网络模型使用,阻塞模式发生的严重问题就不会发生了,但是accept 的返回值为-1,可以通过查看错误码,处理看似异常的错误 例如EAGAIN

所以一定要将监听套接字设置为非阻塞的

connect


一个服务端程序有时需对外提供服务,但又同时作为客户端向其他服务发起连接,当向其他服务发起连接时,对于单线程服务程序,一个阻塞模式发起连接通常不能接受

  • 阻塞

    阻塞模式下发起连接,直到TCP三次握手完成,或者发生错误时connect函数才返回,阻塞模式通常适合程序启动时需要先与某服务程序建立连接,才能继续向下执行的场景

  • 非阻塞

    在非阻塞 TCP 套接字上调用 connect 函数,会立即返回一个 EINPROGRESS 错误。TCP 三次握手会正常进行,应用程序可以继续处理其他逻辑。 当该连接建立成功或者失败时,通过select、epoll网络模型可以进行连接的状态检测