HTTP keepalive详解

http keepalive 介绍

在http早期,每个http请求都要求打开一个tpc socket连接,并且使用一次之后就断开这个tcp连接。

使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。

但是,keep-alive并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keep-alive,有时比重复利用连接带来的损失还更大。所以,正确地设置keep-alive timeout时间非常重要。
Httpd守护进程,一般都提供了keep-alive timeout时间设置参数。比如nginx的keepalive_timeout。这个keepalive_timout时间值意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接。

当httpd守护进程发送完一个响应后,理应马上主动关闭相应的tcp连接,设置 keepalive_timeout后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,便是keepalive_timeout时间。如果守护进程在这个等待的时间里,一直没有收到浏览发过来http请求,则关闭这个http连接。

http keepalive 分析

假设我们的httpd服务器是nginx, nginx的keepalive_timeout默认是60,即默认开启了http keepalive模式,而且http的长连接会保持60秒,60秒内没有数据来往即关闭连接。

  • 情况一:设置nginx的keepalive_timeout=0,即不开启keepalive模式,这种情况下,每个http请求过来,nginx处理完毕即主动发送FIN信号关闭连接

这里我们设置业务sleep 60 秒

#tcpdump -n host 218.1.57.236 and port 80
20:36:50.792731 IP 218.1.57.236.43052 > 222.73.211.215.http: S 1520902589:1520902589(0) win 65535
20:36:50.792798 IP 222.73.211.215.http > 218.1.57.236.43052: S 290378256:290378256(0) ack 1520902590 win 5840
20:36:50.801629 IP 218.1.57.236.43052 > 222.73.211.215.http: . ack 1 win 32768

20:36:50.801838 IP 218.1.57.236.43052 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
20:36:50.801843 IP 222.73.211.215.http > 218.1.57.236.43052: . ack 797 win 59

20:37:50.803230 IP 222.73.211.215.http > 218.1.57.236.43052: P 1:287(286) ack 797 win 59
20:37:50.803289 IP 222.73.211.215.http > 218.1.57.236.43052: F 287:287(0) ack 797 win 59
20:37:50.893396 IP 218.1.57.236.43052 > 222.73.211.215.http: . ack 288 win 32625
20:37:50.894249 IP 218.1.57.236.43052 > 222.73.211.215.http: F 797:797(0) ack 288 win 32625
20:37:50.894252 IP 222.73.211.215.http > 218.1.57.236.43052: . ack 798 win 59

可以看到上面是经典的http过程,首先是3次握手建立连接,然后客户端发送请求,服务器处理60秒后返回数据,http响应一旦发送完毕,nginx马上关闭这个tcp连接
关闭 keepalive 情况下,一个socket资源从建立到真正释放需要经过的时间是:建立tcp连接 + 传送http请求 + 业务执行 + 传送http响应 + 关闭tcp连接 + 2MSL

  • 情况二:设置nginx的keepalive_timeout=300
#tcpdump -n host 218.1.57.236 and port 80
21:38:05.471129 IP 218.1.57.236.54049 > 222.73.211.215.http: S 1669618600:1669618600(0) win 65535
21:38:05.471140 IP 222.73.211.215.http > 218.1.57.236.54049: S 4166993862:4166993862(0) ack 1669618601 win 5840
21:38:05.481731 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 1 win 32768

21:38:05.481976 IP 218.1.57.236.54049 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
21:38:05.481985 IP 222.73.211.215.http > 218.1.57.236.54049: . ack 797 win 59
21:38:07.483626 IP 222.73.211.215.http > 218.1.57.236.54049: P 1:326(325) ack 797 win 59
21:38:07.747614 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 326 win 32605

21:43:07.448454 IP 222.73.211.215.http > 218.1.57.236.54049: F 326:326(0) ack 797 win 59
21:43:07.560316 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 327 win 32605
21:43:11.759102 IP 218.1.57.236.54049 > 222.73.211.215.http: F 797:797(0) ack 327 win 32605
21:43:11.759111 IP 222.73.211.215.http > 218.1.57.236.54049: . ack 798 win 59

