广阔天地大有作为

你想拥有什么,就去追求什么

24 Mar 2018

一些TCP基础笔记

本文主要是看书看文章时做的笔记,记录一些TCP相关的基本概念。引用文章来自补充阅读,点击可到原文链接。

概念

MTU:maximum transmission unit最大传输单元

每种网络都不一样,以太网是1500。最小46字节。当数据块大于MTU时,将在发送端IP层进行分片,接收端IP层进行重组。IP分组在网络中传输中出现丢包时,由于IP层没有重传机制,TCP将重传整个报文段而不是丢失的IP分组

PS: 以太网最小数据帧长度为,最小64字节,其中6字节目的地址 、字节6源地址、2字节类型、46字节数据、4字节校验和.

MSS:maximum segment size最大分段大小

MSS是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的时候往往用MTU值代替(需要减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes)所以往往MSS为1460。通讯双方会根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。

MSL:Maximum Segment Lifetime报文最大生存时间

报文在网络上存在的最长时间,超过这个时间报文将被丢弃。在RFC793指出MSL为2分钟,实际应用中常用的是30秒(linux),1分钟和2分钟等。

TTL:Time To Live生存时间

生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个ip数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。

RTT:round-trip time客户到服务器往返所花时间

RTT由三部分组成:链路的传播时间(propagation delay)、末端系统的处理时间、 路由器缓存中的排队和处理时间(queuing delay)。 其中,前两个部分的值对于一个TCP连接相对固定,路由器缓存中的排队和处理时间会随着整个网络拥塞程度 的变化而变化。所以RTT的变化在一定程度上反应了网络的拥塞程度。

RTO:Retransmission TimeOut重传超时时间

重传机制依赖于RTT(Round Trip Time)的测量,从而计算RTO(Retransmission Timeout)。

TSO:TCP Segment Offload

是一种利用网卡的处理能力,降低CPU发送数据包负载的技术。对于支持TSO的网卡,TCP协议栈在封包的时候会逐渐尝试增大MSS,网卡接收到TCP向下递交的数据后,按照MTU进行分片、复制TCP头且重新计算校验和,这样在网卡上完成了对大块数据的TCP分段,缓解了CPU的计算压力。

查看是否开启TSO

  • sudo ethtool -k eth0

关闭和打开TSO

  • $ sudo ethtool -K eth0 tso off // 关闭tso

  • $ sudo ethtool -K eth0 tso on // 开启tso

TCP协议结构

Alt text

可靠性

TCP被称为可靠的传输协议,所谓的可靠其实是相对UDP的不可靠而言。不是保证数据一定被对方接收。而是提供数据的可靠传递以及故障的可靠通知。

从TCP头中的几个字段来解释为什么TCP是可靠的。

  1. seq num:确保接收端收到的报文有序,并检测丢失分组和冗余分组
  2. ack num:确保接收端正确收到分组,以及期望下一个分组
  3. checksum:校验数据是否篡改
  4. window:流量控制解决接收端处理数据不及时导致接收缓冲区被填满后丢失数据

数据在各网络层的封装

Alt text

补充阅读

理解sequence number acknowledgment nuber

漫谈TCP

三次握手、四次挥手

Alt text Alt text

上面两个图来从不同的角度来看TCP的状态转换。上图来自经典的unp,比较完整的表述了状态转换,下图来自网上,则通过三次握手四次挥手客户端与服务端状态的变化来表述。

三次握手

四次挥手

与TCP连接不一样的,断开的时候需要四次挥手。 从上面的状态转移图可以看出,四次挥手要比三次握手复杂。

2MSL

2MSL是为了确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到Ack,就会触发被动端重发Fin,一来一去正好2个MSL。

由于2MSL的存在,在TIME_WAIT状态下该连接涉及的客户端和服务端端口不能被使用,为了绕过此限制的方法:SO_REUSEADDR(详细介绍见后续章节)。通过设置此值,TCP服务器能够快速成功重启;对于多网卡客户端,在对长连接服务器压力测试时,通过设置此值绑定相同端口到多个网卡提高单机QPS。

为什么是三次握手

三次握手为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。比如client发送了一个请求,但是在网络上超时了,因此又发送了一个同样的请求,server收到以后给予回复。但是此时刚才无效的请求又过来了,服务端再次回复了,这就造成了错误。

为什么是四次挥手

为了确保数据能完整的传输。关闭连接时,收到对方FIN报文,表示对方数据发送完了,并不表示接收端所有的数据都发送完了,所以未必会立马关闭SOCKET,可以在发送完数据后,再发送FIN报文给对方表示现在可以关闭连接了。所以在这里ACK和FIN是分开发送的。

另一种解释:这是因为TCP的半关闭特性导致的。所谓的半关闭,是指TCP提供了连接的一端在结束它的数据发送后仍能接收来自对端数据的能力。半关闭也可以理解为主动关闭方完成了写数据操作,但仍可以进行读操作。这也是TCP被称为全双工协议的原因。为了实现此特性,TCP实现中提供了一个shutdown函数。虽然存在半关闭特性,但实际的应用中却很少使用,基本上都是简单粗暴的通过调用close函数来结束两个方向上的连接,而不是shutdown。

