TCP协议详解

TCP协议详解

TCP是什么?

TCP(Transmission Control Protocol),传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。主要有以下特点:

  • TCP 是面向连接的。

就好像打电话一样,通信前需要先拨号建立连接,结束通话后要挂机释放连接。

  • 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的(一对一)。
  • TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
  • TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据。
  • 面向字节流

TCP中的“流”(Stream),指的是流入进程或从进程流出的字节序列。
“面向字节流”的含义是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据仅仅看成一连串的无结构的字节流。

TCP的头部是什么样的?

TCP头部信息

  • 20个字节固定头部结构
  • 40个字节头部选项字段
    TCP头部结构

TCP头部结构:

typedef struct _TCP_HEADER
{
short m_sSourPort;       // 源端口号16bit
short m_sDestPort;        // 目的端口号16bit
unsigned int m_uiSequNum;   // 序列号32bit
unsigned int m_uiAcknowledgeNum; // 确认号32bit
short m_sHeaderLenAndFlag;   // 前4位:TCP头长度;中6位:保留;后6位:标志位
short m_sWindowSize;      // 窗口大小16bit
short m_sCheckSum;       // 检验和16bit
short m_surgentPointer;      // 紧急数据偏移量16bit
}attribute((packed))TCP_HEADER, *PTCP_HEADER;

  • 16位源端口:告诉主机该报文段来自哪里
  • 16位目标端口:传给哪个上层协议或者应用程序
    客户端通常使用系统自动选择的端口号,服务器使用知名服务器的端口号。在Linux下,所有知名服务器的端口号都定义在/etc/server/底下。
    知名端口号
  • 32位序号:每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收。当一个主机开启一个TCP会话时,它的序列号被系统初始化为某个随机值ISN。后续的TCP报文段中序号加上该报文段所携带数据的第一个字节在整个字节流中的偏移。例如,某个TCP报文段传送的数据是字节流中的第1025-2048字节,那么该报文段的序号值就是ISN+1025。
  • 32位确认号:目的主机返回确认号,使源主机知道某个或几个报文段已被接收在这里插入图片描述
  • 4位头部长度:指示何处数据开始
  • 16位窗口大小:长度为16位,2个字节,表示想收到的每个TCP数据段的大小
  • 16位校验和:长度为16位,2个字节,由发送端填充,接收端对TCP报文段执行CRC算法以校验TCP报文段在传输过程中是否损坏
  • 16位紧急指针:长度位16位,2个字节,指向后面是优先数据的字节,在RRG标志设置了时才有效。如果URG标志没有被设置,紧急域作为填充。加快处理标识为紧急的数据段。
  • TCP头部选项字段:
    Kind(1字节)+length(1字节)+内容(n字节)

在这里插入图片描述
TCP头部选项:
Kind=0 表示结束选项
Kind=1 空操作选项
Kind=2 最大报文段长度选项
Kind=3 窗口扩大因子选项,TCP连接初始化时,通讯双方使用该选项来协商接收通告窗口的扩大因子,只能出现在同步报文段中。
Kind=4 选择确认选项(SACK)TCP通信中,某个报文段丢失,TCP模块会重发最后确认的TCP报文段的后续所有报文段,这可能导致重复,SACK技术直接发送丢失的报文段就好。
Kind=5 SACK实际工作选项,该选项参数告诉发送方本端已经收到并缓存不连续的数据块,发送端根据此选项检查并重发丢失的数据块。
Kind=8 时间戳选项,比较准确的计算通讯双方的回路时间的方法。

什么是TCP三次握手?

三次握手,简单来说就是:

  • 发送方:我要和你建立连接。
  • 接收方:你真的要和我建立连接吗?
  • 发送方:我真的要和你建立连接,成功。

详细步骤:
TCP三次握手

  • 第一次握手:Client将标志位SYN=1,随机产生一个值 seq=J,并将该数据包发送给Server。此时,Client进入SYN_SENT状态,等待Server确认。
  • 第二次握手:Server收到数据包后根据标志位 SYN=1 知道Client请求建立连接,Server将标志位SYNACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。此时,Server进入SYC_RCVD状态。
  • 第三次握手:Client收到确认后,检查ack是否为J+1ACK是否为1。如果正确则如下操作:
    • Client则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server。此时Client进入ESTABLISHED状态。
    • Server检查ack是否为K+1ACK是否为1,如果正确则连接建立成功。此时Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