可以看到这个http过程跟上面的keepalive=0情况是比较类似的,只不过当服务器响应完数据后,没有立刻发送FIN信号关闭连接,而是从38分等到43分,nginx才发送FIN信号关闭连接。说明了nginx的keepalive=300配置生效了。

  • 情况三,设置keepalive_timeout=180,并且在同一个tcp连接发送多个http响应
# tcpdump -n host 218.1.57.236 and port 80
22:43:57.102448 IP 218.1.57.236.49955 > 222.73.211.215.http: S 4009392741:4009392741(0) win 65535
22:43:57.102527 IP 222.73.211.215.http > 218.1.57.236.49955: S 4036426778:4036426778(0) ack 4009392742 win 5840
22:43:57.111337 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1 win 32768

22:43:57.111522 IP 218.1.57.236.49955 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
22:43:57.111530 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 797 win 59
22:43:59.114663 IP 222.73.211.215.http > 218.1.57.236.49955: P 1:326(325) ack 797 win 59
22:43:59.350143 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 326 win 32605

22:45:59.226102 IP 218.1.57.236.49955 > 222.73.211.215.http: P 1593:2389(796) ack 650 win 32443
22:45:59.226109 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 2389 win 83
22:46:01.227187 IP 222.73.211.215.http > 218.1.57.236.49955: P 650:974(324) ack 2389 win 83
22:46:01.450364 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 974 win 32281

22:47:57.377707 IP 218.1.57.236.49955 > 222.73.211.215.http: P 3185:3981(796) ack 1298 win 32119
22:47:57.377714 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 3981 win 108
22:47:59.379496 IP 222.73.211.215.http > 218.1.57.236.49955: P 1298:1622(324) ack 3981 win 108
22:47:59.628964 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1622 win 32768

22:50:59.358537 IP 222.73.211.215.http > 218.1.57.236.49955: F 1622:1622(0) ack 3981 win 108
22:50:59.367911 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1623 win 32768
22:50:59.686527 IP 218.1.57.236.49955 > 222.73.211.215.http: F 3981:3981(0) ack 1623 win 32768
22:50:59.686531 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 3982 win 108

这次我们开启了keepalive=180,并且连续发送了几个http请求,可以看到,都复用了同一个连接(因为没有再次的三次握手建立连接),而nginx再最后一次数据发送后,在过了180秒后,发送FIN信号关闭了连接。这说明nginx的keepalive倒计时是从最后一个数据包开始计算的!

http keepalive 实践: 配置nginx 与 upstream 长连接keep-alive配置

nginx代理与上游服务器 upstream 之间的连接默认是关闭了长连接,我们可以通过抓包来看到,通过浏览器提交上来的HTTP/1.1的请求,经过代理服务器以后改为HTTP/1.0, 所以想要代理与服务器之间使用长连接,需要修改nginx的相关配置。

