TCP协议

前言

TCP协议是一个复杂而又重要的协议,在TCP/IP协议族中具有极其重要的位置。有多重要?看看命名就懂了。

它位于 OSI 7层模型中的第四层(Transport Layer),这一层的数据叫做 Segment。发送数据时,这一层的数据会切割放入第三层(Network Layer)IP协议的Packet中,再放入第二层(Ethernet Layer)的Frame中。传输到对端后,则由底向上地解包组装。

TCP Header

image.png

如上图所示,TCP头部主要由20字节的固定头部和可变长度的非固定头部组成,具体分析一下固定头部的字段含义:

  1. Source Port和 Destination Port: TCP层只有两个端口值,加上IP层的Source IP、Destination IP以及TCP协议,构成一个五元组,可以唯一确定一个连接。
  2. Sequence Number:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号,用来解决网络包乱序(reordering)问题。
  3. Acknowledgement Number:就是ACK,用于确认收到,用来解决不丢包的问题。
  4. Offset:头部偏移量,标识该TCP头部有多少个4字节。这个值占四个字节,因此头部最长为60字节。
  5. Reserved: 6位保留值,置为0。
  6. TCP Flags:表示包的类型,用于操控TCP的状态机,其中:
    URG 紧急指针是否有效;
    ACK 表示确认号是否有效;
    PSH 提示接收端应用程序应该立即从TCP接收缓冲区读走数据;
    RST 表示要求对方重新建立连接;
    SYN 表示请求建立一个连接;
    FIN 表示通知对方本端要关闭连接;
  7. Window: 表示窗口的容量,用于解决流控的。16位,因此TCP标准窗口最大为2^16 - 1 = 65535个字节。
  8. Checksum: 由发送端填充,接收端对报文段执行CRC算法,用以检验TCP报文段在传输中是否损坏。
  9. Urgent Pointer:一个正偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。

TCP状态机

所谓的连接,其实就是发送端与接收端对于状态的一种维护,当两者都处于连接状态,并且五元组是一样的情况下,就可以理解为“建立了连接”。

先祭出一张状态机转换图


图1. 状态机转换

再祭出一张TCP建连、发送数据、断开的流程图。


图2. TCP流程图

解释一下其中的一些point。

SEQ和ACK

SEQ和ACK是TCP协议可靠性(有序性)保证的关键值。SEQ也就上图中的 x 和 y。SEQ在SYN时由各端生成,作为以后的数据通信的序号。对端发送回应报文时会将ACK值置为收到的连续SEQ最大值+1,也就是期望收到的下一个数据包的号码。

举个🌰
假设A端在不停地发送数据包,B端接收到的SEQ为[0,1,4,5],B发送的ACK值分别为[1,2,2,2],此时B收到了3号数据包,依然会发送ACK为2的回应报文,当B收到2号数据包时,则回复报文中ACK置为6。

快速重传

在上面的🌰中,A要如何重传数据呢?有两种选择,一种是只重传timeout的包,另一种是把timeout包以及后面的包都重传一次。第一种会节省带宽,但是慢,因为如果真的网络不好,可能后面的每一个包都没有收到,每一个包都要等超时后再重传;第二种会快一点,但是会浪费带宽,也可能会有无用功。但总体来说都不好。因为都在等timeout,timeout可能会很长。因此,TCP引入了快速重传机制。

Fast Retransmit :如果发送方连续收到3次相同的ack,就重传。这种机制不和时间挂钩,而是由数据驱动,解决了timeout的问题。而重传哪些数据则由另一个机制 SACK来实现。

Selective Acknowledgment (SACK):在TCP头OPTION段里加一个SACK,ACK还是Fast Retransmit的ACK,SACK用于汇报收到的数据碎版,这样就只需要重传丢掉的包即可。首先,2个设备必须同时支持SACK,建立连接的时候需要使用SACK Permitted的option,如果允许,后续的传输过程中TCP segment中的可以携带SACK option,这个option内容包含一系列的非连续的没有确认的数据的seq range。
TBD

滑动窗口

数据传输阶段发送真正的数据内容,一端可以连续发送多个数据包而不需要等待对端对应的ACK,还能接收多少个数据包,是由包头中的Window字段决定的,也就是著名的滑动窗口算法。
由于TCP是全双工通信,两端都维护了一套滑动窗口。我们只需要研究其中一端即可。
滑动窗口分为发送窗口和接收窗口。对于发送窗口,祭上一张图:


图3. 滑动窗口