仔细看来,Client会发起两次数据包,分别是SYNCACK;Server会发起一次数据包,包含SYNCACK。也就是说,三次握手的过程中,Client和Server互相做了一次SYNCACK

🦅 为什么 TCP 连接需要三次握手,两次不可以么,为什么?
为了防止已失效的连接请求报文突然又传到了服务端,因而产生错误。

客户端发出的连接请求报文并未丢失,而是在某个网络节点长时间滞留了,以致延误到链接释放以后的某个时间才到达Server。

  • 若不采用“三次握手”,那么只要Server发出确认数据包,新的连接就建立了。由于Client此时并未发出建立连接的请求,所以其不会理财Server的确认,也不与Server通信;而这时Server一直在等待Client的请求,这样Server就白白浪费了一定的资源。
  • 若采用“三次握手”,在这种情况下,由于Server端没有收到来自客户端的确认,则就会知道Client并没有要求建立请求,就不会建立连接。
  • 这就很明白了,防止了服务器端的一直等待而浪费资源。

什么是TCP四次挥手?

四次挥手,简单来说,就是:

  • 发送方:我要和你断开连接!
  • 接收方:好的,可以。
  • 接收方:我也要和你断开连接!
  • 发送方:好的,断吧。

详细来说,步骤如下:
TCP四次挥手

如下使用Client和Server的方式,仅仅是为了方便,也是可以从Server向Client发起。

  • 第一次挥手:Client发送一个FIN=M,用来关闭Client到Server的数据传送。此时,Client进入FIN_WAIT_1状态。
  • 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为CLOSE_WAIT状态。注意,TCP链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。
  • 第三次挥手:Server发送一个FIN=N,用来关闭Server到Client的数据传送。此时Server进入LAST_ACK状态。
  • 第四次挥手:Client收到FIN后,此时Client进入TIME_WAIT状态。接着,Client发送一个ACK给Server,确认序号为N+1。Server接收到后,此时Server进入CLOSED状态,完成四次挥手。

🦅 为什么要四次挥手?
TCP协议是一种面向连接的、可靠的、基于字节流的运输层协议。TCP是全双工模式,这就意味着:

  • 当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接收来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的。

因为主机2此时可能还有数据想要发送给主机1,所以挥手不能像握手只有三次,而是多了那么“一次”!

  • 当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了。之后彼此就会愉快的中断这次TCP连接。

如果要正确的理解四次的原理,就需要了解四次挥手过程中的状态变化。

主动方=发送方;被动方=接收方。
状态前面的(主动方)(被动方),表示该状态属于谁。

  • (主动方)FIN_WAIT_1:这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:
    • FIN_WAIT_1状态实际上是当Socket在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该Socket即进入到FIN_WAIT_1状态。
    • 而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,都应该马上回应ACK报文。所以,FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常我们可以用netstat看到。
  • 主动方FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的Socket,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你(ACK信息),稍后再关闭连接。
  • (被动方)CLOSE_WAIT:这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个Socket后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个Socket,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
  • (被动方)LAST_ACK:这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
  • (主动方)TIME_WAIT:表示收到了对方的FIN报文,并发出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

    为何一定要等2MSL?
    如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新的TCP连接报文冲突,造成数据冲突,为避免此种情况,需要耐心的等待网络老的TCP连接的活跃报文全部死掉,2MSL这个时间可以满足这个需求。

  • CLOSED:表示连接中断。
    另外,关于TIME_WAIT和CLOSE_WAIT状态的区别可以参考《TIME_WAIT和CLOSE_WAIT状态区别》

TCP数据如何传输?

建立连接后,两台主机就可以相互传输数据了。如下图所示:
TCP套接字的数据交换过程

  • 上图给出了主机A分2次(分2个数据包)向主机B传递200字节的过程。
  • 首先,主机A通过1个数据包发送100个字节的数据,数据包Seq号设置为1200。主机B为了确认这一点,向主机A发送ACK包,并将Ack号设置为1301.
    • 为了保证数据准确到达,目标机器在收到数据包(包括SYN包、FIN包、普通数据包等)包后必须立即回传ACK包,这样发送方才能确认数据传输成功。
    • 此时Ack号为1301而不是1201,原因在于Ack号的增量为传输的数据字节数。假设每次Ack号不加传输的字节数,这样虽然可以确认数据包的传输,但无法明确100字节全部正确传递还是丢失了一部分,比如只传递了80字节。因此按如下的公式确认Ack号:Ack号=Seq号+传递的字节数+1.
    • 与三次握手协议相同,最后加1是为了告诉对方要传递的Seq号。