upstream test_keepalive{
    server 127.0.0.1:5002;
    keepalive 4; 
}
server {
    listen       80;
    location / {
    proxy_pass http://test_keepalive;
    proxy_set_header Host            $host:8000;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

upstream里面的有几个配置是跟keepalive相关的:

  • keepalive: 4, 每个nginx worker可以保留的idle连接,也就是说最多可保留的长连接,如果超过这个数量,会根据LRU算法,least recently used 用的最少的连接会被close,需要注意的是,这个参数不会限制worker的跟upstream的连接数。
  • keepalive_timeout :60, nginx跟upstreamd的 idle 空闲长连接,最大超时时间,超过时间没有数据往来则超时关闭
  • keepalive_requests : 100,设置每个长连接最多能处理的请求次数,超过了以后连接就会被close,定期关闭对于清理每个连接的占用的内存是非常必要的,否则连接占用的内存会越来越大,这是不推荐的。

对于location里面的配置,主要是修改http版本为1.1, 并且把nginx默认传递给upsteam connection:close的行为去掉
http_version需要指定用1.1 ,以及原有的Connection头部要被擦掉

  • proxy_http_version 1.1;
  • proxy_set_header Connection "";

这样就完成了nginx转发给upstream的长连接配置了,但是,我们的upstream也要配置keepalive才行啊,否则它无视nginx发来的connection:keepalive 头部也不行。
我们用的是flask 框架,本地调试的话要加上下面的配置,才能使用上http 1.1

flask的本地run()启用keep-alive
>Before app.run(), Add one line, WSGIRequestHandler.protocol_version = "HTTP/1.1"
Don't forget from werkzeug.serving import WSGIRequestHandler.

思考一下,用uwsgi的花需要另外配置keepalive吗?
回答:uwsgi支持keep-alive 如果nginx通过http pass 给uwsgi, --http-keepalive

uwsgi --http=:8000 --http-keepalive -w app &> /dev/null

但是nginx 一般都是通过uwsgi_pass 传递请求给uwsgi,而我们说的是http协议的keepalive ,所以对于其他协议诸如,uwsgi协议,fastcgi协议都是不能生效的
参考
nginx proxy 的规则
nginx keepalive doc
那么至此,nginx跟上游服务器的长连接配置就完成了

curl 测试跟nginx keep-alive 连接是否复用

curl -v  http://127.0.0.1:80/api/recommend/index -H "Host: app-store-server.webapp.163.com"  http://127.0.0.1:80/api/recommend/index

curl后面跟随两个连接,让curl同时请求两个url
这时抓包

127.0.0.1.48402 > 127.0.0.1.80: Flags [S], seq 4215851718, win 43690, options [mss 65495,sackOK,TS val 2968699288 ecr 0,nop,wscale 7], length 0
127.0.0.1.80 > 127.0.0.1.48402: Flags [S.], seq 4157782454, ack 4215851719, win 43690, options [mss 65495,sackOK,TS val 2968699288 ecr 2968699288,nop,wscale 7], length 0
127.0.0.1.48402 > 127.0.0.1.80: Flags [.], ack 1, win 342, options [nop,nop,TS val 2968699288 ecr 2968699288], length 0
127.0.0.1.48402 > 127.0.0.1.80: Flags [P.], seq 1:115, ack 1, win 342, options [nop,nop,TS val 2968699289 ecr 2968699288], length 114: HTTP: GET /api/recommend/index HTTP/1.1
127.0.0.1.80 > 127.0.0.1.48402: Flags [.], ack 115, win 342, options [nop,nop,TS val 2968699289 ecr 2968699289], length 0
127.0.0.1.80 > 127.0.0.1.48402: Flags [P.], seq 1:233, ack 115, win 342, options [nop,nop,TS val 2968699291 ecr 2968699289], length 232: HTTP: HTTP/1.1 200 OK
127.0.0.1.48402 > 127.0.0.1.80: Flags [.], ack 233, win 350, options [nop,nop,TS val 2968699291 ecr 2968699291], length 0
127.0.0.1.48402 > 127.0.0.1.80: Flags [P.], seq 115:229, ack 233, win 350, options [nop,nop,TS val 2968699292 ecr 2968699291], length 114: HTTP: GET /api/recommend/index HTTP/1.1
127.0.0.1.80 > 127.0.0.1.48402: Flags [.], ack 229, win 342, options [nop,nop,TS val 2968699334 ecr 2968699292], length 0
127.0.0.1.80 > 127.0.0.1.48402: Flags [P.], seq 233:465, ack 229, win 342, options [nop,nop,TS val 2968699335 ecr 2968699292], length 232: HTTP: HTTP/1.1 200 OK
127.0.0.1.48402 > 127.0.0.1.80: Flags [F.], seq 229, ack 465, win 359, options [nop,nop,TS val 2968699335 ecr 2968699335], length 0
127.0.0.1.80 > 127.0.0.1.48402: Flags [F.], seq 465, ack 230, win 342, options [nop,nop,TS val 2968699335 ecr 2968699335], length 0
127.0.0.1.48402 > 127.0.0.1.80: Flags [.], ack 466, win 359, options [nop,nop,TS val 2968699335 ecr 2968699335], length 0

可以看到,两次的请求,都是使用同一个端口,请求完毕了后,curl主动关闭socket,毕竟程序也关闭了
所以客户端->nginx的连接是实现了http keep-alive
而此时,如果注释掉了nginx配置,

   #  proxy_http_version 1.1;
   #  proxy_set_header Connection "";

那么此时再次请求,客户端->nginx连接还是可以keep-alive的,但是nginx->upstream就不行了,因为那是针对ngxin->upstream的配置,客户端跟nginx的配置默认就是能keep-alive

requests测试nginx keepalive 连接

import time
import requests 

for i in range(5):
    r = requests.get('http://127.0.0.1/api/recommend/index?a', headers={"Host": "app-store-server.webapp.163.com", "Connection": "keep-alive"})
    time.sleep(2)

为什么这样请求过去nginx,跟nginx的连接没有复用,每次循环,客户端完了都会发送F.信号,而不会复用连接呢?
这个问题跟一开始我想在终端curl http://xxx 一样想看到第N次curl的时候,都会复用同一个连接,但是上网查了下,使用curl -v curl默认就是http 1.1, connection: keep-alive的,但是curl这个程序结束以后,就会直接关闭socket,这也很正常,程序都关闭了,socket自然就会关闭。

但是这里的我自己写个程序,循环5次,程序没有关闭,它也会自动关闭socket,没有复用连接,看来是requests的问题。
上网查了下,是requests的问题,它默认不支持keepalive,需要使用requests.session 对象来请求,才能使用上keepalive

浏览器上观察与nginx 的keep-alive

浏览器打开 http://app-store-server.webapp.163.com:8000/api/recommend/index?a 确实可以复用连接,因为http1.1默认开启了connection: keepalive
当connection:keep-alive的头跟随请求到达nginx,nginx就会keepalive,nginx 是否开启keep-alive的配置就是keepalive-timout, 设置成0就是不开启keepalive。
为了校验效果我我把nginx的keepalive_timeout 设置成了10
连续请求3次,tcp抓包看到

# 第一次请求 三次握手建立连接
10.0.2.2.54568 > 10.0.2.15.80: Flags [S], seq 3866240001, win 65535, options [mss 1460], length 0
17:18:47.237953 IP 10.0.2.15.80 > 10.0.2.2.54568: Flags [S.], seq 3002799617, ack 3866240002, win 29200, options [mss 1460], length 0
17:18:47.238366 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [.], ack 1, win 65535, length 0

# 第一次请求
17:18:47.238397 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [P.], seq 1:514, ack 1, win 65535, length 513: HTTP: GET /api/recommend/index?a HTTP/1.1
17:18:47.238402 IP 10.0.2.15.80 > 10.0.2.2.54568: Flags [.], ack 514, win 30016, length 0
17:18:47.241415 IP 10.0.2.15.80 > 10.0.2.2.54568: Flags [P.], seq 1:233, ack 514, win 30016, length 232: HTTP: HTTP/1.1 200 OK
17:18:47.241993 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [.], ack 233, win 65535, length 0

# 第二次
17:18:51.767547 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [P.], seq 514:1027, ack 233, win 65535, length 513: HTTP: GET /api/recommend/index?a HTTP/1.1
17:18:51.770731 IP 10.0.2.15.80 > 10.0.2.2.54568: Flags [P.], seq 233:465, ack 1027, win 31088, length 232: HTTP: HTTP/1.1 200 OK
17:18:51.771010 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [.], ack 465, win 65535, length 0

# 第三次
17:18:54.606365 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [P.], seq 1027:1540, ack 465, win 65535, length 513: HTTP: GET /api/recommend/index?a HTTP/1.1
17:18:54.609627 IP 10.0.2.15.80 > 10.0.2.2.54568: Flags [P.], seq 465:697, ack 1540, win 32160, length 232: HTTP: HTTP/1.1 200 OK
17:18:54.610215 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [.], ack 697, win 65535, length 0

# 等待10秒后 nginx发送Fin信号
17:19:04.618932 IP 10.0.2.15.80 > 10.0.2.2.54568: Flags [F.], seq 697, ack 1540, win 32160, length 0
17:19:04.619351 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [.], ack 698, win 65535, length 0

# 这里有点疑惑了, 浏览器没有发送Fin信号,反而是R.
17:21:04.620415 IP 10.0.2.2.54568 > 10.0.2.15.80: Flags [R.], seq 1540, ack 698, win 65535, length 0

等待10秒后,nginx发送F.信号了,符合我们预期,然后浏览器也立马返回ack信号了。
但是浏览器居然没有发送F.给nginx,这时后的80端端口处于 FIN_WAIT2状态

tcp        0      0 10.0.2.15:80            10.0.2.2:57624          FIN_WAIT2   -    

为什么浏览器没有及时发送F.信号呢?而是在两分钟后,浏览器发送R.信号给nginx,这时的连接才被完全关闭了。

在nginx还处于FIN_WAIT2的时候,也就是说client没想过要发送Fin信号,是因为它认为自己还有数据要发,所以不关闭吗?然后设置了2分钟超时时间,发送R信号,重置连接。 那么这样服务器不是一直得占用了一个端口处于FIN_WAIT2状态吗?这不是很浪费吗?
在nginx还处于FIN_WAIT2的时候,我让浏览器继续发送同一个请求过去nginx,看看会怎样的,抓包看到

# 浏览器继续像正常情况下发送请求
10.0.2.2.58500 > 10.0.2.15.80: Flags [P.], seq 514:1027, ack 234, win 65535, length 513: HTTP: GET /api/recommend/index?a HTTP/1.1
# nginx 由于之前已经发送Fin信号,说明它已经想关闭连接了,这里再次收到数据后,发送一个重置标识 
18:33:39.452057 IP 10.0.2.15.80 > 10.0.2.2.58500: Flags [R], seq 1177597640, win 0, length 0
# 这个时候浏览器才走正常流程,发送F.信号。
18:33:39.452155 IP 10.0.2.2.58500 > 10.0.2.15.80: Flags [F.], seq 1027, ack 234, win 65535, length 0
# nginx 看到F.信号,没有鸟它,毕竟我之前发送了R.信号,破镜不能重圆,只能继续发送R信号
18:33:39.452163 IP 10.0.2.15.80 > 10.0.2.2.58500: Flags [R], seq 1177597640, win 0, length 0
# 浏览器收到后,好吧,那就结束把!
18:33:39.452304 IP 10.0.2.2.58500 > 10.0.2.15.80: Flags [R.], seq 4103854591, ack 234, win 0, length 0
18:33:39.452835 IP 10.0.2.2.58509 > 10.0.2.15.80: Flags [S], seq 193480705, win 65535, options [mss 1460], length 0
18:33:39.452857 IP 10.0.2.15.80 > 10.0.2.2.58509: Flags [S.], seq 1631094942, ack 193480706, win 29200, options [mss 1460], length 0
18:33:39.452981 IP 10.0.2.2.58509 > 10.0.2.15.80: Flags [.], ack 1, win 65535, length 0
18:33:39.453059 IP 10.0.2.2.58509 > 10.0.2.15.80: Flags [P.], seq 1:514, ack 1, win 65535, length 513: HTTP: GET /api/recommend/index?a HTTP/1.1

上面的流程:

  • 浏览器继续向正常情况下发送请求
  • nginx 由于之前已经发送Fin信号,说明它已经想关闭连接了,这里再次收到数据后,发送一个重置标识
  • 这个时候浏览器才走正常流程,发送F.信号。
  • nginx 看到F.信号,没有鸟它,毕竟我之前发送了R.信号,破镜不能重圆,只能继续发送R信号
  • 浏览器收到后,好吧,那就结束把!
  • 下面就重新开端口,开启新的一个三次握手,确定新连接

测试过程中还碰到一种情况是,nginx keepalive 超时后,发送FIN给客户端, 这时客户端再次请求,就走正常流程了,向nginx发送FIN.信号,nginx也回应ack信号。

18:27:36.692101 IP 10.0.2.15.80 > 10.0.2.2.59264: Flags [F.], seq 929, ack 2477, win 34045, length 0
18:27:36.692546 IP 10.0.2.2.59264 > 10.0.2.15.80: Flags [.], ack 930, win 65535, length 0

# nginx发送F后,我在浏览器继续发送同一个请求,
18:27:40.712616 IP 10.0.2.2.59264 > 10.0.2.15.80: Flags [F.], seq 2477, ack 930, win 65535, length 0
18:27:40.712675 IP 10.0.2.15.80 > 10.0.2.2.59264: Flags [.], ack 2478, win 34045, length 0
... 连接关闭后,下个请求就重新建立新的请求了

经过上面的测试,思考几个问题:

  • nginx 80端口处于FIN_WAIT2的时候,如果长时间不请求,则会在client的超时(上面2分钟)后发送R信号关闭连接?这个是为什么? client为什么不返回FIN?
  • 有时候client是正常发送Fin关闭连接,有时候是发送RST信号关闭连接?
  • nginx 80端口处于FIN_WAIT2的时候,如果继续请求,则会重置完连接后,重新启动新的连接三次握手,正常请求,然后FIN_WAIT2也会消失
  • 我发现同一个电脑,不同电脑发送请求,都会用同一个连接,难到不是一个浏览器各自独立的 吗?难道是NAT网络的影响?还是长连接根本就是全局管理的?
  • ngxin使用其他协议,比如使用uwsgi_pass ,fastcgi_pass转发请求到upstream的话,怎么保持keepalive?
  • ack 机制,我发现了只有3次握手,和4次握手的时候,ack是加1的,如果正常传输数据,ack就是上一个包最后的seq数字,不会加1。
  • 程序关闭,比如客户端的curl,关闭了就一定会发送关闭连接的请求吗?端口依附在程序上面的吗
  • 在nginx keepalive 超时的时候,发送FIN给浏览器的时候,浏览器立刻回应了ack,但是浏览器却没有恢复FIN信号给nginx呢 ?而是在两分钟后,浏览器发送R.信号给nginx,这时的连接才被完全关闭了。
    在nginx还处于FIN_WAIT2的时候,也就是说client没想过要发送Fin信号,是因为它认为自己还有数据要发,所以不关闭吗?然后设置了2分钟超时时间,发送R信号,重置连接。 那么这样服务器不是一直得占用了一个端口处于FIN_WAIT2状态吗?这不是很浪费吗?

参考

TCP keepalive的探究 (2) : 浏览器的Keepalive机制
TCP保活(TCP keepalive)
tcpdump flags
HTTP Keep-Alive是什么?如何工作?
超时与重试机制(一)

FIN_WAIT2 状态https://my.oschina.net/airship/blog/2875176
curl keep-alive https://serverfault.com/questions/199434/how-do-i-make-curl-use-keepalive-from-the-command-line
requests的keep-alive http://xiaorui.cc/2017/04/03/%E6%9E%84%E5%BB%BA%E9%AB%98%E6%95%88%E7%9A%84python-requests%E9%95%BF%E8%BF%9E%E6%8E%A5%E6%B1%A0/
rst包攻击 https://blog.csdn.net/russell_tao/article/details/7228923
TCP的ack机制 http://xstarcd.github.io/wiki/shell/TCP_ACK.html
为什么基于TCP的应用需要心跳包 http://hengyunabc.github.io/why-we-need-heartbeat/
https://www.cnblogs.com/hukey/p/5481173.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容