学习笔记:TCP协议3次握手4次挥手

TCP协议结构

TCP协议结构

里面主要部分是包序号和确认序号,包序号是为了解决乱序的问题,知道那个包在前,哪个包在后。确认序号,发出去的包要有个确认,如果没有收到就要重新发送。
状态位

  • SYN:发起一个连接
  • ACK:回复
  • RST: 重新连接
  • FIN:结束连接
    窗口大小,TCP要做流量控制,通信双方各声明一个窗口,标识自己的处理能力,让对端别发送太快,撑死我,也别发送太慢,饿死我

TCP的3次握手

3次握手流程

TCP建立连接时的3次握手也常称为“请求->应答->应答之应答”。为什么是3次呢,因为建连接的双方都要做一个发送和收到确认才能证明双方连接建立了。
3次握手除了建立连接之外,还为了沟通意见事情,就是TCP包序号的问题,这也是SYN的由来,全称Synchronize Sequence Number。双方要确定一个起始的序号,序号不能每次都从1开始,因为从1开始如果掉线又重连,上次的包可能还在路上,那序号就重复了。所以每个连接都要有个不同的起始序号。这个起始序号是随着时间变化的,可以看作一个32位的计数器,每4ms加1,这样到两个包重复,需要4个小时,早就超过上一个的生存时间了(TTL)。
Sequence Number如何增加
sequence number不是每次加1的,而是和传输的字节数有关。如果双方3次握手后seq都是1,这时候A给B发了一个长度1440的包,那seq就变成1440,。B给1440的ACK回1441,证明1440收到了。
状态演变
Server端监听某个端口,处于LISTEN状态。客户端主动发起连接发送SYN,之后处于SYN-SENT状态。服务端收到SYN之后,向客户端发送SYN和ACK,之后服务端处于SYN-RCVD状态。客户端收到服务端的反馈后再发送ACK,之后客户端处于ESTABLISHED状态,服务端收到后,也处于ESTABLISHED状态。
这里有一个问题,如果client发送SYN之后掉线了,服务端发送SYN-ACK后会一直保持住资源,等待客户端回来的ACK。如果没收到,就会重试SYN-ACK,默认重试5次,间隔从1s开始,每次翻倍。所以,总共需要1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,Server端才会结束,释放资源。
SYN flood攻击
client利用server端会等待的特性(63s),不断给server发SYN,发完就下线,把服务端的SYN队列耗尽,让正常的链接请求不能处理。linux处理这个的方法是设置tcp_syncookies参数,当SYN队列满了以后,TCP发SYN-ACK的时候会带上cookie,然后正常的client再ACK的时候带上就可以建立链接,但是这种方式不是常规用法。更通用的方法是调整几个参数,第一个是tcp_synack_retries,用来减少Server端SYN-ACK重试次数;第二个是tcp_max_syn_backlog,增大队列长度;第三个是tcp_abort_on_overflow,处理不过来干脆拒绝连接。

TCP的四次挥手

一方发起关闭请求

四次挥手流程

发起端(A)想断开连接的时候,发送FIN,然后进入FIN_WAIT_1的状态。接收端(B)收到后,发送ACK,表示自己知道了,进入CLOSE_WAIT状态。
A收到B的确认后进入FIN_WAIT_2的状态,如果这个时候B跑路,A将永远处于这个状态。linux针对这种情况,支持tcp_fin_timeout参数,超时后A就算B跑路了。
如果B没跑路,会再发FIN和ACK,表示我也不玩了,然后B进入LAST-ACK状态。A收到后回复ACK,进入TIME-WAIT状态。而B收到A的ACK后进入CLOSED状态。A等待一段时间也会进入CLOSE就彻底结束了。
A为什么要等一段时间呢,一是为了防止A最后的ACK B没收到,B会要求重发,如果A直接CLOSE,就不会重发了。二是B在发送最后的FIN和ACK之前的数据包可能比这两个包晚到。所以A要等一段再关闭。
A的TIME_WAIT的时间是2个MSL(Max Segment Lifetime)报文最大生存时间。
还有一种情况是B一直没收到最后的ACK,于是要求重发,而A已经过了2MSL了,A会回一个RST表示自己跑路了。

双方都发起断链请求

两端同时断链接

因为TCP是全双工的,所以,发送方和接收方都需要Fin和Ack。只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。

TCP状态机

TCP状态机

扩展问题思考

日常维护中查看服务器所有连接的状态

$netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
#结果实例
TIME_WAIT 814
CLOSE_WAIT 1
FIN_WAIT1 1
ESTABLISHED 634
SYN_RECV 2
LAST_ACK 1

如果web服务器连接出异常,百分之八九十都是下面两种情况:

  • 服务器保持了大量TIME_WAIT状态
  • 服务器保持了大量CLOSE_WAIT状态
    这个时候新的请求就无法被处理了,接着就是大量Too Many Open Files异常
    服务器保持了大量TIME_WAIT怎么解决
    这个是服务器主动发起关闭连接引起,所以要调整的是本端服务器的参数,修改/etc/sysctl.conf
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间   
net.ipv4.tcp_syn_retries=2  
#net.ipv4.tcp_synack_retries=2  
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒  
net.ipv4.tcp_keepalive_time=1200  
net.ipv4.tcp_orphan_retries=3  
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间  
net.ipv4.tcp_fin_timeout=30    
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。  
net.ipv4.tcp_max_syn_backlog = 4096  
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭  
net.ipv4.tcp_syncookies = 1  
  
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭  
net.ipv4.tcp_tw_reuse = 1  
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭  
net.ipv4.tcp_tw_recycle = 1  
  
##减少超时前的探测次数   
net.ipv4.tcp_keepalive_probes=5   
##优化网络设备接收队列   
net.core.netdev_max_backlog=3000 

这里头主要注意到的是net.ipv4.tcp_tw_reuse
net.ipv4.tcp_tw_recycle
net.ipv4.tcp_fin_timeout
net.ipv4.tcp_keepalive_*
这几个参数。

net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。
net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。
net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。
注意tcp_tw_reuse,tcp_tw_recycle开启的风险,用这个是非常危险的:http://blog.csdn.net/wireless_tech/article/details/6405755
所以设置keepAlive是非常重要的。

服务器保持了大量TIME_WAIT怎么解决
如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。

如果你使用的是HttpClient并且你遇到了大量CLOSE_WAIT的情况,那么这篇日志也许对你有用:http://blog.csdn.net/shootyou/article/details/6615051

在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:

服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。

所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。

参考文章:
[极客时间] 趣谈网络协议 --刘超
[CSDN] 再谈应用环境下的TIME_WAIT和CLOSE_WAIT

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

推荐阅读更多精彩内容