网络协议分析-TCP

本文最后更新于:几秒前

TCP特点:

  1. 面向数据流

  2. 虚链路连接

  3. 有缓冲的传输

​ 发送方和接收方都有一个固定大小的缓冲空间。

  1. 无结构的数据流

​ TCP流服务并不提供结构化的数据流形式。

  1. 全双工连接

TCP流服务提供的连接功能是双向的,连接的任一方都能够发送和接收数据。

TCP连接建立:

TCP 连接的建立是基于客户端/服务器模式。TCP使用三次握手来建立连接。

20430499-c2fc3a596f5c874a

具体步骤:

- 客户端发送SYN报文段,指明客户端打算连接的服务器端口以及初始序号(x)。

- 服务器发送包含服务器初始序号(y)的SYN报文段作为应答,并包含确认信息(ACK),告诉客户端自己已收到第一个SYN报文,并且同意建立这个连接。确认号被设定为初始序号+1(x+1),并期望收到下一个SYN。

- 客户端将确认号设置为服务器的初始序列号+1(y+1)以对服务器SYN报文进行确认,通知目的主机已成功建立了双方所同意的这个连接。

三次握手的功能:

- 同意连接协商,确保双方做好传输数据的准备。

- 协商各自报文段初始序列号

- 协商最大报文段长度(只有SYN报文才能协商该参数)

初始序列号的选取:

TCP规定初始序列号不能为1,由于TCP是全双工的,所以每个方向上报文的初始序列号都要在三次握手阶段通告给对方。

TCP初始序列号不为1的原因是为了防止安全性攻击。

取值方法:

ISN=M+F(localIP,localport,remoteIP,remoteport,secretkey)

其中M是一个计时器,每隔4毫秒+1,F是一个散列算法,secretkey是一个秘密值。这样就使得ISN是一个小于2^32的随机整数,且随时间增长而增大,到2^32后再回滚。安全性增强。

MSS协商:

TCP通过三次握手建立连接的过程中可协商MSS。每个TCP报文是以不同长度在一个连接上传输的,因此一个连接的两端必须协商一个最大的报文长度值。

如果连接的两端处于同一个物理网络中,TCP协议会计算合适的MSS,使得数据报大小与网络的MTU近似。如果不在同一个物理网络,他们就会把路径上最小的MTU作为MSS,默认值为536字节。

TCP连接的关闭:

TCP使用改进的三次握手方法关闭连接。首先进行关闭的一方执行主动关闭,而另一方则执行被动关闭。

20430499-c2fc3a596f5c874a

具体步骤如下:

- 进行关闭的一方在发送完全部数据并等待确认全部到达后,发送一个FIN报文。

- 当另一方收到这个FIN后,先发回一个确认ACK,确认号为收到的序号+1,同SYN一样,一个FIN报文也占用一个序号。同时通知应用程序,通信结束,后续没有任何数据了。

- 当应用程序处理完毕后,关闭它的连接,向主动关闭的一方,发送FIN报文字段。

- 关闭发起的一方,发送最后一个ACK,将确认序号设置为收到的序号+1。

TCP连接异常关闭:

TCP使用连接复位操作来执行异常关闭,发起端送出一个RST报文,此时连接双方立即停止传输,关闭连接,并释放所用的缓冲区等有关资源。

TCP半开连接检测:

半开连接产生的原因:

- 进程崩溃: 如果一个进程正常关闭,通常会发送一个“FIN”包,通知对方连接已经关闭。但是,如果进程崩溃或终止(例如,从任务管理器终止),对对端的“FIN”通知是不能保证发送的。操作系统有可能代表崩溃的进程发送一个“FIN”数据包;不过,这取决于操作系统。

- 电脑死机: 如果整个计算机(包括操作系统)崩溃或断电,肯定无法通知对方连接已经断开。

路由器崩溃/重启。客户端和服务器连接之间的任何路由器也可能崩溃或重启;如果此时一方正在发送数据,则会导致连接中断,发送方的连接断开。如果在此期间双方没有数据发送,那么连接不会断开。

- 拔掉网络电缆: 客户端和服务器连接之间的网络电缆断开都会导致连接中断而不通知。这与路由器的情况类似;如果在电缆断开到接通期间,没有数据正在传输,则连接实际上不会丢失。但是,计算机通常会检测是否拔掉了特定的网络电缆,并且可能会通知其本地套接字网络丢失(远程端不会被通知)。

- 无线设备(包括笔记本电脑)超出范围: 移出接入点范围的无线设备将失去连接。这是一个经常被忽视但日益普遍的情况。

服务器对每条连接都要维护相应的记录,这需要耗费内存空间,如果客户端异常关机,那么就没有必要再维护这条连接记录了。为了解决这个问题,TCP引入了保活计时器,一旦服务器发现某条连接上已经有2小时没有通信了,便会向客户端发送探查报文并根据情况进行处理:

正常工作并且从服务器可达:

发送探查报文,如果收到回应,便重置保活计时器,重述上述过程

客户端崩溃:

连续发送10个探查报文,回应超时时间间隔设置为75秒,若始终没有回应,则终止连接。

客户端崩溃后重新启动:

此时客户端会向服务器发送RST报文,服务器收到这个报文后终止连接。

TCP报文格式:

20430499-016379da7ab1ad64

源端口,目的端口: 各占2字节,包含连接双方用于表识进程的端口号。

序列号: 占4字节,TCP为每个报文赋予一个序号,指明双方在发送的数据字节流中的位置。

确认号: 占4字节,表示的是期望收到对方下一个报文的序号。

首部长度: 占4bit 是以4字节为单位的首部长度值。

保留未用: 6bit 固定为0

窗口: 占2字节,用于指定缓冲区大小,以字节为单位。

码元比特:

URG: 紧急指针字段有效,表示该报文段中有紧急数据,尽快发送

ACK: 表示报文段中携带了确认信息。

PSH: 表示本报文段中请求推操作来强迫数据发送。

RST: 表明TCP连接中出现严重错误偏差,必须释放连接。

SYN: 在建立连接时使用。

FIN: 在结束连接时使用。

TCP校验和: TCP首部校验和占2字节,提供包括TCP首部在内全部数据完整性校验。

TCP选项: 其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,那么选项部分最长为:(2^4-1)*4-20=40字节。

具体有:

MSS、窗口扩大因子、选择性确认、时间戳、MD5签名、认证

TCP拥塞控制:

1. 拥塞:

即对资源的需求超过了可用的资源。若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降。

拥塞控制: 防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。

流量控制: 指点对点通信量的控制,是端到端正的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

拥塞控制代价: 需要获得网络内部流量分布的信息。在实施拥塞控制之前,还需要在结点之间交换信息和各种命令,以便选择控制的策略和实施控制。这样就产生了额外的开销。拥塞控制还需要将一些资源分配给各个用户单独使用,使得网络资源不能更好地实现共享。

2. 几种拥塞控制方法

慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。

2.1 慢开始和拥塞避免

发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞。

发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。

慢开始算法: 当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。

20430499-5f37aeb30caa208f

每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。

另,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。

为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量(如何设置ssthresh)。慢开始门限ssthresh的用法如下:

当 cwnd < ssthresh 时,使用上述的慢开始算法。

当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。

当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。

拥塞避免算法: 让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。

如下图,用具体数值说明了上述拥塞控制的过程。现在发送窗口的大小和拥塞窗口一样大。

20430499-f4bd7e6a9d90d227

<1>. 当TCP连接进行初始化时,把拥塞窗口cwnd置为1。前面已说过,为了便于理解,图中的窗口单位不使用字节而使用报文段的个数。慢开始门限的初始值设置为16个报文段,即 cwnd = 16 。

<2>. 在执行慢开始算法时,拥塞窗口 cwnd 的初始值为1。以后发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值另1,然后开始下一轮的传输(图中横坐标为传输轮次)。因此拥塞窗口cwnd随着传输轮次按指数规律增长。当拥塞窗口cwnd增长到慢开始门限值ssthresh时(即当cwnd=16时),就改为执行拥塞控制算法,拥塞窗口按线性规律增长。

<3>. 假定拥塞窗口的数值增长到24时,网络出现超时(这很可能就是网络发生拥塞了)。更新后的ssthresh值变为12(即变为出现超时时的拥塞窗口数值24的一半),拥塞窗口再重新设置为1,并执行慢开始算法。当cwnd=ssthresh=12时改为执行拥塞避免算法,拥塞窗口按线性规律增长,每经过一个往返时间增加一个MSS的大小。

强调:“拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。“拥塞避免”是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。

2.2 快重传和快恢复

如果发送方设置的超时计时器时限已到但还没有收到确认,那么很可能是网络出现了拥塞,致使报文段在网络中的某处被丢弃。这时,TCP马上把拥塞窗口 cwnd 减小到1,并执行慢开始算法,同时把慢开始门限值ssthresh减半。这是不使用快重传的情况。

快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时才进行捎带确认。

20430499-dbdc69b9883d01c7

接收方收到了M1和M2后都分别发出了确认。现在假定接收方没有收到M3但接着收到了M4。显然,接收方不能确认M4,因为M4是收到的失序报文段。根据可靠传输原理,接收方可以什么都不做,也可以在适当时机发送一次对M2的确认。但按照快重传算法的规定,接收方应及时发送对M2的重复确认,这样做可以让发送方及早知道报文段M3没有到达接收方。发送方接着发送了M5和M6。接收方收到这两个报文后,也还要再次发出对M2的重复确认。这样,发送方共收到了接收方的四个对M2的确认,其中后三个都是重复确认。快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段M3,而不必继续等待M3设置的重传计时器到期。由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约20%。

与快重传配合使用的还有快恢复算法,其过程有以下两个要点:

<1>. 当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半。这是为了预防网络发生拥塞。请注意:接下去不执行慢开始算法。

<2>. 由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

