TCP协议详解

文章目录

  • 1、TCP协议简介
  • 2、TCP报头
  • 3、通过序号与确认应答提高可靠性
  • 4、超时重传
  • 5、连接管理
    • 5.1、TCP的连接建立(三次握手)
    • 5.2、TCP的连接释放(四次挥手)
  • 6、滑动窗口
  • 7、流量控制
  • 8、拥塞控制
  • 9、延迟确认应答
  • 10、捎带应答
  • 11、总结


1、TCP协议简介

TCP是TCP/IP体系中非常复杂的一个协议,它有以下特点:

  1. TCP是面向连接的运输层协议。这就是说,应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。这就是说,应用进程之间的通信好像在“打电话”:通话前要先拨号建立连接,通话结束后要挂机释放连接。
  2. 每一条TCP连接只能有两个端点, 每一 条TCP连接只能是点对点的(一对一)
  3. TCP提供可靠交付的服务。也就是说,通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
  4. TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,.上层的应用进程在合适的时候读取缓存中的数据。
  5. 面向字节流。TCP中的“流"指的是流入到进程或从进程流出的字节序列。面向字节流” 的含义是:虽然应用程序和 TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。也就是说TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。

总的来说:TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议

2、TCP报头

TCP协议详解_第1张图片

  1. 源端口和目的端口 各占 2个字节,分别写入源端口号和目的端口号
  2. 序号 占4字节。序号的范围是[0, 2^32-1]。在TCP连接中传输数据的每一个字节都要进行按序编号。起始序号必须在建立连接时就设置。
  3. 确认序号 占4字节,期望收到下一个报文段的第一个数据字节的序号。
  4. 数据偏移 占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段实际上是指出TCP报文段的首部长度。由于首部中还有长度不确定的选项字段(保留字段,6位),因此数据偏移字段是必要的。但数据偏移是以4字节为整数倍。由于4位二进制数能够表示的最大十进制数字是15, 因此数据偏移的最大值是60字节,这也是TCP首部的最大长度(即选项长度不能超过40字节),但最小值为固定长度20字节。
  5. 保留 占6位,保留为今后使用,但目前应置为0。
  6. 紧急URG 当 URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。要与首部中紧急指针(Urgent Pointer)字段配合使用。
  7. 确认ACK 仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
  8. 推送PSH 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH= 1的报文段,就尽快地(即“推送”向前)交付给接收应用进程,而不再等到整个缓存都填满了后再向上交付。
  9. 复位RST 当 RST= 1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开-一个连接。RST也可称为重建位或重置位。
  10. 同步SYN 在连接建立时用来同步序号。当SYN= 1而ACK= 0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。因此,SYN 置为1就表示这是一个连接请求或连接接受报文。
  11. 终止FIN 用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
  12. 窗口 占2字节。窗口值是[0, 2^16-1]之间的整数。窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。例如,设确认号是701,窗口字段是1000。 这就表明,从701号算起,发送此报文段的一方还要接收1000个字节数据( 字节序号是701 ~ 1700)的接收缓存空间。窗口字段明确指出了现在允许对方发送的数据量。窗口值是经常在动态变化着。
  13. 检验和 占2字节。检验和字段检验的范围包括首部和数据这两部分。判断数据是否出错。
  14. 紧急指针 占2字节。紧急指针仅在URG = 1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为零时也可发送紧急数据。
  15. 选项 长度可变,最长可达40字节。当没有使用选项时,TCP的首部长度是20字节

3、通过序号与确认应答提高可靠性

在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做ACK(确认应答)

通常,两个人对话时,在谈话的停顿处可以点头或询问以确认谈话内容。如果对方迟迟没有回应,说话的一方还可以重复一遍以保证对方确实可以听到。因此,对方是否理解了此次对话的内容以及是否听到了对话的内容都要靠对方的反应来判断。

网络中的确认应答就类似于上面的例子。当对方听懂对话内容时会说:“嗯”,这就相当于返回了一个确认应答(ACK)。而当对方没有理解对话内容或没有听清时往往会问一句“啊?”这好比一个否定确认应答

TCP协议详解_第2张图片

TCP通过肯定的ACK实现可靠的数据传输。当发送端将数据发出之后会等待对端的确认应答,如果有确认应答,说明数据已经成功到达,如果没有,那么数据有可能丢失了。

如下图所示,在一定时间内没有等到确认应答,发送端就可以认为数据已经丢失,就会进行重发。由此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。

