TCP 短连接的情况,client 向 server 发起连接请求,server 接到请求,然后双方建立连接。client 向 server 发送消息,server 回应 client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是client先发起close操作。一般的 server 不会回复完 client 后立即关闭连接的,当然不排除有特殊的情况。短连接一般只会在 client/server 间传递一次读写操作。
短连接的有点是管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段。
client 向 server 发起连接,server 接受 client 连接,双方建立连接。Client 与 server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。
首先说一下 TCP/IP 详解上讲到的 TCP 保活功能,保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将永远等待客户端的数据,保活功能就是试图在服务器端检测到这种半开放的连接。
在需要长连接的网络通信程序中,经常需要心跳检测机制,来实现检测对方是否在线或者维持网络连接的需要,这一机制是在应用层实现的。对应的,在 TCP 协议中,也有类似的机制,就是 TCP 保活机制。
TCP 连接建立后,在一段时间范围内双发没有互相发送任何数据,这时候站在服务器端,其需要关心两个问题:
- 怎么判断对方是否还在线。这是因为,TCP 对于非正常断开的连接系统并不能侦测到(比如网线断掉)。
- 长时间没有任何数据发送,连接可能会被中断。这是因为,网络连接中间可能会经过路由器、防火墙等设备,而这些有可能会对长时间没有活动的连接断掉。
保活机制是由一个保活计时器实现的。当计时器被激发,连接一段将发送一个保活探测报文,另一端接收报文的同时会发送一个ACK
作为响应。
具体实现上有以下几个相关的配置:
- 保活时间:默认7200秒(2小时)
- 保活时间间隔:默认75秒
- 保活探测数:默认9次
查看Linux系统中TCP保活机制对应的系统配置如下(不同系统实现可能不同):
sl@Li:/proc/sys/net/ipv4$ cat tcp_keepalive_time
7200
sl@Li:/proc/sys/net/ipv4$ cat tcp_keepalive_intvl
75
sl@Li:/proc/sys/net/ipv4$ cat tcp_keepalive_probes
9
连接中启动保活功能的一端,在保活时间内连接处于非活动状态,则向对方发送一个保活探测报文,如果收到响应,则重置保活计时器,如果没有收到响应报文,则经过一个保活时间间隔后再次向对方发送一个保活探测报文,如果还没有收到响应报文,则继续,直到发送次数到达保活探测数,此时,对方主机将被确认为不可到达,连接被中断。
TCP 保活功能工作过程中,开启该功能的一端会发现对方处于以下四种状态之一:
- 对方主机仍在工作,并且可以到达。此时请求端将保活计时器重置。如果在计时器超时之前应用程序通过该连接传输数据,计时器再次被设定为保活时间值。
- 对方主机已经崩溃,包括已经关闭或者正在重新启动。这时对方的 TCP 将不会响应。请求端不会接收到响应报文,并在经过保活时间间隔指定的时间后超时。超时前,请求端会持续发送探测报文,一共发送保活探测数指定次数的探测报文,如果请求端没有收到任何探测报文的响应,那么它将认为对方主机已经关闭,连接也将被断开。
- 客户主机崩溃并且已重启。在这种情况下,请求端会收到一个对其保活探测报文的响应,但这个响应是一个重置报文段
RST
,请求端将会断开连接。 - 对方主机仍在工作,但是由于某些原因不能到达请求端(例如网络无法传输,而且可能使用 ICMP 通知也可能不通知对方这一事实)。这种情况与状态 2 相同,因为 TCP 不能区分状态 2 与状态 4,结果是都没有收到探测报文的响应。
- 在出现短暂的网络错误的时候,保活机制会使一个好的连接断开;
- 保活机制会占用不必要的带宽;
保活功能在默认情况下是关闭的。没有经过应用层的请求,Linux 系统不会提供保活功能。
- (1) Keep Alive机制开启后,TCP层将在定时时间到后发送相应的KeepAlive探针以确定连接可用性。默认时间为7200s(两小时),失败后重试10次,每次超时时间75s。显然默认值无法满足移动网络下的需求;
- (2) 即便修改了(1)中的默认值,也不能很好的满足业务需求。TCP的KeepAlive用于检测连接的死活而不能检测通讯双方的存活状态。比如某台服务器因为某些原因导致负载超高,无法响应任何业务请求,但是使用TCP探针则仍旧能够确定连接状态,这就是典型的连接活着但业务提供方已死的状态,对客户端而言,这时的最好选择就是断线后重新连接其他服务器,而不是一直认为当前服务器是可用状态,一直向当前服务器发送些必然会失败的请求。
- (3) socks代理会让Keep Alive失效。socks协议只管转发TCP层具体的数据包,而不会转发TCP协议内的实现细节的包。所以,一个应用如果使用了socks代理,那么TCP的KeepAlive机制就失效了。
- (4) 部分复杂情况下Keep Alive会失效,如路由器挂掉,网线直接被拔除等;
实现 HTTP/1.0 keep-alive 连接的客户端可以通过包含 Connection:Keep-Alive 首部请求将一条连接保持在打开状态,如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有Connection: Keep-Alive 首部,客户端就认为服务器不支持 keep-alive,会在发回响应报文之后关闭连接。HTTP/1.1以后Keep-Alive是默认打开的。