下图给出了快重传和快恢复的示意图,并标明了“TCP Reno版本”。

区别:新的 TCP Reno 版本在快重传之后采用快恢复算法而不是采用慢开始算法。

20430499-543a6a439480129f

也有的快重传实现是把开始时的拥塞窗口cwnd值再增大一点,即等于 ssthresh + 3 X MSS 。这样做的理由是:既然发送方收到三个重复的确认,就表明有三个分组已经离开了网络。这三个分组不再消耗网络 的资源而是停留在接收方的缓存中。可见现在网络中并不是堆积了分组而是减少了三个分组。因此可以适当把拥塞窗口扩大了些。

在采用快恢复算法时,慢开始算法只是在TCP连接建立时和网络出现超时时才使用。

采用这样的拥塞控制方法使得TCP的性能有明显的改进。

接收方根据自己的接收能力设定了接收窗口rwnd,并把这个窗口值写入TCP首部中的窗口字段,传送给发送方。因此,接收窗口又称为通知窗口。因此,从接收方对发送方的流量控制的角度考虑,发送方的发送窗口一定不能超过对方给出的接收窗口rwnd 。

发送方窗口的上限值 = Min [ rwnd, cwnd ]

当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。

当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。

TCP的防丢失机制:

为了防止数据丢失,TCP使用了“带重传的肯定确认技术”,要求接收方收到数据之后,向源站回送确认报文。

发送方在发送下一个报文时,必须收到上一个报文的确认报文。发送方发出报文时,同时设定一个计时器,如果源站在限定时间内没有收到确认报文,则认为该报文丢失,重新发送。

TCP防止重复和乱序的机制

- 由于TCP引入了重传肯定确认技术,可能会导致报文的重复。

- 为了解决这个问题,TCP给每个报文都指定了一个唯一序列号,并要求接收方记住报文的序号,并对收到的报文进行检测。肯定确认协议会在确认信息中携带一个序号,这样接收方就能正确的把报文段与确认关联起来。

- 由于引入了唯一的序列号,使得报文乱序问题也得到了解决。

TCP确认机制的特点:

TCP确认指明的是期望接收的下一个报文段的序号,而不是已经收到的报文段序号。

累计确认。TCP的确认信息会报告已经积累了多少个字节的数据流。

优点:

® 使得变长段传输方式下不会产生二义性

® 即便确认信息丢失也不一定会导致重传

缺点:

发送方不能收到所有成功传输的报文段确认信息,而只能知道已收到数据流中的某一个位置信息。

TCP超时重传定时器设置:

TCP软件把报文段从发出到收到确认所经历的时间段定义为RTT

TCP自适应重传算法:

TCP监视每个连接的性能,由此推算出合适的定时时限。当连接的性能变化时,TCP随时修改定时器时限来适应时延的变化。

具体算法在此不表述。

TCP的滑动窗口机制:

TCP的滑动窗口机制与其他协议稍有不同,TCP滑动窗口设定了三个指针,左边界指针把已发送的字节流中已经得到确认的部分与未得到确认的部分区分开,右边界指针指出了序列中在未得到确认的情况下可以发送的最高字节的序号;已发与未发界限指针位于窗口内部,表示窗口内已发送和尚未发送的字节界限。

TCP端到端的流量控制:

为了实现端到端的流量控制,TCP连接双方各自设置了两个窗口。发送窗口和接收窗口分别对应发送缓冲区和接收缓冲区,这两个窗口大小是动态可调的。接收方主机在确认时,使用窗口通告,向发送方主机告知自己接收缓冲区大小,以便源站调整发送窗口的大小。

TCP的坚持定时器:

TCP不对单纯的ACK报文确认,只对含有数据的报文进行确认,如果一个只含有非0窗口通告信息,而这个信息丢失,则双方就会因为相互等待而陷入死锁。

为了防止这种情况的发生,发送方使用一个坚持定时器来周期性的向接收方查询,以发现窗口是否增大。

在确定坚持定时器的超时时间值时,TCP采用了指数退避策略。例如:首次超时时间计算出来是1.5秒,则第二次超时间为 1.5x2^1,第三次为1.5x2^2……以此类推。

糊涂窗口综合症(SWS):

SWS是指当接收方发出0窗口通告信息后,TCP应用程序从接受缓冲区读取1字节数据,这时由于缓冲区有可用空间,接收方会立即发送一个非0窗口通告信息,如此反复,会导致发送接收双方一直以小报文传输,降低了网络带宽利用率。

解决方法:

接收方:

要等到缓冲区可用空间至少达到总空间的一半或者达到MSS之后才发送新的窗口通告。

发送方:

等数据足以填满一个能够达到最大长度的报文段后才从缓冲区发送出去。

如果不够一个报文长度,则推迟发送或者等到收到一个确认时,才把这个小报文段发送出去。


欢迎指出文章中的错误,谢谢


网络协议分析-TCP
https://blog.icansudo.top/2020/04/23/网络协议分析-TCP/
作者
odin
发布于
2020年4月23日
许可协议