🦅TCP数据传输丢失怎么办?

这个问题也可以改成提问,什么是TCP重传。

因为各种原因,TCP数据包可能存在丢失的情况,TCP会进行数据重传。如下图所示:
TCP套接字数据传输过程中发生错误

  • 上图表示通过Seq 1301 数据包向主机B传递100字节的数据,但中间发生了错误,主机B未收到。经过一段时间后,主机A仍未收到对于Seq 1301 的ACK确认,因此尝试重传数据。为了完成数据包的重传,TCP套接字每次发送数据包时都会启动定时器,如果在一定时间内没有收到目标机器传回的ACK包,那么定时器超时,数据包会重传。上图演示的时数据包丢失的情况,也会有ACK包丢失的情况,一样会重传。
  • 重传超时时间(RTO,Retransmission Time Out)

    这个值太大了会导致不必要的等待,太小会导致不必要的重传,理论上最好是网络RTT时间,但又受制于网络距离与瞬态时延变化,所以实际上使用自适应的动态算法(例如Jacobson算法和Karn算法等)来确定超时时间。
    往返时间(RTT,Round-Trip Time)表示从发送端发送数据开始,到发送端收到来自接收端的ACK确认包(接收端收到数据后便立即确认),总共经历的时延。

  • 重传次数

    TCP数据包重传次数,根据系统设置的不同而有所区别。有些系统,一个数据包只会被重传3次,如果重传3次后还未收到该数据包的ACK确认,就不再尝试重传。但有些要求很高的业务系统,会不断的重传丢失的数据包,以尽最大可能保证业务数据的正常交互。
    最后需要说明的是,发送端只有在收到对方的ACK确认包后,才会清空输出缓冲区中的数据。

什么是TCP滑动窗口?

在看TCP滑动窗口的概念之前,我们先来看看它出现的背景?

将TCP与UDP这样的简单传输协议区分开来的是,他传输数据的质量需要协议有以下两大关键功能:

  • 可靠性:保证数据确实到达目的地。如果未到达,能够发现并重传。
  • 数据流控:管理数据的发送速率,以使接收设备不至于过载。

    要完成这些任务,整个协议操作是围绕滑动窗口+确认机制来进行的。因此,了解了滑动窗口,也就是理解了TCP。

那么到底什么是TCP滑动窗口呢?

滑动窗口协议,是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。
TCP的滑动窗口解决了端到端的流量控制问题,允许接受方对传输进行限制,知道它拥有足够的缓冲空间来容纳更多的数据。

可能这么描述之后,可能会有点懵逼,建议看下面三篇文章。

TCP协议如何来保证传输的可靠性?

TCP提供一种面向连接的、可靠的字节流服务。其中,面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前先建立一个TCP连接。

  • 在一个TCP连接中,仅有两方进行彼此通信。
  • 而字节流服务意味着两个应用程序通过TCP链接交换8bit字节构成的字节流,TCP不在字节流中插入记录标识符。

对于可靠性,TCP通过以下方式进行保证:

  • 数据包校验:目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这是TCP发送数据超时后会重发数据。
  • 对失序数据包重排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重新排序,然后才交给应用层。
  • 丢失重复数据:对于重复数据,能够丢弃重复数据。
  • 应答机制:当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
  • 超时重发:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
  • 流量控制:TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收缓冲区所能容纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP使用的流量控制协议是可变大小的滑动窗口协议

什么是TCP拥堵?

计算机网络中的带宽、交换节点中的缓存及处理及等都是网络的资源。在某段时间,若对网络中某一资源超过了该资源所能提供的可用部分,网络中的性能就会变坏,这种情况叫做拥塞

🦅 怎么解决 TCP 拥堵?

通过拥塞控制来解决。拥堵控制,就是防止过多的数据注入网络中,这样可以使网络中的路由器链路不致过载。注意,拥塞控制和流量控制不同,前者是一个全局性的过程,而后者指点对点通信量的控制。

拥塞控制的方法主要有以下四种:

  1. 慢开始。
  2. 拥塞避免
  3. 快重传。
  4. 快恢复。

1)慢开始

不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。

2)拥塞避免

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

在这里插入图片描述
3)快重传

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

快重传算法规定,发送方只要一连收到三个重复确认,就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
在这里插入图片描述
4)快恢复

快重传配合使用的还有快恢复算法,当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。

  • 但是接下去并不执行慢开始算法:因为如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。
  • 所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。
    在这里插入图片描述
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值