一共分为4块:

  1. Sent and Acknowledged:这些数据表示已经发送成功并已经被确认的数据,比如图中的前31个bytes,这些数据其实的位置是在窗口之外了,因为窗口内顺序最低的被确认之后,窗口要向右移动相应的偏移量。
  2. Send But Not Yet Acknowledged:这部分数据称为发送但没有被确认,数据被发送出去,没有收到接收端的ACK,认为并没有完成发送,这个属于窗口内的数据。
  3. Not Sent,Recipient Ready to Receive:这部分是尽快发送的数据,这部分数据已经被加载到缓存中等待发送,也在窗口中。其实这段窗口接收方是有能力接受这些包的,所以发送方需要尽快的发送这些包。
  4. Not Sent,Recipient Not Ready to Receive: 这些数据属于未发送,同时接收端也不允许发送的,因为这些数据已经超出了接收的范围。

假设发送端收到了ACK=37的TCP包,则窗口会变为如下模样:


图4.滑动窗口2

对于接收窗口,则分为3块:

  1. Received and ACK Not Send to Process:这部分数据属于接收了数据但是还没有被上层的应用程序接收,被缓存在窗口内。
  2. Received Not ACK: 已经接收,但是还没有回复ACK,这些包可能输属于Delay ACK的范畴了。
  3. Not Received:有空位,还没有被接收的数据。

ATTENTION
滑动窗口的存在,导致一种攻击模式的产生:TCP零窗口攻击。

首先引入Zero Window的概念:当发送方的发送速度大于接收方的处理速度,接收方的缓冲塞满后,就会告诉发送方当前窗口size=0,请停止发送,发送方此时会停止发送数据。这样这个TCP连接就会被一直hold住。

接收方的Window size大于0后如何通知发送方呢?TCP使用ZWP (Zero Window Probe)技术,即发送方发现Window为0后,想接收端发送ZWP包,一般这个值会发送3次,每次间隔30-60s,如果一直是0,可能有的TCP实现会发送RST主动断开连接。这里为什么不让接收端等待窗口可用后主动上报呢?其实就是为了解决TCP零窗口攻击。

如果有攻击者发送大量0窗口的连接(DDoS攻击),服务端的接口资源就会被耗尽,想让攻击者主动上报窗口变更是不可能的,因此改用发送端主动询问的方式。

Congestion Handling 拥塞控制

TCP的设计中,流控(Flow Control)除了使用滑动窗口外,还根据更下层的网络的情况,做了一些控制。如果网络时延增加,TCP只会不断重试,考虑到网络内有无数个TCP连接,这样只会加重网络拥塞情况,最终拖垮整个网络。因此,TCP提出了四个算法:1)慢启动,2)拥塞避免,3)拥塞发生,4)快速恢复。具体论文参考引用3。
TBD

MSL

TCP Segment在网络上的存活时间最大值为Maximum Segment Lifetime(MSL)。

为什么在四次挥手中客户端要在TIME_WAIT状态后等待2MSL的时间才进入closed的状态?因为需要确认被动端接收到了最后一次ACK包,如果被动端未接收到ACK包,会重发FIN指令,为了保证重试的FIN指令一定能够收到,因此需要等待2MSL的时间。

由此可以推测,我们期望由客户端主动关闭这个全双工连接,否则服务端需要维持很多处在TIME_WAIT阶段的连接,非常消耗资源。

那么服务端如何处理很多TIME_WAIT状态连接的情况呢?有两个参数,分别是tcp_tw_reuse和tcp_tw_recycle。具体使用方式参考引用2。但是打开这两个参数是一个违反TCP协议的危险操作。此外还有一个参数tcp_max_tw_buckets。这个是控制并发的TIME_WAIT的数量,默认值是180000,如果超限,那么,系统会把多的给destory掉,然后在日志里打一个警告(如:time wait bucket table overflow),官网文档说这个参数是用来对抗DDoS攻击的。可以根据实际情况修改。

sequence number如何生成

ISN是不能hard code的,不然会出问题的。

举个🌰
如果连接建好后始终用1来做ISN,如果client发了30个segment过去,但是网络断了,于是 client重连,又用了1做ISN,但是之前连接的那些包到了,于是就被当成了新连接的包,此时,client的Sequence Number 可能是3,而Server端认为client端的这个号是30了,包的顺序无法保证了。

RFC793中说,ISN会和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。而我们又假设Segment在网络上的存活时间不会超过MSL,所以只要MSL的值小于4.55小时,那么我们就不会重用到ISN。

引用

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