3

skynet 处理 TCP 连接半关闭问题

 2 years ago
source link: https://blog.codingnow.com/2021/02/skynet_tcp_halfclose.html
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.

skynet 处理 TCP 连接半关闭问题

TCP 连接是双工的,既可以上行数据,又可以下行数据。连接断开时,两侧通道也是分别关闭的。

从 API 层面看,如果 read 返回 0 ,则说明上行数据已经关闭,后续不再会有数据进来。但此时,下行通道未必关闭,也就是说对端还可能期待收取数据。

同样,如果 write 返回 -1 ,错误是 EPIPE ,则表示下行通道已经关闭,不应再发送数据。但上行通道未必关闭,之后的 read 还可能收到数据。

用 shutdown 指令可以主动关闭单个通道(上行或下行)。

如果 tcp 连接只有一侧关闭,我们(skynet 中)称之为半关闭状态。在最开始,skynet 是将半关闭视为关闭的。这是因为,skynet 一开始只考虑网络游戏应用。在网络游戏中,连接被视为不可靠的,任何时候客户端都应该妥善处理服务器断开的情况,而服务器也应该处理客户端不告而别。业务层一般会额外做一套握手协议,不完全依赖 tcp 的底层协议。状态一般是不在客户端保存的。如果上一次链接上有数据没有发送到,那么下一次建立连接会重新拿取必要的数据。

所以,在 read 返回 0 ,或是 write 出错后,skynet 的底层都直接 close 连接。这种简单粗暴的方法可以大大简化底层的实现复杂度。同时,如果需要确保业务层数据交换的可靠性,就在业务层增加确认机制。见 issue 50


随着 skynet 应用领域的扩展,我发现在很多场合依赖已有的协议(不方便增加确认机制)时,必须更好的处理半关闭状态。比如,服务器想推送很多数据然后关闭连接。如果业务层不提供客户端确认收到的机制,就很难正确实现。只是简单的 write write …… close ,很可能在底层还没有全部发送完数据前就先把连接 close 了。

这时,skynet 增加了一个简单的半关闭处理机制。如果主动调用 close 时发现之前还有从业务层写出的数据并没有发送完,那么就让 fd 进入半关闭状态。半关闭状态下,fd 不可读写,从业务层看已经关闭,但底层会一直保留 fd 到数据全部发送完毕或发送失败(对端关闭)。

这个机制良好运作了好几年,直到有很多基于 skynet 的 web server 的应用场景出现。

我们发现,某些浏览器在从 web server 下载文件时会有这样的行为:发送一个 http 请求,然后立刻关闭 write 但保留 read 。也就是说,在 web server 看,读完请求后,会 read 到 0 。如果此时服务器主动关闭连接,后续就不再能写出回应包。由于 read 的行为是 skynet 底层的网络线程主动进行,而不是业务层调用 read 触发的,所以业务层无法控制它。

为解决这个问题,我在 2021 年初 提交了一个 pr 能更妥善的处理半关闭问题。在实现过程中我发现,如果我想保持兼容,并不把复杂性甩给上层(让业务层可以自己小心的单边关闭连接)。那么,我需要更仔细的处理半关闭的情况。底层使用一个半关闭状态值是不够的,必须区分 HALFCLOSE_READHALFCLOSE_WRITE 两个状态(和操作系统内核处理半关闭状态一致)。而且,我还需要区分半关闭状态是服务器主动进行的,还是对端操作的。

主动进入半关闭状态比较简单:业务层不允许只关闭一半的信道(读或写),但它在关闭时,之前还有推送到底层的下行数据没有发送完,就需要进入半关闭状态,调用 shutdown 关闭读,但还需要等待数据发送完毕。

被动进入半关闭状态需要分为两种:read 到 0 或 write 出现 EPIPE 错误。分别应该设为不同的半关闭状态。

由于我在这方面经验不足,这次的修改遗留了很多 bug 。感谢 skynet 的广泛应用,很快就收到了反馈。这两周一直在修复实现问题。具体可以看 issue 1346 以及相关讨论。

现在的版本暂时没有新的 bug 报告。已发现的 bug 很多是对 epoll 理解不够,kqueue 也和 epoll 有许多细微的差别。仔细阅读手册,查看 stackoverflow 的相关问题,对理解它们帮助很大。例如:TCP: When is EPOLLHUP generated?

云风 提交于 February 27, 2021 11:06 AM | 固定链接


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK