0xAA55 发表于 2019-2-11 11:52:27

【翻译】【TCP】关闭连接的讲究

原文网址:https://developer.att.com/video-optimizer/docs/best-practices/closing-connections
译者:0xAA55

介绍

移动应用通常在完成数据传输、不再需要连接后,依然保持连接状态,即使已经不需要使用这个TCP连接了。这样的问题通常由于连接的关闭过程没有被正确进行(比如关闭连接的时候遭遇丢包、高延迟等),而关闭一个连接本应是通讯的一个重要环节。这个连接最终由于超时而被关闭。这是一个非常低效的方式来关闭一个TCP连接。

最好在传输数据后尽快关闭连接,以防止无线电信道不必要地保持打开状态。

下图(图1)说明了无效关闭的连接问题。该图显示了AT&T视频优化器诊断工具的“诊断”选项卡中的“跟踪”图表。视频优化工具从应用程序收集跟踪数据,根据建议的最佳实践对其进行评估,并生成分析结果。


图1:跟踪结果显示连接延迟终止。

在上图的“Bursts”行里,第10到40秒左右的红色和绿色部分表示数据传输。第80秒附近有一条蓝色的细线表示连接关闭。

在“Bursts”行下面的“RRC States”行,表示的是无线电传输设备的运行状态,可以理解为功耗。它有个高供能状态、低供能状态和关闭状态。可以看出,设备在传输数据之后就进入了空闲状态,然后设备进入低供能状态并关闭。然而,在第80秒处,设备为了发送一条TCP关闭连接的报文,它需要被重新开启,然后发送数据,再进入高供能、低供能状态并关闭,造成能量浪费。而如果你在完成数据的传输后就立即关闭连接的话,就能省下这些事,让无线电设备不用被再次唤醒。

刚才的实践充分证明了及时并有效地关闭TCP连接的重要性,并且能更直观地看出不这么做带来的功耗损失。

背景知识

在通常的操作情况下,传输的两端都在同时进行传输和接收数据的过程。连接的终止通常由其中一端主动发出信号来表明自己想要关闭连接,但要正常关闭连接需要多个步骤进行来保证连接能被优雅地关闭。

要了解这些要求,记住两个TCP标志很重要:
[*]ACK- 表示确认。 客户端发送的初始SYN数据包之后的所有数据包都应设置此标志。
[*]FIN- 表示发送方将不再发送数据。


当TCP会话的任一侧完成发送数据时,它可以发送FIN信号以关闭连接。当对方收到FIN时,它必须通知应用程序对方正在关闭传输。发送FIN通常是应用程序关闭套接字的结果,另一方通过ACK确认。

即使使用三次握手(SYN,SYN-ACK,ACK)建立TCP连接,也可以通过各种方式终止它,包括以下内容:
[*]其中一方发送FIN数据包,另一方发送ACK,然后开始四次握手来终止整个TCP会话。四次握手结束后,两边在真正关闭连接之前等待一段时间。这段时间里,相同的端口不能发起新连接。它可以防止延迟传递的数据包引起后续的同端口新连接的混淆。
[*]当其中一方发送FIN,另一方发送FIN & ACK,然后自己再发送ACK,可以完成一场三次握手终结连接的过程。
[*]当双方同时发送FIN & ACK的时候,就可以完成一场两次握手终结连接的过程。

无论使用哪种方法终止TCP连接,重要的是要注意它们都是主动进行的终止连接的过程。

问题

如果没有主动关闭TCP连接,则会出现此问题。如果连接未按设计关闭,则可能会因超时而关闭,作为连接的管理的一部分。

TCP协议为了解决这样的问题而制造了“超时”的概念。服务器通常会在一个连接处于空闲的状态下对其进行计时,超时后就会将其关闭。如果一个连接本来已经结束了它的传输,但如果其中一方没有收到另一方的主动关闭连接的请求,它就会一直等待对方的通信直到超时。这是不必要的资源消耗。

如果一个携带了数据的终止传输的报文没有被接收端正确处理(只接收了数据,没有判断它是否为终止连接的请求),这就会导致服务端一直等待客户端(咕咕咕)直到超时。对于无线传输而言,这通常会引起无线网卡设备被启动用于终止连接。根据图1,可以看到无线电设备先进入高供电状态,再进入低供电状态,最后关闭。

这会让使用电池的设备浪费更多的电能。

最佳实践建议

那就是在数据完成传输后迅速关闭TCP连接。这可以防止设备只为了关闭连接而重启无线网卡设备,使其不需要等到TCP超时机制后再发送一个断开连接的报文。这能节约计算机的算力和内存,以及电池的电量,免得让空闲状态被关闭的无线网卡又要被唤醒,然后只发送一个断开连接请求的报文,再重新变得空闲并关闭。

0xAA55 发表于 2019-2-11 11:54:19

这篇AT&T的文章真是废话繁多,就好像是写给一个不懂服务器运维,并且手撸TCP报文的物联网玩家看的。

对于我们平时使用socket来进行TCP通信的情况下而言,我认为更需要去注意的是自己使用TCP进行数据传输的逻辑。不管是阻塞型IO还是非阻塞型、自制状态机方式的IO,有一个坑是需要注意的,那就是shutdown()把关闭连接的请求加入发送缓冲区队列的尾部,并让你的socket进入FIN_WAIT1状态——在某些平台上,如果你的发送缓冲区满了,它就不会发出关闭连接的请求,却让你的socket进入FIN_WAIT1的状态。在关闭连接的时候,需要务必确保所有的数据都发出去了,或者发送缓冲区空,或者使用别的方法丢弃需要发送的数据然后强行关闭——这个办法虽然会引起对方无法收到关闭的请求。
页: [1]
查看完整版本: 【翻译】【TCP】关闭连接的讲究