创建TCP应用程序使用socket编程时,常用的操作读、写、接收连接、连接,使用这些操作时,有阻塞、非阻塞之分。对于服务端程序,非阻塞模式是最常用的 方法,通常配合多路复用技术select、epoll、poll等来使用
下面列出了读操作的阻塞和非阻塞模式:
阻塞
read()总是在接收缓冲区有数据时就立即返回,不是等到应用程序给定的数据充满才返回。当接收缓冲区为空时,阻塞模式会等待
非阻塞
当接收缓冲区为空时,非阻塞模式立即返回-1,并有 EWOULDBLOCK 或 EAGAIN 错误,所以处理这类错误时,在下一个tick可以继续调用 read()
下面列出写读操作的阻塞和非阻塞模式:
阻塞
阻塞模式下,write 只有在发送缓冲区足以容纳应用程序的输出字节时才返回,在写的过程中,对方主动关闭了套接字,这个时候 write 调用会立即返回, 并通过返回值告诉应用程序实际写入的字节数,如果再次对这样的套接字进行 write 操作,就会返回失败。失败是通过返回值 -1 来通知到应用程序的
非阻塞
在非阻塞的模式下,则是能写入多少就写入多少,并返回实际写入的字节数
监听套接字的阻塞和非阻塞模式:
阻塞
阻塞模式下直到有完成的连接,才返回,对于单线程的服务端程序这个方案不能接受,当配合select、epoll网络模型使用时,在监听套接字上有可读事件发生时, 并没有马上调用 accept。由于客户端发生了 RST 分节,该连接被接收端内核从自己的已完成队列中删除了,此时再调用 accept, 由于没有已完成连接(假设没有其他已完成连接),accept 一直阻塞,更为严重的是,该线程再也没有机会对其他 I/O 事件进行分发, 相当于该服务器无法对新连接和其他 I/O 进行服务
非阻塞
将监听套接字设置为非阻塞模式,通常配合select、epoll网络模型使用,阻塞模式发生的严重问题就不会发生了,但是accept 的返回值为-1,可以通过查看错误码,处理看似异常的错误 例如EAGAIN
所以一定要将监听套接字设置为非阻塞的
一个服务端程序有时需对外提供服务,但又同时作为客户端向其他服务发起连接,当向其他服务发起连接时,对于单线程服务程序,一个阻塞模式发起连接通常不能接受
阻塞
阻塞模式下发起连接,直到TCP三次握手完成,或者发生错误时connect函数才返回,阻塞模式通常适合程序启动时需要先与某服务程序建立连接,才能继续向下执行的场景
非阻塞
在非阻塞 TCP 套接字上调用 connect 函数,会立即返回一个 EINPROGRESS 错误。TCP 三次握手会正常进行,应用程序可以继续处理其他逻辑。 当该连接建立成功或者失败时,通过select、epoll网络模型可以进行连接的状态检测