11

后端面试之网络编程-socket返回可读,read返回0是什么情况?

 2 years ago
source link: https://www.leftpocket.cn/post/tcpip/socket_read_write/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

​ 原文地址:码农在新加坡的个人博客

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

如果面试官问你,如果一个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信号(异常)。

<全文完>

欢迎关注我的微信公众号:码农在新加坡,有更多好的技术分享。

pic


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK