TCP协议学习笔记:

TCP协议学习笔记:,第1张

TCP协议学习笔记:

网络层的IP协议本身是不可靠的,其可靠性需要上层协议(TCP协议)来保证。

文章目录
    • TCP协议学习笔记:
      • 协议特点
      • TCP协议头部结构
      • TCP连接状态与状态转移
      • TCP三次握手与四次挥手
      • TCP超时重传机制
      • TCP滑动窗口:
      • TCP流量控制与拥塞控制:
      • 补充:

协议特点

传输层主要的协议就是UDP、TCP;

两者特点

UDP:用户数据报协议,特点:面向无连接、可以单播、多播、广播;面向数据报文;不可靠

TCP:传输控制协议,特点:面向连接的;基于字节流;可靠的

两者区别:

特点UDPTCP
是否创建连接面向无连接面向连接
是否可靠不可靠可靠
连接对象可以是一对一、一对多、多对多等等一对一
首部字节8字节至少20字节
传输方式面向数据报面向字节流
适用场景实时应用可靠性要求高的

TCP的可靠传输主要通过:

建⽴连接(标志位):通信前确认通信实体存在。

序号机制(序号、确认号):确保了数据是按序、完整到达。

数据校验(校验和):CRC校验全部数据。

超时重传(定时器):保证因链路故障未能到达数据能够被多次重发。

窗⼝机制(窗⼝):提供流量控制,避免过量发送

**拥塞控制:**通过拥塞控制,避免过量发送,导致丢包。

TCP协议头部结构

1.序号(32bit):传输⽅向上字节流的字节编号。初始时序号会被设置⼀个随机的初始值(ISN),之后每次发

送数据时,序号值 = ISN + 数据在整个字节流中的偏移。假设A 发送 B且ISN初始值为2048,第⼀段数据512字节已经

到B,则第⼆段数据发送时序号为2048+ 512。序号主要⽤于解决网络包乱序问题。

2.确认号(ack:32bit):接收⽅对发送⽅TCP报⽂段的响应,其值是收到的序号值 + 1。

⾸部⻓(4bit):标识⾸部有多少个4字节 * ⾸部⻓,最⼤为15,即60字节。

3.标志位(6bit):

URG:标志紧急指针是否有效。

ACK:标志确认号是否有效(确认报⽂段)。⽤于解决丢包问题。

PSH:提示接收端⽴即从缓冲读⾛数据。

RST:表示要求对⽅᯿新建⽴连接(复位报⽂段)。

SYN:表示请求建⽴⼀个连接(连接报⽂段)。

FIN:表示关闭连接(断开报⽂段)。

4.窗⼝(16bit):接收窗⼝(RWND)。⽤于告知对⽅(发送⽅)本⽅的缓冲还能接收多少字节数据。⽤于解决流控。

5.校验和(16bit):接收端⽤CRC检验整个报⽂段有⽆损坏。

6.紧急指针:发送端向接收端发送紧急数据的方法,所代表的值就是一个正偏移量,与序号字段的值相加就是表示最后一个紧急数据的下一个字节的序号。

TCP连接状态与状态转移

图中:注意主动关闭、被动关闭,其状态转移特点

CLOSED:初始状态。

LISTEN:服务器处于监听状态。

SYN_SEND:客户端socket执⾏CONNECT连接,发送SYN包,进⼊此状态。

SYN_RECV:服务端收到SYN包并发送服务端SYN包,进⼊此状态。

ESTABLISH:表示连接建⽴。客户端发送了最后⼀个ACK包后进⼊此状态,服务端接收到ACK包后进⼊

此状态。

FIN_WAIT_1:终⽌连接的⼀⽅(通常是客户机)发送了FIN报⽂后进⼊。等待对⽅FIN。

CLOSE_WAIT:(假设服务器)接收到客户机FIN包之后等待关闭的阶段。在接收到对⽅的FIN包之后,

自然是需要立即回复ACK包的,表示已经知道断开请求。但是本⽅是否⽴即断开连接(发送FIN包)取决

于是否还有数据需要发送给客户端,若有,则在发送FIN包之前均为此状态。

FIN_WAIT_2:此时是半连接状态,即有⼀⽅要求关闭连接,等待另⼀⽅关闭。客户端接收到服务器的

ACK包,但并没有⽴即接收到服务端的FIN包,进⼊FIN_WAIT_2状态。

LAST_ACK:服务端发动最后的FIN包,等待最后的客户端ACK响应,进⼊此状态。

**TIME_WAIT:**客户端收到服务端的FIN包,并⽴即发出ACK包做最后的确认,在此之后的2MSL时间称为

TIME_WAIT状态;(MSL为报文的最大生存时间,RFC 1122建议值是2min)

TCP三次握手与四次挥手


将前面两节以及状态转移图结合TCP建立连接与关闭连接来看(经典:三次握手,四次挥手)

三次握手:

第一次:客户端发送请求报文段(包含SYN位,seq=J).

客户端:状态–>SYN_SEND

第二次:服务器在收到客户端请求后,发送含ACK、SYN位的确认请求报文段,其中ack(确认号)=J+1;seq=K;

服务端:状态:SYN_RECV

第三次:客户端发送含ACK位的确认报文段,ack=K+1;

客户端:状态–>ESTABLISH 服务器:状态 ->ESTABLISH

为什么需要三次?

第一、二次,是客户端与服务端彼此确认通信时的初始化序号,保证通讯的同步,不乱序;

如果没有第三次:就可能出现服务端发送的ack丢失,客户端并未收到,就会超时再重发请求,从而导致服务器对于同一个客户端保持多个连接,造成资源浪费;

四次挥手:(理论上来说挥手主要看谁主动断开的,本次例子是客户端主动断开,如果是服务器主动断开,那么对象互换一下就行了。)

第一次:客户端主动发送含FIN位的结束报文,seq=u给服务器;

此时客户端状态:FIN_WAIT_1

第二次:服务器发送含ACK位、ack=u+1的报文给客户端;

服务器状态:CLOSE_WAIT

此时,服务器接收到FIN包会⽴即回应ACK包表示已接收到断开请求;如果还有剩余数据要发送就会进⼊CLOSED_WAIT状态,没有的话,就直接发ACK、FIN,(也就是包含第三次内容)。

客户端状态:FIN_WAIT_2

此时,客户端是半连接状态,即有⼀⽅要求关闭连接,等待另⼀⽅关闭;客户端仍可以接收数据,但已经没有发送数据能⼒。

客户端状态一直保持FIN_WAIT_2,直到服务端发送结束报文。

(如果这个时候服务器突然关闭,客户端久久不能收到服务器的结束报文,此时客户端连接就由内核管理,变为孤儿进程,并指定生存时间)

第三次:服务器发送含FIN位,seq=w,ack=u+1的报文;

服务器状态: LAST_ACK

客户端状态: TIME_WAIT

为什么客户端还需要TIME_WAIT状态,不直接CLOSED?

服务端没有接收到最后⼀次ACK包会在超时之后,再发FIN包,此时如果客户端已经CLOSED,所以服务端就不会收到ACK⽽是收到RST。所以TIME_WAIT状态就是防⽌最后⼀次握⼿数据没有到达对⽅⽽导致重发FIN。

第四次客户端:发送最后一个含ACK位、ack=w+1的报文给服务器

客户端状态:CLOSED

服务器状态:CLOSED;

为什么挥手要四次?

TCP是双⼯的,所以发送⽅和接收⽅都需要FIN和ACK(2*2=4)。只不过有⼀⽅是被动的,所以看上去

就成了4次挥⼿。

TCP超时重传机制

超时重传:发送端发送报⽂后若⻓时间未收到确认的报⽂则需要重发该报⽂

可能导致超时的情况:

1.发送的数据丢失,没有到达接收端

2.接受端收到数据,但是ACK报文在返回时丢失

3.接收端拒绝或丢弃数据

重传事件间隔(RTO):从上次发送数据,因为没有收到ACK回应,到下一次需要重发的时间;

每次重传时间为上次重传间隔的两倍,计量单位为RTT,

RTT:数据从发送到接收到对⽅响应之间的时间间隔,即数据报在⽹络中⼀个往返⽤时。其⼤小不稳定。

TCP滑动窗口:

先了解一下TCP的发送、接收窗口:

TCP是双工协议,双方可以同时通信所以发送方接收方各自维护一个发送窗和接收窗。

发送窗:⽤来限制发送⽅可以发送的数据大小,其中发送窗⼝的大小由接收端返回的TCP报⽂段中

窗⼝字段来控制,接收⽅通过此字段告知发送⽅⾃⼰的缓冲(受系统、硬件等限制)大小(发送窗口大小由接收方来控制)

接收窗:标记可以接收数据的大小,其大小取决于系统、硬件等限制

TCP中发送的数据流可以分为:发送数据流、接收数据流;

发送数据流:已经发送已确认、已经发送但未确认、未发送但可以发送、不可发送

发送窗口包含的数据流:已发送但确认 + 未发但可发送

接收数据流:已经接收、未接收但准备接收、未接收且不准备接收

接收窗口包含的数据流:未接收但准备接收。

滑动窗口的具体实现例子如下图所示:

step1.接收方发送ack=10,发送方得知接收方已经接收10,需要发送11以及之后数据。在按序发送过程中,已经发送了11,12,发送方又一次接收到ack=10,但不影响发送窗口后面的数据发送。

因为发送窗口范围的数据都可以发送,不会等待前面数据的ACK确认,这也是滑动窗口能够有效提高发送效率的原因;

但是前面数据ACK确认会对发送窗口有影响:发送窗口只有收到接受端发来的ACK确认,才能移动发送窗口左边界;

step2.再之后会依次发送可以发送的数据。由于丢失11,发送端会继续发送11以及之后的数据,假设其中发送13又丢失了,但14,15被接收,确认11丢失。此时

发送窗口虽然没有接收到ACK11,但接收到ACK12,就通过下一个确认应答来进行确认,序号11也就得到确认(12之前的确认,11不就确认了)。发送窗口往后移动2个,接收窗口右边界也移动至13序号。

同时,接收窗口大小为3,发送窗口大小也会变化,变化为13-15(图中未改)

发送端会继续按序号13开始发送数据。

http://t.csdn.cn/6T0Up(这里博客有动画演示流程,更生动些)

​ 总结:

​ 滑动窗口的机制:

​ 1.发送窗口只有接收到发送窗口内字节的确认,才能移动发送窗口的左边界

​ 2.接收窗口只有前面所有端得到确认时,才会移动左边界(或者说往后移),如果一个字节数据虽然接收了,但之前还 存 在未收到的情况,窗口时不会移动(比如例子图中,13未接收,14,15虽然接收了,但也不能移动)

​ 3.遵循快速重传、累计确认、选择确认等规则。

​ TCP流量控制与拥塞控制

​ TCP流量控制:主要通过滑动窗口来实现流量控制。

发送窗口底层:基于 *** 作系统开辟的一块缓冲区(主要包含4个部分,见下图),用来存放需要发送的数据,其本质是循环数组的实现,主要通过三个指针来维护:

第一个指针指向:第一个已发送但未收到ack的字节;

第二个指针指向:第一个允许发送但未发送的字节;

第三个指针指向:表示发送窗口的大小

其实际 *** 作时,本质上是一个循环数组的实现,当接收到ack或者窗口大小发生变化时,只需要移动相应指针,当超过缓冲区最大地址后,利用循环头部,覆盖原本数据即可。

接收窗口底层:也是存在于 *** 作系统中开辟的一块缓冲区(包含三个部分),本质上也是循环数组利用两个指针维护

(一个指向第一个可以接收的字节,为:NECT_REC;一个指向接收窗口大小,为RWND),

在接收到数据时,接收端先发送ack应答包

其机制:

当接收窗口接收的数据序号大于REC_NEXT(指向第一个可以接收的字节),就往后移动REC_NEXT到第一个为空的位置上;

**当序号>REC_NEXT,**但是小于REC_NEXT+RWND(即为可以接收的数据),就将其加入到对应位置上;

如果序号

此外:介绍RWND变化情况:
接收窗口接收到数据包,RWND会减去一个对应包数据的长度;

应用程序读取了数据包,RWND会增加一个(对应数据包的长度)。

TCP流量控制与拥塞控制:

滑动窗口可以实现TCP中的流量控制。

流量控制,主要是防止发送方数据发送过快,导致数据容易丢失,可以通过滑动窗口的机制来控制发送方的发送速率。

了解了滑动窗口机制,流量控制就很好理解了(看上节)

拥塞控制:防止过多数据注入到网络中导致网络路由或者链路过载

流量控制与拥塞控制的区别:

1.流量控制属于(发送与接收)双方协商,拥塞控制涉及通信链路全局

2.实现方面:流量控制需要通信双⽅各维护⼀个发送窗、⼀个接收窗,对任意⼀⽅,接收窗⼤⼩由⾃身决定,发送窗⼤⼩由接收⽅响应的TCP报⽂段中窗⼝值确定;(由接收方确定)

拥塞控制的拥塞窗⼝⼤⼩变化由试探性发送⼀定数据量数据探查⽹络状况后⽽⾃适应调整。(发送方自己感知)

3.最终发送窗口的值为:min(流控发送窗口(接收通告窗口RWND),拥塞窗口(CWND));

拥塞控制

拥塞控制的目的:提高网络利用率、降低丢包率;

拥塞控制如何控制发送:通过一个拥塞窗口来限制发送的最大数据量;

拥塞发生的判断条件:

传输超时或者TCP重传定时器溢出

接收到重复的确认报文(一般是收到三个重复的报文就可以认为是拥塞发生了)

TCP主要的控制算法:慢开始、拥塞避免、快重传、快恢复;

慢开始与拥塞避免:(在第一种拥塞发生条件触发;如果第二种条件发生时,也出现TCP重传定时器溢出,也是使用该算法)

一开始令CWND=1(即只能发送一个报文段),当收到ack报文后,将CWDN加倍;(依次为2、4、8、16…………),并设置一个慢启动门限值ssthresh,当cwnd>=ssthresh,就会进入拥塞避免,每次将CWND+1SMSS(TCP报文段最大长度);

当出现超时,就让ssthresh=cwnd/2;再重新执行慢开始。

快重传与快恢复:(在第二种拥塞发生条件触发)

收到第三个重复确认报文段(比如序号为a),则此时会执行快速重传,重传a+1;在此情况下认为应该是个别报文段缺失,不是网络拥塞,那么就执行快恢复:ssthresh=cwnd/2,cwdn=ssthresh;此时就会进入拥塞避免阶段

慢开始于快恢复区别:

慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。

补充:

1.TCP带外数据

带外数据:用于迅速通告对方本端发生的重要事件。其特点在于:不管排在前面发送缓冲区的数据,应立即发送带外数据.

TCP利用其头部的紧急指针与紧急指针标志两个字段来实现一种紧急方式,其传递的数据含义和带外数据类似,就称为TCP紧急数据。

2.TCP粘包、拆包及解决办法(FROM https://zhuanlan.zhihu.com/p/108822858)

为什么常说 TCP 有粘包和拆包的问题而不说 UDP ?

由前两节可知,UDP 是基于报文发送的,UDP首部采用了 16bit 来指示 UDP 数据报文的长度,因此在应用层能很好的将不同的数据报文区分开,从而避免粘包和拆包的问题。

而 TCP 是基于字节流的,虽然应用层和 TCP 传输层之间的数据交互是大小不等的数据块,但是 TCP 并没有把这些数据块区分边界,仅仅是一连串没有结构的字节流;另外从 TCP 的帧结构也可以看出,在 TCP 的首部没有表示数据长度的字段,基于上面两点,在使用 TCP 传输数据时,才有粘包或者拆包现象发生的可能。

什么是粘包、拆包?

假设 Client 向 Server 连续发送了两个数据包,用 packet1 和 packet2 来表示,那么服务端收到的数据可以分为三种情况,现列举如下:

第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象。

第二种情况,接收端只收到一个数据包,但是这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。

第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。

为什么会发生 TCP 粘包、拆包?

  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包。
  • 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包。
  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

粘包、拆包解决办法

由于 TCP 本身是面向字节流的,无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,归纳如下:

  • **消息定长:**发送端将每个数据包封装为固定长度(不够的可以通过补 0 填充),这样接收端每次接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
  • **设置消息边界:**服务端从网络流中按消息边界分离出消息内容。在包尾增加回车换行符进行分割,例如 FTP 协议。
  • **将消息分为消息头和消息体:**消息头中包含表示消息总长度(或者消息体长度)的字段。
    行拆包。
  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

粘包、拆包解决办法

由于 TCP 本身是面向字节流的,无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,归纳如下:

  • **消息定长:**发送端将每个数据包封装为固定长度(不够的可以通过补 0 填充),这样接收端每次接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
  • **设置消息边界:**服务端从网络流中按消息边界分离出消息内容。在包尾增加回车换行符进行分割,例如 FTP 协议。
  • **将消息分为消息头和消息体:**消息头中包含表示消息总长度(或者消息体长度)的字段。
  • 更复杂的应用层协议比如 Netty 中实现的一些协议都对粘包、拆包做了很好的处理。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/788485.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存