补充阅读

TIME_WAIT和CLOSE_WAIT

漫谈TCP

TCP的那些事儿

重传与拥塞

重传机制

重传是可靠性的保证,是发送端感知到网络出现丢包,主动发起重传。发送端是如何感知的呢?用户主动确认+超时机制,接收端通过ack来告知发送端前面的ack-1个字节已经被接收,未被确认的报文在超时后进行重传。

重传机制依赖于RTT(Round Trip Time)的测量,从而计算RTO(Retransmission Timeout)。

先假设一个场景,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到),此时的TCP会怎么办?我们要知道,因为SeqNum和Ack是以字节数为单位,所以ack的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端就以为之前的都收到了。

超时重传

请求包发出去的时候,开启一个计时器,当计时器达到时间(RTO)之后,没有收到ACK,则就进行重发请求的操作,一直重发直到达到重发上限次数或者收到ACK。

超时重传也有两种选择

快速重传机制

当接收方收到的数据包是不正常的序列号,那么接收方会重复把应该收到的那一条ACK重复发送,这个时候,如果发送方收到连续3条的同一个序列号的ACK,那么就会启动快速重传机制,不需要等到timeout,把这个ACK对应的发送包重新发送一次。 Alt text 发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。

滑动窗口

TCP滑动窗口主要是提供TCP的流控特性,解决接收端处理数据不及时导致接收缓冲区被填满后丢失数据的问题。 TCP头中的window字段,是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。 Alt text

从上图中看出,把发送数据横拍做长列状,发送方一但有数据收到ACK,那么滑动窗口左侧边就进行左移。同样,一旦接收方有数据被应用层消费,那么,滑动窗口的右侧边就进行右移。

  1. 已发送并得到对方ACK;
  2. 已发送未收到ACK;
  3. 未发送但对方允许发送;
  4. 未发送,对方不允许发送。

其中已发送未收到ACK和未发送但对方允许发送的数据称为发送窗口

  1. 已接收;
  2. 未接收,准备接收;
  3. 未接收,并不准备接收。

其中未接收,准备接收的数据称为接收窗口

一旦滑动窗口大小缩小为0,发送端将停止发送数据,等待接收端新接收窗口值(大于0)的到来以移动滑动窗口的右边沿。上述滑动窗口机制自然能抑制发送端的发包速率,但同时引入了糊涂窗口综合症。

糊涂窗口综合症(Silly Window Syndrome)是指接收方通告一个较小的窗口,而发送方发送少量的数据的现象。要解决这个问题也不难,就是避免对小的window size做出响应,直到有足够大的window size再响应。

拥塞处理

如果网络不佳的情况下,接收端可能会因为网络包拥塞而无法接收到,而根据重传的特性发送端会在RTO时间后重传数据,这样更加加剧数据拥塞。解决办法有慢启动、拥塞避免、快速重传与快速恢复

发送方维护了两个窗口:拥塞窗口和滑动窗口。两者都是试图对发送窗口大小进行控制的,自然发送窗口大小=min{滑动窗口大小,拥塞窗口大小}。当无网络拥塞发生时,滑动窗口大小一般小于拥塞窗口大小。

慢启动

慢启动的意思是,刚刚加入网络的连接,一点一点地提速,其实并不慢,拥塞窗口大小呈指数上升。

拥塞避免算法

慢启动使得cwnd是呈指数增长。一定不可能是无限增长的,这里就有个阀值,超过这个阀值,就进入拥塞避免算法。 拥塞避免算法说的是拥塞窗口的增加不再是“每收到一个ACK,拥塞窗口就增加一个报文段”。 而是“每收到一个ACK,cwnd = cwnd + 1/cwnd”

判断拥塞

  1. 超时重传

发出去一个包,超时定时器就开始计时,当超时定时器到时间之后,没有收到ACK,那么这个时候就判断为拥堵了,需要进行重传。TCP会直接把cwnd调整为1,sshthread 调整为cwnd/2,重新进入到慢启动流程。

  1. 快速重传

比如5个请求,但是第2个请求丢失了,第1、3、4、5请求到了接收端,3、4、5请求触发了三个ACK返回,但是由于接收端没有收到请求1,返回的三个ACK都是ACK1的,所以发送方就表现为收到重复ACK。,当连续收到三条重复ACK的时候就进行重传,不需要等待重传计时器。这个时候TCP会觉得网络还是可以的,反应不会那么激烈,cwnd调整为cwnd/2, sshthresh调整为cwnd大小,进入快速恢复算法。

快速恢复

快速恢复算法是为了不要有一个重传就那么大响应。能尽快恢复到网络流畅时候稳定的状态。

补充阅读

TCP的阻塞和重传机制

TCP的那些事儿(上)

TCP的那些事儿(下)

漫谈TCP