1.TCP如何实现可靠链接
与UDP不同的是,TCP提供了一种面向连接的、可靠的字节流服务。TCP是如何实现可靠连接?
TCP是实现可靠连接的机制是:主要通过检验和、序列号、确认应答(ACK)、重发控制、连接管理、窗口控制等实现可靠性连接。
连接管理:建立连接的三次握手和断开连接的四次挥手。
数据分段:应用数据分割成TCP认为最适合发送的数据块。以段为单位发送数据包,在建立TCP连接的同时,两端协商发送数据包的单位,称为“最大消息长度”:MSS。 【TCP数据(MSS字节)】【TCP首部(20字节)】【IP首部(20字节)】。值得注意的是,MSS只能出现在SYN报文段中,若一方不接收来自另一方的MSS值,则MSS就定为536字节。一般来讲,在不出现分段的情况下,MSS值还是越大越好,这样可以提高网络的利用率。
校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。(发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段)。
序列号:TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
确认应答:接收方收到报文就会确认(累积确认:对所有按序接收的数据的确认)。
重发控制:当TCP发出一个段后,它启动一个定时器,等待接收方确认收到这个报文段。如果不能及时收到一个确认ACK,将重发这个报文段。一旦开始重传,下一次等待的时间间隔指数增长,重发一定次数后还是收不到ACK信号,将强制终止连接。
流量控制:TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,就把窗口缩小,并把窗口window值告诉发送端(提示发送方降低发送的速率,防止包丢失)。
TCP使用的流量控制协议是可变大小的滑动窗口协议。(TCP 利用滑动窗口实现流量控制,随ACK报文发送)
拥塞控制:当网络拥塞时,减少数据的发送。发送端通过拥塞窗口主动控制传输流量。慢启动:防止双方通信刚开始就传送大量数据包,发送端拥塞窗口初始设置为1MSS,每接受一个ACK信号,窗口扩大为两倍。发送数据时,取拥塞窗口和滑动窗口的较小值。同时设定一个慢启动阈值,当拥塞窗口大小超过阈值时,改为线性增长,直到网络拥塞。拥塞时将慢启动阈值设置为当前窗口的的一半,并将拥塞窗口的值设置为1,然后再次重复操作。
2.TCP/IP 协议数据封装的过程
以传输层采用TCP或者UPD、网络层采用IP、链路层采用Ethernet为例,可以看到TCP/IP中报文的封装过程如图所示。用户数据经过应用层协议封装后传递给传输层,传输层封装TCP头部,交给网络层,网络层封装IP头部后,再交给数据链路层,数据链路层封装Ethernet帧头和帧尾,交给物理层,物理层以比特流的形式将数据发送到物理线路上。
不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理。
目的主机收到数据包后,如何经过各层协议栈最后到达应用程序呢?整个过程如下图所示。
以太网驱动程序(网卡)首先根据以太网首部中的“上层协议”字段确定该数据帧的有效载荷(payload,指除去协议首部之外实际传输的数据)是IP、ARP还是RARP协议的数据报,然后交给相应的协议处理。假如是IP数据报,IP协议再根据IP首部中的“上层协议”字段确定该数据报的有效载荷是TCP、UDP、ICMP还是IGMP,然后交给相应的协议处理。假如是TCP段或UDP段,TCP或UDP协议再根据TCP首部或UDP首部的“端口号”字段确定应该将应用层数据交给哪个用户进程。IP地址是标识网络中不同主机的地址,而端口号就是同一台主机上标识不同进程的地址,IP地址和端口号合起来标识网络中唯一的进程。
注意,虽然IP、ARP和RARP数据报都需要以太网驱动程序来封装成帧,但是从功能上划分,ARP和RARP属于链路层,IP属于网络层。虽然ICMP、IGMP、TCP、UDP的数据都需要IP协议来封装成数据报,但是从功能上划分,ICMP、IGMP与IP同属于网络层,TCP和UDP属于传输层。
3.TCP/IP 数据包
我们通过 Wireshark 抓包:就分别看到五层数据:
第一行Frame 3339:物理层数据帧:线路83字节,实际捕获83字节
第二行Ethernet II:链路层网卡,以太网协议版本II,源地址:厂名_序号(网卡地址),目的:厂名_序号(网卡地址)
第三行Internet Protocol Version 4:网络层ip数据包,IPV4,源IP地址:10.44.13.7;目的IP是:10.171.8.154
第四行Transmission Control Protocol:传输层TCP数据包:源端口21000,目的端口:52529;Seq序列号:每发送一个RTP数据包,序列 号就加1;ACK是TCP数据包首部中的确认标志,对已接收到的TCP报文进行确认,其为 183589表示确认号有效;Len长度是17字节;
第五行data:数据
整个数据封装的格式如下图所示:
Wireshark显示的下面这些数据信息的顺序与各数据包内各字段的顺序相同,其他帧的内容展开与此类似。
帧号 时间 源地址 目的地址 高层协议 包内信息概况 No. Time Source Destination Protocol Info 1 0.000000 202.203.44.225 202.203.208.32 TCP 2764 > http [SYN] Seq=0 Len=0 MSS=1460 源端口>目的端口[请求建立TCP链接] 第一层物理层的数据帧概况
Frame 1 (62 bytes on wire, 62 bytes captured) 1号帧,线路62字节,实际捕获62字节 Arrival Time: Jan 21, 2008 15:17:33.910261000 捕获日期和时间 [Time delta from previous packet:0.00000 seconds]此包与前一包的时间间隔 [Time since reference or first frame: 0.00 seconds]此包与第1帧的间隔时间 Frame Number: 1 帧序号 Packet Length: 62 bytes 帧长度 Capture Length: 62 bytes 捕获长度 [Frame is marked: False] 此帧是否做了标记:否 [Protocols in frame: eth:ip:tcp] 帧内封装的协议层次结构 [Coloring Rule Name: HTTP] 用不同颜色染色标记的协议名称:HTTP [Coloring Rule String: http || tcp.port == 80] 染色显示规则的字符串:
第二层数据链路层以太网帧头部信息
Ethernet II, Src: AcerTech_5b:d4:61 (00:00:e2:5b:d4:61), Dst: Jetcell_e5:1d:0a (00:d0:2b:e5:1d:0a) 以太网协议版本II,源地址:厂名_序号(网卡地址),目的:厂名_序号(网卡地址) Destination: Jetcell_e5:1d:0a (00:d0:2b:e5:1d:0a) 目的:厂名_序号(网卡地址) Source: AcerTech_5b:d4:61 (00:00:e2:5b:d4:61) 源:厂名_序号(网卡地址) Type: IP (0x0800) 帧内封装的上层协议类型为IP(十六进制码0800)看教材70页图3.2
第三层网层IP包头部信息
Internet Protocol, Src: 202.203.44.225 (202.203.44.225), Dst: 202.203.208.32 (202.203.208.32) 互联网协议,源IP地址,目的IP地址 Version: 4 互联网协议IPv4(此部分参看教材119页图4.15的IPv4数据报字段结构) Header length: 20 bytes IP包头部长度 Differentiated Services Field:0x00(DSCP 0x00:Default;ECN:0x00)差分服务字段 Total Length: 48 IP包的总长度
Identification:0x8360 (33632) 标志字段
Flags: 标记字段(在路由传输时,是否允许将此IP包分段,教材125页)
Fragment offset: 0 分段偏移量(将一个IP包分段后传输时,本段的标识) Time to live: 128 生存期TTL Protocol: TCP (0x06) 此包内封装的上层协议为TCP Header checksum: 0xe4ce [correct] 头部数据的校验和 Source: 202.203.44.225 (202.203.44.225) 源IP地址 Destination: 202.203.208.32 (202.203.208.32) 目的IP地址
以下为传输层TCP数据段头部信息
Transmission Control Protocol, Src Port: 2764 (2764), Dst Port: http (80), Seq: 0, Len: 0 传输控制协议TCP的内容 Source port: 2764 (2764)源端口名称(端口号)(此部分参看教材149页图5.7) Destination port: http (80) 目的端口名http(端口号80) Sequence number: 0 (relative sequence number) 序列号(相对序列号) Header length: 28 bytes 头部长度 Flags: 0x02 (SYN) TCP标记字段(本字段是SYN,是请求建立TCP连接) Window size: 65535 流量控制的窗口大小 Checksum: 0xf73b [correct] TCP数据段的校验和 Options: (8 bytes) 可选项
4.TCP 数据包的报文
TCP建立连接,断开,数据传输都是使用同样数据报文格式。 下图中的四个非常重要的部分:
序号:Sequence Number,用来解决网络包乱序(reordering)问题。 确认序号:Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题。 窗口Window:又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。 TCP Flag :也就是包的类型,主要是用于操控TCP的状态机的,比如SYN。 Checksum: TCP数据段的校验和
其中我们抓包看到Transmission Control Protocol 首部的数据信息:
Transmission Control Protocol, Src Port: 21000, Dst Port:52529, Seq: 12936, ACK 183589 Len: 17 传输控制协议TCP的内容 Source port: 21000 源端口名称(端口号)(用于寻找发端应用进程) Destination port: 52529 目的端口 Sequence number: 0 (relative sequence number) 序列号(相对序列号,此序列号用来确定传送数据的正确位置,且序列号,用来侦测丢失的包);
[Next sequence number: 215 (relative sequence number)] #下一个序列号
Acknowledgement number :183589 是32位确认序号,确认其有效; Header length: 32 bytes 头部长度 Flags: 0x02 (SYN) TCP标记字段(本字段是SYN,是请求建立TCP连接) Window size value: 6364 流量控制的窗口大小 Checksum: 0xf73b [correct] TCP数据段的校验和 Options: (12 bytes) 可选项
下面说明详细说明:
源端口和目的端口:各占2个字节,16比特的端口号加上32比特的IP地址,共同构成相当于传输层服务访问点的地址
Seq序号: 占4个字节,是本报文段所发送的数据部分第一个字节的序号。在TCP传送的数据流中,每一个字节都有一个序号。
1、假设某时序号为300,简单的理解就是发送方告诉接收端“我发送的数据是从第300开始的”。
2、假设起数据len=100字节,则下一个报文段的序号就是400;
ACK 确认序号:占4字节,是期望收到对方下次发送的数据的第一个字节的序号,也就是期望收到的下一个报文段的首部中的序号;
1、确认序号是上一次已经成功接收到数据字节序号加1。还可以理解为接收端告诉发送端下一次想接收开始序号。假设某时确认序号为1000,简单的理解就是接收方告诉发送方“我已经收到第999序号了,我下一次想接收的数据是从1000开始的”。
2、由于序号字段有32比特长,可以对4GB的数据进行编号,这样就可保证当序号重复使用时,旧序号的数据早已在网络中消失了;
在数据传输过程中:
第一报文发送:Seq1=1 ACK1=1 len1=359
收到第一个报文回复:Seq2 =1 ACK2=Seq1 ACK1= 360, len1=17
下一个报文发送:Seq3=ACK2 =Seq1 (上一个发送的报文seq1 上一个发送的报文len1)=360 ACK3=Seq1 ACK1 =18 len3=0
两次ACK一样:你应该发数据,但我们没有收到数据,所以你还得同样ack继续发送
Header length首部长度(4位):报文头长度(单位:位)/32
1000(转化为10进制为8,8*32/8 = 32,该报文报头长度为32个字节)
存在该字段是因为TCP报头中任选字段长度可变
报头不包含任何任选字段则长度为20字节;4位所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节
Flag标志位:FLAGS字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.
SYN:表示建立连接,
同步序号用来发起一个连接,在建立连接时使用,当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,在发回的报文段中使SYN=1和ACK=1。因此,SYN=1表示这是一个连接请求或连接接受报文,而ACK的值用来区分是哪一种报文;
ACK:ACK表示响应,确认
序号有效,只有当ACK=1时,确认序号字段才有意义;
PSH:PSH表示有 DATA数据传输,
当PSH=1时,表明请求远地TCP将本报文段立即传送给其应用层,而不要等到整个缓存都填满了之后再向上交付。
FIN:表示关闭连接
发端完成发送任务(主动关闭),用来释放一个连接,当FIN=1时,表明欲发送的字节串已经发完,并要求释放传输连接;
RST:表示重建连接
当RST=1时,表明出现严重差错,必须释放连接,然后再重建传输连接。复位比特还用来拒绝一个非法的报文段或拒绝打开一个连接;
Nonce Sum:有效排除潜在的ECN滥用,RFC 3540 Congestion Window Reduced(CWR):拥塞窗口减少标志 ECN-Echo:ECE / ECN标志 URG: 紧急指针有效(urgentpointer) 当URG=1时,表明此报文应尽快传送,而不要按原来的排队顺序来传送。与“紧急指针”字段配合使用,紧急指针指出在本报文段中的紧 急数据的最后一个字节的序号,使接收方可以知道紧急数据共有多长;
1)、、其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应, 如果只是单个的一个SYN,它表示的只是建立连接。TCP的几次握手就是通过这样的ACK表现出来的。 2)、SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。 3)、RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。 一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。 4)、PSH为1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。
window窗口大小(2字节):
(TCP的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,起始于确认序 号字段指明的值,这个值是接收端正期望接收的字节。窗口大小是一个16bit字段,因而窗口大小最大为65535字节)。
Checksum检验和(2字节):
检验和覆盖整个TCP报文段;强制字段,由发送端计算存储,由接收端进行验证
Urgent pointer 紧急指针(2字节):
当Urgent标志置1时,紧急指针才有效
Options选项 :
选项字段允许每台主机设定能够接受的最大TCP载荷能力(缺省536字节) 。
5.TCP窗口控制
窗口控制的目的:
如果每数据包都需要等待一个ACK确认后再发下个数据包,然后发送的数据又很多,那么势必会影响到传输速度,吞吐量低,而这显然违背了TCP高性能传输的特性。为了解决这个弊端,TCP提出了一个很好的解决方案——窗口控制。所以,TCP必需要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引起网络拥塞,导致丢包。
窗口就是缓冲区, 用来暂时存储数据等待发送和接收,其实就是对每一次发送的数据大小进行限制, 每个窗口的都有大小限制,超过部分不能发送。也就是说,滑动窗口就是在发送完一个数据包后,不需要等待 ACK 消息返回,可以发送后面的数据包,解决吞吐量问题。
流量控制机制:
1、流量控制协议通过滑动窗口来实现. 2、流量控制是为了控制发送方发送速率,保证接收方来得及接收。 3、接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
拥塞控制:
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。
拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
TCP的拥塞控制采用了四种算法,即 慢开始 、 拥塞避免 、快重传 和 快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
慢开始: 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1,每经过一个传播轮次,cwnd加倍。
拥塞避免: 拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送放的cwnd加1.
快重传与快恢复: 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。 当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。
快重传指的是当接受方收到顺序错误得数据时不接收数据,同时重复发起对于之前数据的确认,发动到第三次,发送方得知自己的一部分数据再发送中丢失,立即发起重传,不需要等待下一次发送信息时一起发送过去.,且重传时触发和拥塞一样得情况,进入快恢复阶段
途中M3丢失 ,接收方不接受M4和M5 ,而是不断发送确认M2得报文,三次之后,发送方重传M3的数据
快恢复就是再发生拥塞和重传时,窗口经历了拥塞避免阶段,然后进入快恢复阶段,和拥塞避免一样都是每次加一,这样能提高恢复速度,像老版本(Tahoe)中需要重新经历慢开始
拥塞控制的四个算法都可以结合5-27图来理解,只要把这张图理解了,就基本理解了拥塞窗口的拥塞控制方法(重要)
拥塞控制和流量控制的区别 流量控制是端到端的控制,为了解决发送方和接收方速度不同而导致的数据丢失问题,当发送方发送的太快,接收方来不及接受就会导致数据丢失,流量控制用滑动窗口的形式解决问题。
例如A通过网络给B发数据,A发送的太快导致B没法接收(B缓冲窗口过小或者处理过慢),这时候的控制就是流量控制,原理是通过滑动窗口的大小改变来实现。
拥塞控制是为了解决过多的数据注入到网络,导致网络奔溃,超过负荷.当发送方发送数据大量的数据会注入到网络,如果没有限制,网络就会超负荷变卡,拥塞控制的用的是拥塞窗口解决的问题的。
A与B之间的网络发生堵塞导致传输过慢或者丢包,来不及传输。防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不至于过载。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络性能有关的所有因素。
5.TCP重传机制
TCP重传机制:
TCP协议是一种面向连接的可靠的传输层协议,它保证了数据的可靠传输,对于一些出错,超时丢包等问题TCP设计的超时与重传机制。其基本原理:在发送一个数据包(比如SYN)之后,就开启一个定时器,若是在一定时间内(RTO)没有收到发送数据包的ACK确认报文,则对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号。
TCP重传机制的原因:
TCP 数据包在一个IP包中传输。IP包可能在网络中丢失。导致IP包丢失的原因可能有很多,比如IP包经过太多的路由器接力,达到hop limit;比如路由器太过拥挤,导致一些IP包被丢弃;再比如路由表(routing table)没有及时更新,导致IP包无法送达目的地
两种重新发送TCP片段的机制:
超时重新发送
我们之前已经简单介绍过重新发送的机制:当发送方送出一个TCP片段后,将开始计时,等待该TCP片段的ACK回复。如果接收方正确接收到符合次序的片段,接收方会利用ACK片段回复发送方。发送方得到ACK回复后,继续移动窗口,发送接下来的TCP片段。如果直到计时完成,发送方还是没有收到ACK回复,那么发送方推断之前发送的TCP片段丢失,因此重新发送之前的TCP片段。这个计时等待的时间叫做重新发送超时时间(RTO, retransmission timeout)。
RTO:沙漏中沙子的多少
发送方应该在等待多长时间之后重新发送呢?这是重新发送的核心问题。上述过程实际上有往返两个方向:
1. 发送片段从发送方到接收方的传输,2. ACK片段从接收方到发送方的传输。
整个过程实际耗费的时间称做往返时间(RTT, round trip time)。如果RTT是固定的,比如1秒,那么我们可以让RTO等于RTT。但实际上,RTT的上下浮动很大。比如某个时刻,网络中有许多交通,那么RTT就增加。在RTT浮动的情况下,如果我们设置了过小的RTO,那么TCP会等待很短的时间之后重新发送,而实际上之前发送的片段并没有丢失,只是传输速度比较慢而已,这样,网络中就被重复注入TCP片段,从而浪费网络传输资源。另一方面,如果RTO时间过长,那么当TCP片段已经实际丢失的情况下,发送方不能及时重新发送,会造成网络资源的闲置。所以,RTO必须符合当前网络的使用状况。网络状况越好,RTO应该越短;网络状况越差,RTO应该越长。
RTT: 往返时间
TCP协议通过统计RTT,来决定合理的RTO。发送方可以测量每一次TCP传输的RTT (从发送出数据片段开始,到接收到ACK片段为止),这样的每次测量得到的往返时间,叫做采样RTT(srtt, sampling round trip time)。建立连接之后,每次的srtt作为采样样本,计算平均值(mean)和标准差(standard deviation),并让RTO等于srtt平均值加上四倍的srtt标准差。
RTO = mean 4 std
(上述算法有多个变种,根据平台不同有所变化)
平均值反映了平均意义上的RTT,平均往返时间越大,RTO越大。另一方面,标准差越大也会影响RTO。标准差代表了RTT样本的离散程度。如果RTT上下剧烈浮动,标准差比较大。RTT浮动大,说明当前网络状况相对不稳定。因此要设置更长的RTO,以应对不稳定的网络状况。
快速重新发送
我们刚才介绍了超时重新发送的机制:发送方送出一个TCP片段,然后开始等待并计时,如果RTO时间之后还没有收到ACK回复,发送方则重新发送。TCP协议有可能在计时完成之前启动重新发送,也就是利用快速重新发送(fast-retransmission)。快速发送机制如果被启动,将打断计时器的等待,直接重新发送TCP片段。
由于IP包的传输是无序的,所以接收方有可能先收到后发出的片段,也就是乱序(out-of-order)片段。乱序片段的序号并不等于最近发出的ACK回复号。已接收的文本流和乱序片段之间将出现空洞(hole),也就是等待接收的空位。比如已经接收了正常片段5,6,7,此时又接收乱序片段9。这时片段8依然空缺,片段8的位置就是一个空洞。
补上空洞
TCP协议规定,当接收方收到乱序片段的时候,需要重复发送ACK。比如接收到乱序片段9的时候,接收方需要回复ACK。回复号为8 (7 1)。此后接收方如果继续收到乱序片段(序号不是8的片段),将再次重复发送ACK=8。当发送方收到3个ACK=8的回复时,发送方推断片段8丢失。即使此时片段8的计时器还没有超时,发送方会打断计时,直接重新发送片段8,这就是快速重新发送机制(fast-retransmission)。
快速重新发送机制利用重复的ACK来提示空洞的存在。当重复次数达到阈值时,认为空洞对应的片段在网络中丢失。快速重新发送机制提高了检测丢失片段的效率,往往可以在超时之前探测到丢失片段,并重复发送丢失的片段。
我们实际遇到的问题:
我们在服务器tcpdump抓包发现存在的TCP Retransmission的现象
从图中可以看到TCP三次握手的第一步SYN出现重传。客户端发送SYN后如果没有收到服务器放的ACK确认,就会导致重传的发生,因为客户端机器认为远程机器没有收到包,而发生重新发送SYN包的事件。
既然在服务器上抓包能捕获SYN的请求,那就说明服务器端接收到了请求但是没有回应ACK包:
我们查看内核参数中打开了net.ipv4.tcp_tw_recycle = 1, 在tcp_tw_recycle模式下,服务器判断是无效连接的条件是:
1、来自对端的tcp syn请求携带时间戳
2、本机在MSL时间内接收过来自同一台ip机器的tcp数据
3、新连接的时间戳小于上次tcp数据的时间戳
以上条件满足时,连接请求会被拒绝
因此在高并发的情况下,就有可能导致服务器收到SYN但是不会向客户端发送SYN ACK包。因为MSL时间内接收过来自同一台ip机器的tcp数据,导致服务器认为包不可信而丢弃。
我们通过 netperf 监测TCP的应答/响应性能对比:
打开net.ipv4.tcp_tw_recycle = 1,请求交易速率只有594.97次/秒:
关闭net.ipv4.tcp_tw_recycle = 0,请求交易速率提高到了1406.29次/秒:
TCP数据报文的Options
当前,TCP常用的Option如下所示:
Kind (Type) | Length | Name | Reference | 描述 & 用途 |
---|---|---|---|---|
0 | 1 | EOL | RFC 793 | 选项列表结束 |
1 | 1 | NOP | RFC 793 | 无操作(用于补位填充) |
2 | 4 | MSS | RFC 793 | 最大Segment长度 |
3 | 3 | WSOPT | RFC 1323 | 窗口扩大系数(Window Scaling Factor) |
4 | 2 | SACK-Premitted | RFC 2018 | 表明支持SACK |
5 | 可变 | SACK | RFC 2018 | SACK Block(收到乱序数据) |
8 | 10 | TSPOT | RFC 1323 | Timestamps |
19 | 18 | TCP-MD5 | RFC 2385 | MD5认证 |
28 | 4 | UTO | RFC 5482 | User Timeout(超过一定闲置时间后拆除连接) |
29 | 可变 | TCP-AO | RFC 5925 | 认证(可选用各种算法) |
253/254 | 可变 | Experimental | RFC 4727 | 保留,用于科研实验 |
一般Option的格式为TLV结构,如下所示:
Kind / Type(1 Byte) | Length(1 Byte) | Value |
---|
1. EOL和NOP Option(Kind 0、Kind 1)只占1 Byte,没有Length和Value字段;
2. NOP用于 将TCP Header的长度补齐至32bit的倍数(由于Header Length字段以32bit为单位,因此TCP Header的长度一定是32bit的倍数);
3. SACK-Premitted Option占2 Byte,没有Value字段;
4. 其余Option都以1 Byte的“Kind”开头,指明Option的类型;Length指明Option的总长度(包括Kind和Length)
5. 对于收到“不能理解”的Option,TCP会无视掉,并不影响该TCP Segment的其它内容;
① .Maximum Segment Size (MSS) Option
一般情况下,通信双方在建立连接时,SYN Segment中会携带MSS Option,MSS指明本端可以接受的最大长度的TCP Segment(Payload,不含TCP Header),也就是说,对端发送数据的长度不应该大于MSS(单位Byte)。 ———— 1. 首先要明确一点,MSS并非和对端协商的值,而是对对端发送数据长度的“限制”,表明在整个TCP连接期间,都不会接收长度大于MSS的TCP Segment。
2. 如果收到的SYN中没有MSS,将使用默认值536。MSS Option的Value字段长度固定为16bit,所以MSS最大值为65535(单位Byte)。因此,网络中所有设备都被要求,必须能够处理大小小于576Byte的数据包(IP Header TCP Header Default MSS 最小值为 576 Byte)
3. IPv4网络中,MSS的典型取值为1460 ,1460Byte 20Byte IP Header 20Byte TCP Header = 1500Byte = 以太网典型MTU;
4. IPv6网络中,典型MSS取值为1440;另外,如果MSS=65535,表示MSS = PMTU - 60
② .Selective Acknowledgment (SACK) Options
在标准的TCP实现中,使用的是累加式的ACK,例如“ACK Num = n”代表对序列号n以前的Bytes进行确认。但是,显然,这样将无法对不连续的Segment进行确认。此外,当出现不连续Segment时,还会导致TCP的接收队列出现一个“坑”,不将这个坑填上,坑后的数据就无法交付给应用程序。
为解决上述问题,TCP定义了SACK Option,可以使TCP接收者将这个“坑”的位置通告给发送者,让其对这一段数据进行重传。
注意:若要使用SACK特性,必须在建立连接时,在SYN Segment中附加上SACK-Permitted Option,以此告知对方自己支持SACK。 SACK-Permitted Option格式如下所示:
Kind = 4 | Length = 2 |
---|
SACK Option通过“Left Edge ~ Right Edge”,指定了一个或多个范围的Seq Num,称为SACK Block,指明了处于“坑”后面(或坑之间)的、已成功接收的Bytes。SACK Option格式如下所示 ————
Kind (5) | Length (可变) | Left Edge of 1st Block (32bit) | Right Edge of 1st Block (32bit) | …… | …… | Left Edge of nst Block (n≤4) | Right Edge of nst Block (n≤4) |
---|
另外,由于TCP Header最长为60 Byte,因此SACK Option中最多只能包含4个SACK Block Example(终端A - 终端B) ———— 1. 终端A收到了TCP数据流中的Seq Num为0 ~ 1452、2905~4096的字节,但缺少了1453~2904;
2. 终端A向B发送ACK Segment,其中ACK Num=1453、SACK Option=2905~4097,表明它已经收到了数据流中的Seq Num为2905 ~ 4096的字节,但没有还没收到1453~2904;
3. 终端B收到这个SACK后,重传包含Byte 1453~2904的TCP Segment;
4. 终端A向B发送ACK Segment,其中ACK Num=4097,表明它已经收到Seq Num 4097之前的所有字节;
5. 之后,数据通信恢复正常。 ③ .Window Scale (WSCALE or WSopt) Option TCP Header的Window Size字段长度为16bit,因而正常情况下,Window Advertisement最大只能是65535 Bytes; Window Scale Option用于将TCP Header的Window Size字段从16bit扩展至最多30bit,格式如下所示:
Kind (3) | Length (3) | shift.cnt (范围0~14) |
---|
———— 1. shift.cnt的取值范围为0~14,表示将Window Advertisement的值扩展至“WindowSize × 2s
2. WSopt只能出现在SYN Segment或SYN ACK Segment中,因此shift.cnt在三次握手之后就会固定下来。
3. 另外,WSopt是双向独立的,因此连接的两个方向可以有不同的Shift.cnt。但是,WSopt必须双向同时启用,也就是说,如果SYN中不带有WSopt,SYN ACK中也不能出现WSopt;同样,如果SYN ACK中不带有WSopt,那么发起SYN的一端就会当作自己也不曾发送过WSopt。
4. shift.cnt根据接收Buffer的大小,由TCP自动选取。接收Buffer由系统或程序设定。
④ .Timestamps Option and PAWS(Protection against Wrapped Sequence Numbers,防止序列号回绕)
启用Timestamp Option后,每个TCP Segment中都会带有Timestamp Option,其中包含了两个32bit的Timestamp(TSval和TSecr),具体格式如下所示:
Kind (8) | Length (10) | Timestamp Value(TSval) | Timestamp Echo Reply(TSecr) |
---|
1. TSval指明了发送端在发送TCP Segment时的Timestamp;接收端在对该TCP Segment做ACK时,将TSval值回显在TSecr字段中。
注意:由于TCP连接是双向的,接收端在ACK中回显TSecr时,也会把自己当前的Timestamp放入TSval字段。
2. Timestamp是一个随时间单调递增的值,由于TCP接收端只需要在ACK中将TSval简单地回显,因此通信双方并不需要进行时间同步等操作。
3. 通过Timestamp Option,发送端再也不需要在内存中保存发送Segment的时间了,只需要将其放入TSval,然后接收端将其回显在ACK Segment即可。当发送端收到ACK Segment后,取出TSscr,和当前时间做算术差,即可完成一次RTT的测量。
4. 若非通过Timestamp Option来计算RTT,大部分TCP实现只会以“每个Window采样一次”的频率来测算RTT。因此通过Timestamp Option,可以实现更密集的RTT采样,使RTT的测算更精确。 另外,Timestamp Option还有PAWS(Protection Against Wrapped Sequence Numbers,防止序列号回绕)功能,详见以下示例 ———— 1. 假设TCP Window Size为1GB(使用Window Scale),发送者每发送一个Window的数据Timestamp值加100,数据的发送情况如下所示:
时间点 | 发送数据量 | Seq Num | Timestamp | 接收 |
---|---|---|---|---|
1 | 0G:1G | 0G:1G | 0~100 | OK |
2 | 1G:2G | 1G:2G | 100~200 | 其中某些Segment丢包后重传 |
3 | 2G:3G | 2G:3G | 200~300 | OK |
4 | 3G:4G | 3G:4G | 300~400 | OK |
5 | 4G:5G | 0G:1G | 400~500 | OK |
6 | 5G:6G | 1G:2G | 500~600 | 此前丢包的Segment出现了 |
2. 在时间点2的时候,发生了丢包;在时间点4和5之间,序列号发生了回绕;在时间点6,已经被认为“丢包”的Segment延迟到达了。
3. 由于延时到达的Segment的timestamp为1xx,小于时间点6的有效timestamp(5xx),因此这个Segment会根据PAWS机制丢弃,从而不会对TCP造成影响。
使用TCP Timestamps option (TSopt) 进行RTT采样。当前大部分操作系统(Linux、Windows)的实现方式如下
User Timeout (UTO) Option UserTimeout值表明了TCP发送者等待ACK的时间,如果在指定时间内没收到ACK,就会认为对端挂掉。对于传统TCP(RFC 793)而言,UserTimeout是本地配置的。RFC 1122建议,当TCP重传3次后,应该通知应用程序,100s后,应该拆除连接。 通过UTO,可以让TCP将UserTimeout值“告知”给对端,UTO格式如下所示:
Kind (28) | Length (4) | G bit(Granularity bit) | UserTimeout |
---|
1. G bit = 1,表示UserTimeout的单位为分钟;G bit = 0,表示UserTimeout的单位为秒
2. 通过UTO,TCP接收者可以根据“对端的UserTimeout”来调整自己的行为。UserTimeout建议取值为:min(U_Limit,max(Adv_UTO,Remot_UTO,L_Limit))
o U_Limit是本地UserTimeout的最高限制;Adv_UTO是通告出去的UserTimeout;Remot_UTO是对端的UserTimeout;L_Limit是本地UserTimeout的最低限制;
3. 要注意的是,UTO只是用于“告知”,TCP接收者却不一定要根据对端的UTO值来调整自己的行为。
4. 此外,NAT设备也可以根据UTO来调整连接保活计时器
5. 若使用 = min(U_LIMIT, max(ADV_UTO, REMOTE_UTO, L_LIMIT))
⑥ .Authentication Option (TCP-AO)and TCP MD5 Signature Option(TCP-MD5)
TCP-MD5和TCP-AO主要用于防止TCP欺骗攻击(TCP Spoofing Attacks)。TCP-MD5是旧标准(RFC 2385),例如BGP、LDP等协议就是以TCP-MD5作为认证手段的。2010年后,IETF建议使用TCP-AO去取代TCP-MD5,然而TCP-AO当前的普及率还很低。 TCP-MD5和TCP-AO的格式如下所示
TCP-MD5 Option的MD5 Hash根据以下信息计算:
1.TCP伪头部 2.TCP头部(包括Option,checksum设为0) 3.TCP Segment Data 4.密钥 相对于TCP-MD5,TCP-AO的主要改进之处在于: 1. 支持多种MAC算法
2. 支持带内的密钥变更操作
注意:TCP-AO与TCP-MD5一样,都不包含密钥分发机制。因此在密钥分发方面都存在一定风险。