TCP 和 HTTP 的 keep-alive

2019-08-21

今天总结一下TCP和HTTP的keep-alive .

虽然都叫keep-alive,但这是两个完全不同的概念。

TCP keep-alive

TCP中keep-alive的概念,是为了防止建立连接的双方因为长时间没有数据发送,以至于可能会意外断开连接,而定时发送的一个数据包,从而保证连接仍alive的一种手段。

keep-alive数据包是不携带实际数据(payload)的包,所以不会影响到上层应用。

所有平台的tcp实现,都支持keep-alive特性,但是默认是关闭了。可以修改内核参数来默认开启,也可以通过应用程序来开启。

以 Python 为例,只需给socket设置这几个参数即可:

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)

client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) #开启keep-alive
client.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 3) #空闲多久发送keep-alive包
client.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 1) #多久循环一次
client.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 10) # 最大重试次数,这里所示:如果发了10次keep-alive包,都没有收到响应,就可以认为连接已经断开

client.connect(("172.16.200.1", 4000))

while True:
    pass    

利用上面的Python代码,去连接一个服务器,这里我用nc -l 4000来监听4000端口,运行上面的Python代码,用wireshark抓包,即可看到:

tcp-keep-alive

有兴趣的朋友可以自己试验一下。

HTTP keep-alive

HTTP的keep-alive ,不是指的:keep-tcp-connection-alive,而是一种多个请求包共用一个TCP连接的技术,从而省去三次连接四次挥手等开销。

我们知道HTTP是基于TCP的,在没有keep-alive的年代,每个http请求都要创建一个tcp连接,收到响应后就关闭tcp连接。但是如果页面请求量大,这种反复创建和关闭tcp连接的方式带来的资源和时间上的开销就很大了。于是就出现了keep-alive。

所谓keep-alive 就是:http请求头带上:Connection: keep-alive, 然后如果web服务器觉得没问题,在响应头中也回应:Connection: keep-alive。那么下一次页面的HTTP请求就可以复用上一次的连接了,免去了重新建立tcp连接的过程。

keep-alive 在 http/1.1 中是默认开启的,也就是带不带上Connection: keep-alive都一样,但是如果服务器回复:Connection: close。也就是说服务器不答应复用tcp连接,那下次请求还是得重新建立tcp连接。

复用连接就带来另一个问题,所以的报文都在一条连接上发送,就需要准确判断报文的边界:怎么判断请求的边界,我在上一篇博文 中有说过思路,上一篇主要针对服务器接受报文而言,主要是根据Content-length字段来判断请求报文的主体大小。

对于客户端来说,情况和服务器又有所不同,主要是服务器因为要压缩内容以减少传输数据量(gzip),可能对响应进行分块传输Transfer-Encoding: chunked,也就是一边压缩一边传输,无法提前预知响应报文的长度Content-length。判断报文结束又有所不同,如有兴趣可查看ququ前辈这篇文章