TCP协议详解_第3张图片
除此之外,未收到确认应答并不意味着数据一定丢失。也有可能是数据对方已经收到,只是返回的确认应答在途中丢失。这种情况也会导致发送端因没有收到确认应答,而认为数据没有到达目的地,从而进行重新发送。如下图所示:

TCP协议详解_第4张图片
此外,还有一种情况对于接收方来说会比较难受。就是确认应答延时到达,在等待的过程中发送端会误认为数据丢失,所以会反复触发重传,因此对于接受主机来说,它会反复接收到相同的数据,而为了对上层应用提供可靠的传输,就必须得放弃重复的数据包。为此就必须要引入一种机制,使其能够识别是否已经接受数据,又能判断是否需要接受。

序列号是按照顺序给发送数据的每一个字节都标上号码的编号。接收端查询接收数据TCP首部中的序列号和数据的长度,将自己下一步应该接受的序号作为确认应答返送回去。就这样,通过序列号和确认应答号,TCP可以实现可靠传输

TCP协议详解_第5张图片


序列号和确认应答号
TCP协议详解_第6张图片

4、超时重传

时重传的超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过了这个时间仍然为收到确认应答,发送端将会重发数据。那么这个数据是如何确定的呢?

最理想就是找到一个最小时间,它能保证确认应答一定能在这个时间内返回。但是这个时间长短随着数据包途径的网络环境的不同会有所变化。

TCP要求不论处在何种网络环境下都要提供高性能通信,而且无论网络拥堵的情况发生如何改变,都必须保持这一特性。为此,它在每次发包时都会计算往返时间及其偏差,将这个往返时间和偏差相加重发超时的时间,就是比这个综合要稍微大一点的值。
TCP协议详解_第7张图片
在BSD的Unix及Windows系统中,超时都以0.5s为单位进行控制,因此重发超时都是0.5s的整数倍。不过由于最初的数据包还不知道往返时间,所以其重发超时一般设置为6s左右
数据被重发以后若还是收不到应答,就会进行再次发送,此时,等待确认应答的时间将会以2倍,4倍的指数函数延长

不过,数据也不会无限,反复的重发。当达到一定的重发次数之后,如果仍然没有任何确认应答返回,就会判断为网络或对端主机发生异常,强制关闭连接。

5、连接管理

TCP提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好通信两端之间的准备工作。这里的准备工作就是建立连接,也就是我们常说的三次握手。不需要通信时,就需要释放连接,以免浪费资源,这也是我们常听说的四次挥手。在正常情况下,TCP要经过三次握手建立连接, 四次挥手断开连接

5.1、TCP的连接建立(三次握手)

三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

