TCP协议浅谈

Posted 一苇可航的运维故事

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP协议浅谈相关的知识,希望对你有一定的参考价值。

古语说的好,各人自扫门前雪莫管他人瓦上霜;这句话用在OSI协议模型上最合适不过了。你说二层协议它就是妥妥的负责转发,才不管你是否500次。三层就是存路由功能,管你GFW是个啥东西。四层TCP就是负责流控和状态的,管你是不是地主呢。其实我觉得这样的设计真是不错,各司其职,永不越界。就算隔壁的老王在NB,也得按部就班遵照执行。

TCP协议本身是一个特别复杂的协议,从实现算法到数据结构到其思想足足可以用一本书来描述清楚(TCP/IP协议卷一),里面的细节非常繁杂。在书写的时候才发现傻了,东西过于繁多。哎!谁让在群里吹了牛逼。那么我就尽力用白话,实例化的形式来浅谈一下。

TCP格式
这也是基础的基础,大家现在的脑海里可以浮现出来吗? 如果没有,那么请你去牡丹园B口进出100次!
其实简单的说来就是包含 source port, dst port, sn, ack id, windows, tcp flags等。 那么这些东东的实际意义又是啥呢?

source prot 很简单啦。dst port 更简单啦。

sn( sequence number) 就是包的序号咯。比如发送方一共编号了1-10个包,给每个包都贴了一个序号,然后接收端可以根据发送端的序号来确认顺序已经是否丢失等。

ack 就是确认嘛。确保每一个都是处女座的。否则....
win 就是声明自己的接收场所有多大啦。 比如可以容纳100个MSS,还是1个MSS。
tcp flags 就是状态机鸟~ 什么time_wait太多了~~ 要挂了。什么fin_wait又是什么鬼等。
当然还有一些其他的信息,这里不做重点描述咯。

TCP协议的状态
协议状态也是非常重要的。我们可以清晰的看到一个socket连接在何种状态,对于服务本身来说意义巨大。
比如有CC攻击来了,我们的状态显示为syn_revice。是因为我们发了ack给对方我们变成的状态,而对方根本是不存在的IP,所以我们这个半连接状态就存在了。就赌了马路了,
那位兄弟说了,那为啥不直接关闭呢。你这话完全不负责任,因为TCP协议是保障状态的链接,每一个数据包都要保证收到/Ack,所以在收不到ack的情况下,它就会死等,死等。一直等到重传定时器溢出这才拉倒。

那么在协议状态里我觉得关注2个问题:


第一) 为什么会有 2MSL这个时间存在
关闭TCP连接需要四次握手,这是因为TCP是全双工的服务,要关掉就是两边一起。当在ES状态的传完数据的时候,这个时候C会发送FIN包关闭这个链接。
1)C发完fin包,状态变成fin-wait-1
2)C收到对方的fin/ack包状态迁移成closing状态
3)C对ack进行确认,状态由closing状态变成time-wait状态
此时,TIME-WIAT的时间会保存2MSL时间,LINUX默认定义是30S(目前很多内核都Hook过这个时间,变得更短), 也是造成time-wait过多的一个主要原因。那么为什么会存在2MSL呢?
因为第三步的时候,假设C发送的ACK包丢失。如果不等2MSL时间那么当被动端等不到C发过来的ACK就会超时重发,然而这个时候对方的C已经在CLOSE状态了,收到的包就会触发RST造成异常中断造成不正常的状态关闭。

第二) syn/ack 超时和数据ack超时
syn包就是三次确守的初始化包,当这个包ack丢失的时候。会触发重发机制,重发时间大概为3S(应该是RTO默认的时间)这样的间隔。
特别当backlog队列满的时候(一个存放syn包的队列),syn包开始溢出。这个时候用tcpdump抓包就可以看到重试时间符合上面的规律。大概重试63S这个连接就会被断开了。
那么数据ACK超时跟这个有着本质不同。数据ACK会触发重传定时器重传或者快速重传的方式来进行。因为这里涉及到了数据。而不是在握手的状态。

窗口(win)
TCP协议的窗口意义非凡,同样也是设计精妙。主要包含两种窗口算法,一种是流控窗口(cwnd),一种是宣告窗口(rwnd)。
宣告窗口是就是告诉对方,我可以接收多少个MSS分组。 这样发送方就可以根据对方给予的宣告窗口来来发送数据。虽然看上去这是一种很好的协商机制,但是从网络的复杂性来说还是有问题的。
因为,假设两个跨地域的服务器在传输数据,可能中间要经历无数个路由,窗口宣告只是宣告了你我的接收大小,完全忽略了中间路由的接收大小。这样很有可能会造成中间网络过载。

流控窗口这个时候就显得大有用途了。流控窗口的实际意义就是根据实际的网络状况进行窗口的调整。这里有会引入了一个”慢启动“的概念。就是当开始传输数据的时候,窗口期处在慢启动状态,这个时候基本上就是一个MSS分组大小,随着网络反馈良好,窗口开始指数级的增长来适应当前的网络负载。也就是说可能在3个RTT以后,窗口就会增长到4个MSS大小了。 但是当网络又出现延时的时候,这个时候就会进入到 避免拥塞阶段,MSS可能是线性的增长了。所以说这是一个智能的调控过程。

