​ 本篇博客的视频教程首发于 Youtube:科技小飞哥,加入 电报粉丝群 获得最新视频更新和问题解答。

后端面试系列将剖析后端面试中常考技术点,用尽量短的篇幅把一个一个技术点呈现出来。

结论

如果面试官问你,如果一个socket返回可读,read返回0是什么情况?

其实这是一个很简单的问题,它就是考察socket的可读可写条件这个知识点。

面试官也可能直接问你:socket的可读可写条件是什么?

这里先说答案:
如果一个socket返回可读,read返回0,就是socket可读条件的一种,也就是对端关闭。
详细来说就是:该连接的读这一半关闭(也就是接收了FIN的TCP连接)。对这样的socket的读操作将不阻塞并返回0。

下面详细来讲解,socket可读可写条件。

socket可读可写条件

要了解socket可读可写条件,我们先了解几个概念:

1.接收缓存区低水位标记(用于读)和发送缓存区低水位标记(用于写):
每个套接字有一个接收低水位和一个发送低水位。他们由select函数使用。
接收低水位标记是让select返回"可读"时套接字接收缓冲区中所需的数据量。对于TCP,其默认值为1。
发送低水位标记是让select返回"可写"时套接字发送缓冲区中所需的可用空间。对于TCP,其默认值常为2048。(注:面试点之一)

通俗的解释一下,缓存区我们当成一个大小为 n bytes的空间,那么:

接收区缓存的作用就是,接收对面的数据放在缓存区,供应用程序读。当然了,只有当缓存区可读的数据量(接收低水位标记)到达一定程度(eg:1)的时候,我们才能读到数据,不然不就读不到数据了吗。
发送区缓存的作用就是,发送应用程序的数据到缓存区,然后一起发给对面。当然了,只有当缓存区剩余一定空间(发送低水位标记)(eg:2048),你才能写数据进去,不然可能导致空间不够。

2.FIN: (结束标志,Finish)用来结束一个TCP回话,但对应端口仍处于开放状态,准备接收后续数据。

可读

下列四个条件中的任何一个满足时,socket准备好读: 

  1. socket的接收缓冲区中的数据字节大于等于该socket的接收缓冲区低水位标记的当前大小。对这样的socket的读操作将不阻塞并返回一个大于0的值(也就是返回准备好读入的数据)。我们可以用 SO_RCVLOWAT socket选项来设置该socket的低水位标记。对于TCP和UDP的socket而言,其缺省值为1。
  2. 该连接的读这一半关闭(也就是接收了FIN的TCP连接)。对这样的socket的读操作将不阻塞并返回0。(注:面试点之一)
  3. socket是一个用于监听的socket,并且已经完成的连接数为非0。这样的soocket处于可读状态,是因为socket收到了对方的connect请求,执行了三次握手的第一步 (对方发送SYN请求过来,使监听socket处于可读状态)正常情况下,这样的socket上的accept操作不会阻塞。
  4. 有一个socket有异常错误条件待处理。对于这样的socket的读操作将不会阻塞,并且返回一个错误(-1),errno则设置成明确的错误条件。这些待处理的错误也可通过指定socket选项SO_ERROR调用getsockopt来取得并清除。

可写

下列三个条件中的任何一个满足时,socket准备好写: 

  1. socket的发送缓冲区中的数据字节大于等于该socket的发送缓冲区低水位标记的当前大小。对这样的socket的写操作将不阻塞并返回一个大于0的值(也就是返回准备好写入的数据)。我们可以用SO_SNDLOWAT socket选项来设置该socket的低水位标记。对于TCP和UDP的socket而言,其缺省值为2048。
  2. 该连接的写这一半关闭。对这样的socket的写操作将产生SIGPIPE信号,该信号的缺省行为是终止进程。
  3. 有一个socket异常错误条件待处理。对于这样的socket的写操作将不会阻塞并且返回一个错误(-1),errno则设置成明确的错误条件。这些待处理的错误也可以通过指定socket选项SO_ERROR调用getsockopt函数来取得并清除。

关闭

解释一下连接的读/写这一半关闭:

如图所示: socket

读半关闭: 四次挥手的第一步,client调用close发送FIN到server,server接收到FIN后,知道client已经关闭了写,也就是server不会再读到任何数据了,也就是读这一半关闭,这个时候server的这一端一定可读,且返回0(read returns 0)。

写半关闭: 四次挥手的第三步,server调用close发送FIN到client,这个时候server已经关闭了写,这个时候server端不会再写数据到client,也就是写这一半关闭,这个时候server的这一端再对该socket进行写会产生SIGPIPE信号(异常)。

<全文完>