TCP协议详解_第8张图片
最初两端的TCP进程都处于CLOSED(关闭)状态。请注意,A主动打开连接,而B被动打开连接。
B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求。然后服务器进程就处于LISTEN (收听)状态,等待客户的连接请求。如有,即作出响应。

  • 第一次握手:A的TCP客户进程也是首先创建传输控制模块TCB,然后向B发出连接请求报文段,这时首部中的同步位SYN = 1, 同时选择-一个初始序号seq = x。TCP规定,SYN报文段(即SYN = 1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN- SENT(同步已发送)状态。
  • 第二次握手:B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack=x + 1,同时也为自己选择一个初始序号seq= y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。 这时TCP服务器进程进入SYN-RCVD (同步收到)状态。
  • 第三次握手:TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+ 1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq=x+ 1。这时,TCP连接已经建立,A进入ESTABLISHED (已建立连接)状态。当B收到A的确认后,也进入ESTABLISHED状态。

在socket编程中,客户端执行connect()时,将触发三次握手

为什么是三次握手?一次,两次,四次不行吗?

四次不行。我们知道,在通信时收到的报文总是对上一次发送报文的确认,无论四次、五次,或者多次,总有最后一次是无法被确认的。也就是说,最后一次都有可能是失败的,并且还可能让服务器建立大量的无用连接,从而浪费资源。为了让服务器
不要出现误判行为,减小因握手带来的时间消耗,三次是最佳选择。

一次不行。如果客户端发送一次请求报文就进入已连接状态,而服务端收到这个报文也进入连接状态,连接就一定成功吗?不一定,请求报文可能在中途丢失,这既不能验证客户端的收发能力,也不能验证服务端的收发能力。

二次也不行。只能验证客户端收发能力,服务端的接受能力,不能验证服务端的发送能力。现假定出现这样一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。采用三次握手的办法可以防止上述现象的发生。例如在刚才的情况下,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。

一次或两次不行,还有一个原因就是服务端受到攻击的成本太低。例如,一些恶意用户进行大量的SYN请求,导致服务端必须维持大量的连接,从而耗尽资源。这被称为SYN洪水攻击。

TCP的三次握手是否都可以携带数据?

  • 带SYN的是不可以携带数据的第一次和第二次是不可以携带数据的,但是第三次是可以携带数据的。
  • 假如第一次握手可以携带数据的话,那对于服务器太危险,如果恶意攻击服务器,每次都在第一次握手中的SYN报文中放入大量数据。而且频繁重复发SYN报文,服务器会花费很多的时间和内存空间去接收这些报文。
  • 第三次握手,此时客户端已经处于ESTABLISHED状态。对于客户端来说,他已经建立起连接了,并且已经知道服务器的接收和发送能力是正常的。所以也就可以携带数据了。

TCP三次握手失败,服务端会如何处理?

握手失败的原因有两种:

  • 第一种是服务端没有收到SYN,则什么都不做。
  • 第二种是服务端回复了SYN+ACK后,长时间没有收到ACK响应。server端发送了SYN+ACK报文后就会启动一个定时器,等待client返回的ACK报文。如果第三次握手失败的话client给server返回了ACK报文,server并不能收到这个ACK报文。那么server端就会启动超时重传机制,超过规定时间后重新发送SYN+ACK,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries来指定,默认是5次。如果重传指定次数到了后,仍然未收到ACK应答,那么一段时间后,server自动关闭这个连接。但是client认为这个连接已经建立,如果client端向server写数据,server端将以RST包响应。

5.2、TCP的连接释放(四次挥手)

数据传输结束后,通信的双方都可释放连接。最开始A和B都处于ESTABLISHED状态

TCP协议详解_第9张图片

  • 第一次挥手:A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的FIN置1,其序号seq=u,它等于前面已传送过的数据的最后一个字节的序号加1。这时A进入FIN-WAIT-1 (终止等待1)状态,等待B的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。
  • 第二次挥手:B收到连接释放报文段后即发出确认,确认号是ack=u+ 1,而这个报文段自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1。然后B就进入CLOSE-WAIT (关闭等待)状态。TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭。这个状态可能会持续一些时间。A收到来自B的确认后,就进入FIN-WAIT-2 (终止等待2)状态,等待B发出的连接释放报文段。
  • 第三次挥手:若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN = 1。现假定B的序号为w (在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack= u + 1。这时B就进入LAST-ACK (最后确认)状态,等待A的确认。
  • 第四次挥手:A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack=w + 1,而自己的序号是seq=u+ 1 (根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT (时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器设置的时间2MSL后,A才进入到CL OSED状态,才能开始建立下一个新的连接。当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。

以上就是四次挥手的全部过程,下面将逐一解答一些问题。

为什么A在TIME-WAIT状态必须等待2MSL的时间呢?

  1. 为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN + ACK报文段,而A就能在2MSL时间内收到这个重传的FIN + ACK报文段。接着A重传一次确认,重新启动2MSL计时器。最后,A和B都正常进入到CLOSED状态。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后立即释放连接,那么就无法收到B重传的FIN + ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常步骤进入CLOSED状态。
  2. 防止前面提到的“已失效的连接请求报文段"出现在本连接中。A在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。B只要收到了A发出的确认,就进入CLOSED状态。同样,B在撤销相应的传输控制块TCB后,就结束了这次的TCP连接。我们注意到,B结束TCP连接的时间要比A早一些。

为什么握手是三次,而挥手时需要四次呢?

  • 其实在TCP握手的时候,接收端将SYN包和ACK确认包合并到一个包中发送的,所以减少了一次包的发送。对于四次挥手,由于TCP是全双工通信,主动关闭方发送FIN请求不代表完全断开连接,只能表示主动关闭方不再发送数据了。而接收方可能还要发送数据,就不能立即关闭服务器端到客户端的数据通道,所以就不能将服务端的FIN包和对客户端的ACK包合并发送,只能先确认ACK,等服务器无需发送数据时在发送FIN包,所以四次挥手时需要四次数据包的交互

CLOSE_WAIT状态详谈

  • 出现如果服务端出现CLOSE_WAIT状态,说明服务端已经收到的来自客户端发送的FIN报文,并且已经返回了一个ACK的确认报文,客户端到服务端的连接已经关闭。如果服务端一直是CLOSE_WAIT状态,说明服务端在底层并没有调用close()来关闭连接,这样维持半连接是很消耗资源的。可以用netstat查看服务器是否有大量的CLOSE_WAIT状态。
  • 对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭socket,导致四次挥手没有正确完成.,这是一个 BUG.,只需要加上对应的 close 即可解决问题。

TIME_WAIT状态详谈

  • 首先调用close()发起主动关闭的一方,在发送最后一个ACK之后会进入time_wait的状态,也就说该发送方会保持2MSL时间之后才会回到初始状态。MSL指的是是数据包在网络中的最大生存时间。
  • 确保最后一个确认报文能够到达。进而尽快释放服务器的资源。如果没能到达,服务端就会重发FIN请求释放连接。等待一段时间没有收到重发就说明服务的已经CLOSE了。如果有重发,则客户端再发送一次ACK信号
  • 为什么主动发起连接关闭的一方最终需要等待2MSL,前面已经说过,不多赘述

如何解决TIME_WAIT状态引起的bind失败?
在server的TCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的

  • 服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户端来请求)
  • 这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃,就需要被服务器端主动清理掉),就会产生大量TIME_WAIT连接
  • 由于我们的请求量很大,就可能导致TIME_WAIT的连接数很多,每个连接都会占用一个通信五元组(源ip,源端口,目的ip,目的端口,协议)。 其中服务器的ip和端口和协议是固定的。如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了,就会出现问题。

使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符
在这里插入图片描述

6、滑动窗口

TCP以1个段为单位,每发-一个段进行-一次确认应答的处理。这样的传输方式有一个缺点。那就是,包的往返时间越长通信性能就越低。
TCP协议详解_第10张图片


为解决这个问题,TCP引入了窗口这个概念。即使在往返时间较长的情况下,它也能控制网络性能的下降。确认应答不再是以每个分段,而是以更大的单位进行确认时,转发时间将会被大幅度的缩短。也就是说,发送端主机,在发送了一个段以后不必要一直等待确认应答,而是继续发送

TCP协议详解_第11张图片

  • 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000个字节(四个段)。
  • 发送前四个段的时候,不需要等待任何ACK,直接发送。
  • 收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;
  • 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答。只有确认应答过的数据,才能从缓冲区删掉。
  • 窗口越大,则网络的吞吐率就越高

TCP协议详解_第12张图片

  • 如上图,在这个窗口内的数据即便没有收到确认应答也可以发送出去。此外,从该窗口中能看到的数据因其某种原因已在传输中丢失,所以发送端才能收到确认应答,这种情况也需进行重发。为此,发送端主机在等到确认应答返回之前,必须在缓冲区中保留这部分数据。
  • 在滑动窗口以外的部分包括尚未发送的数据以及已经确认对端已收到的数据。当数据发出后若如期收到确认应答就可以不用再进行重发,此时数据就可以从缓存区清除。
  • 收到确认应答的情况下,将窗口滑动到确认应答中的序列号的位置。这样可以顺序地将多个段同时发送提高通信性能。这种机制也被称为滑动窗口控制。

那么如果出现了丢包, 如何进行重传? 这里分两种情况讨论
情况一: 数据包已经抵达, ACK丢了
TCP协议详解_第13张图片
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认。

情况二: 数据包就直接丢了
TCP协议详解_第14张图片

  • 当某一段报文段丢失之后,发送端会收到1001这样的ACK,告知发送端需要发送1001报文段,但是发送端并不会不会立马重新发送,因为之前发的1001报文段可能由于网路较差,还在传输过程中,并没有丢失。如果超时了还没有收到对这个报文段的确认,那么发送端会进行超时重传,重新发送这个报文段
  • 如果发送端主机连续三次收到了同样一个1001这样的ACK,就认为1001数据段已经丢了,就会立刻将对应的报文段重新发送。不会再等到超时后再重传。这种机制也叫快速重传
  • 这个时候接收端收到了1001报文段之后, 再次返回的ACK就是7001了(因为2001-7000)接收端其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中

7、流量控制

一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收,或者发送方的速度太慢,接收方希望发送方发送的速度变快一些
利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。

具体操作

  • 接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段, 通过ACK端通知发送端。
  • 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端。
  • 发送端接受到这个窗口之后,就会减慢自己的发送速度。
  • 如果接收端缓冲区满了,就会将窗口置为0。这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
  • 窗口大小字段越大,说明网络的吞吐量越高。

TCP协议详解_第15张图片

接收端何时把窗口大小告诉发送端呢?
三次握手的时候告诉对方

TCP首部中,有一个16位窗口字段,就是存放了窗口大小信息。
那么问题来了,16位数字最大表示65535,那么TCP窗口最大就是65535字节么?
不是,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是 窗口字段的值左移M位。

8、拥塞控制

有了TCP的窗口控制,收发主机之间即使不再以一个数据段为单位发送确认应答,也能够连续发送大量数据包。然而,如果在通信刚开始时就发送大量数据,也可能会引发其他问题。
一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果突然发送一个较大量的数据,极有可能会导致整个网络的瘫瘓。
TCP为了防止该问题的出现,在通信一开始时就会通过一个叫做慢启动的算法得出的数值,对发送数据量进行控制。

TCP协议详解_第16张图片

  • 首先,为了在发送端调节所要发送数据的量,定义了一个叫做"拥塞窗口"的概念。于是在慢启动的时候,将这个拥塞窗口的大小设置为1个数据段(1460字节)发送数据,之后每收到一次确认应答( ACK),拥塞窗口的值就加1。在发送数据包时,将拥塞窗口的大小与接收端主机通知的窗口大小做比较,然后按照它们当中较小那个值,发送比其还要小的数据量。
  • 如果重发采用超时机制,那么拥塞窗口的初始值可以设置为1以后再进行慢启动修正。有了上述这些机制,就可以有效地减少通信开始时连续发包导致的网络拥堵,还可以避免网络拥塞情况的发生。
  • 不过,随着包的每次往返,拥塞窗口也会以1、2、4等指数函数的增长,拥堵状况激增甚至导致网络拥塞的发生。为了防止这些,引入了慢启动阀值的概念。只要拥塞窗口的值超出这个阀值,在每收到一次确认应答时,拥塞窗口只能线性增长。
  • TCP的通信开始时,并没有设置相应的慢启动阀值。而是在超时重发时,才会设置为当时拥塞窗口一半的大小。以后的每次超时重发的时候, 慢启动阈值会变成原来的一半,同时拥塞窗口置回1。

TCP协议详解_第17张图片


当TCP通信开始以后,网络吞吐量会逐渐上升,但是随着网络拥堵的发生吞吐量也会急速下降。于是会再次进人吞吐量慢慢上升的过程。

9、延迟确认应答

接收数据的主机如果每次都立刻回复确认应答的话,可能会返回一个较小的窗口。那是因为刚接收完数据,缓冲区已满。当某个接收端收到这个小窗口的通知以后,会以它为上限发送数据,从而又降低了网络的利用率。为此,引人了一个方法,那就是收到数据以后并不立即返回确认应答,而是延迟一段时间的机制。

那么所有的包都可以延迟应答么?
肯定也不是
数量限制: 每隔N个包就应答一次
时间限制: 超过最大延迟时间就应答一次
具体的数量和超时时间,依操作系统不同也有差异;。一般N取2,超时时间取200ms。

事实上,大可不必为每一个数据段都进行一次确认应答。TCP采用滑动窗口的控制机制,因此通常确认应答少一些也无妨。TCP文件传输中,绝大多数是每两个数据段返回一次确认应答。

TCP协议详解_第18张图片


10、捎带应答

TCP的确认应答和回执数据可以通过一个包发送。这种方式叫做捎带应答。通过这种机制,可以使收发的数据量减少。

另外,接收数据以后如果立刻返回确认应答,就无法实现捎带应答。而是将所接收的数据传给应用处理生成返回数据以后进再进行发送请求为止,必须一直等待确认应答的发送。也就是说,如果没有启用延迟确认应答就无法实现捎带应答。延迟确认应答是能够提高网络利用率从而降低计算机处理负荷的一种较优的处理机制。

TCP协议详解_第19张图片

11、总结

TCP要保证可靠性,同时又尽可能的提高性能

可靠性:

  • 校验和
  • 序列号(按序到达)
  • 确认应答
  • 超时重发
  • 连接管理
  • 流量控制
  • 拥塞控制

提高性能:

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

你可能感兴趣的:(网络,tcp/ip,网络协议,TCP协议)