TCP分片和IP分片
当初TCP协议设计是没有TCP分片,会有IP分片。当一个数据包大小超过MTU值的时候就会造成IP分片。IP分片会造成很大的一个问题就是当一个分片丢失的时候会重发所有的分片。效率非常低下,所以TCP协议提出了一个概念就是 MSS,最大报文长度。他的意思就是说在一个TCP连接上。一个主机到对方主机可以传输的最大的报文长度。这里一般是1460字节。所以来看是小于MTU的所以不会发生IP分片的事情。

丢包重传的方式
数据包丢失是很常见的事情,比如网络颠簸,比如路由器缓存等。都会导致数据包的丢失。而TCP协议本身是保证协议安全的。所以必须有重传机制。
早期的重传机制就是两个字死等。比如A给B发送1-10数据包,比如第五个包丢失了那么这个时候B就会不发送5包的ACK死等,这个时候当A接收不到 5/ACK的时候就会等超时重传定时器溢出在重发包5,显然这样的重传效率很低。因为RTO(重传计时器)默认是3S。

所以后来内核开始支持early retransmit就是快速重传。快速重传的本质思想就是连续同样的ACK。 还是上面的例子第五个数据包丢失,会快速ACK给对方。这个时候假设6,7一起到了。那么也快速ACK 5,这个时候就知道是发送的数据包5丢失了,所以重传数据包5. 所以基本上是不等RTO计时溢出就已经重传完毕了。

那么快速重传在哪些情况下会失效呢?
好好想,使劲想,往死里想..... 领悟出来了吗?

第一,当tcp流控窗口小于4的时候,这个时候是不生效的。 因为这个时候只能容纳3个MSS大小。 当其中的一个报文段丢失了那么剩下的两个位置根本无法容纳快速回来的ACK报文。所以这个时候快速重传没有意义。

第二,在三次握手的时候因为三次握手,只有两次是主动出发的。syn和syn/ack那么这个时候根本不会触发三个ACK的快速重传要求,所以也不会生效。只能等RTO超时。

那么还有一个重要的问题,就是在快速重传机制生效以后,它是发丢失的包还是发送丢失包以后的所有包呢?
因为发送方并不知道这个连续的3个ACK是谁发回来的,所以还需要一个更好的方法来保障数据碎片。所以出现了一种更好的方式叫SACK。
举个例子(举得好累,求吃饭~)比如A给B传送了1000个数据报文。那么当在105-108的时候数据报文丢失,这个时候会触发快速重传马上ACK了109,并SACK带上110-121的数据报文ACK。(110-121数据已经到达)那么这个时候A就知道105-108的数据丢失,需要重传。


奇异的backlog
backlog到底有多深,你爱我有几分!
其实嘛,backlog在协议栈里出现的次数很多,意义也不同。主要的有如下几种:
亲们可要分清楚哦~~~~

listen backlog:
一个socket服务在监听状态下的队列长度,也就是说存放syn队列的长度。受限系统参数和listen系统调用参数调整。

tcp process backlog:
这个是TCP协议栈的一个backlog队列,它的主要意义是社么呢?
就是当有乱序的数据包过来的时候,TCP PROCESS 会把乱序的数据包暂存在这个队列里。

max_dev_backlog:
这个家伙是网络报文发送到CPU队列的最大长度。

发送队列
这个队列呢其实就是net.ipv4.tcp_wmem,什么是它?
的确确实这个东西,我们所有的窗口调控,慢启动,数据发送都是放到这个队列里哦。 这个队列存放的其实TCP报文文段就是MSS啦。 能存放多少完全靠的是cwnd和rwnd的宣告了。在当一个用户态程序发送数据的时候,从用户态拷贝到内核态的时候就会把数据流分成MSS1,MMS2.。。个大小存在这个wmem内。对应的数据结构就是sk_buff。


实例化一个数据包的发送过程:
1 你的app server会调用syscall. 这里来说用send来发送1M的数据。
2 通过中断陷入到tcp_sendmsg来帮你完成。
3 在从用户态把数据拷贝到内核态的时候,会把1M的数据流进行分组MSS,然后把若干个MSS用sk_buff结构来存放。
4 同时呢把这些分组组成一个发送队列就是前面提到的 net.ipv4.tcp_wmem 大小。
5 这里假设默认的队列不够了,会怎么样呢? 其实会触发wait_memory来等待窗口滑动,释放内存。
6 当TCP准备就绪,开始tcp_push 调用ip方法推送队列里的数据。
7 一开始TCP会采用cwnd和rwnd最小值来确定初始窗口的大小,随着网络负载的反馈流控窗口出现变化
8 一个窗口内携带N多MSS分组通过IP层发送出去
9 当出现丢包以后,触发快速重传算法,发送端判断ACK和SACK 给予丢失数据重传。
10 所有数据传输完毕,返回成功。


如上跟大家浅谈了TCP协议的相关内容。这些都是都是杯水车薪,也是点滴之谈。希望通过这个总结可以让大家对TCP有一些更多的了解。学无止境大家如果对TCP协议有更多的兴趣可以参考TCP/IP协议卷一 和 RFC协议去详细了解。那里有最真实的东西!


以上是关于TCP协议浅谈的主要内容,如果未能解决你的问题,请参考以下文章

浅谈 TCP 协议

TCP协议浅谈

必看浅谈TCP协议连接的11种状态

浅谈http协议(如何TCP实现HTTP)

浅谈TCP IP协议栈IP协议解析

浅谈传输层协议TCP和UDP