TCP协议相关知识铺垫

一.TCP 协议段格式

TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能体现在它首部中的各字段的作用。因此,我们需要详细了解一下TCP首部各字段的作用。
在这里插入图片描述
序列号与确认号

序列号:表示本报文段所发送数据的第一个字节的编号。在TCP连接中所传送的字节流的每一个字节都会按顺序编号。由于序列号由32位表示,所以每2^32个字节,就会出现序列号回绕,再次从 0 开始。

确认号:表示接收方期望收到发送方下一个报文段的第一个字节数据的编号。也就是告诉发送发:我希望你(指发送方)下次发送的数据的第一个字节数据的编号是这个确认号。也就是告诉发送方:我希望你(指发送方)下次发送给我的TCP报文段的序列号字段的值是这个确认号。

六个标志位的作用:

1) 紧急URG(URGent) 当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。

2) 确认ACK(ACKnowledgment) 仅当ACK = 1时确认号字段才有效,当ACK = 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。

3) 推送 PSH(PuSH) 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。

9) 复位RST(ReSeT) 当RST=1时,表明TCP连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。

10) 同步SYN(SYNchronization) 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。

11) 终止FIN(FINis,意思是“完”“终”) 用来释放一个连接。当FIN=1时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。

二.TCP 的粘包问题

●首先要明确,粘包问题中的"包" ,是指的应用层的数据包.
●在TCP的协议头中,没有如同UDP一样的"报文长度"这样的字段
●站在传输层的角度, TCP是一个一个报文传送过来的.按照序号排好序放在接受缓冲区中
●站在应用层的角度看到的只是一 串连续的字节数据.
●那么应用程序看到了这么一个连串的字节数据,就不知道从哪个部分开始到哪个部分结束,是一个完整的应用层数据包,因此出现了粘包问题

TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一个包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。

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

第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象,此种情况不在本文的讨论范围内。
在这里插入图片描述
第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。
在这里插入图片描述
第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。
在这里插入图片描述
粘包、拆包发生原因

发生TCP粘包或拆包有很多原因,现列出常见的几点

1、待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。

2、要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。

3、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。


粘包、拆包解决办法一句话,明确两个包之间的边界

通过以上分析,我们清楚了粘包或拆包发生的原因,那么如何解决这个问题呢?

1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。

2、发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。

3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。


UDP会不会产生粘包问题呢?

UDP则是面向报文传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。

举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

三.滑动窗口

TCP的确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答.收到ACk后再发送下一个数据段.这样做有一个比较大的缺点,就是性能较差.尤其是数据往返的时间较长的时候。

既然这样一发一收的方式性能较低,那么我们就可以一次发送多条数据,就可以大大的提高性能,因此就出现了滑动窗口这一说。
在这里插入图片描述
需要注意的几点:

●滑动窗口的大小指的是 已发送未应答 和 还未发出的数据段 两部分组成的
●发送前三个段的时候, 不需要等待任何ACK,直接发送;
●收到第一个ACK后, 滑动窗口向右移动,继续发送第四个段的数据;依次类推;
●操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从发生缓冲区删掉(为了超时重传);
●滑动窗口越大,则网络的吞吐率就越高;’

形象展示:
在这里插入图片描述

发送方滑动窗口示意图:
在这里插入图片描述

上图中分成了四个部分,分别是:(其中那个黑模型就是滑动窗口)

#1:已收到ack确认的数据。使滑动窗口往右传

#2:已发出但还没收到ack的数据段。

#3:在滑动窗口中还没有发出的数据段(接收方还有空间)。

#4:窗口以外的数据,在发送方发送缓冲区中(接收方没空间容纳的)

注意:

滑动窗口里是 已发出但未收到ACk的数据段 和 还未发出的数据段 (但接收方还有空间接收的)两部分组成的

滑动后的示意图:
在这里插入图片描述

采用这种一次发送多个数据段的方式,可以提高传输效率。但是出现了丢包情况怎么办?

情况一:数据包已经到达,但是 ACK 丢失了

这种情况下, 部分ACK丢了并不要紧, 因为可以通过后续的ACK进行确认;

情况二:数据包丢失了

在这里插入图片描述

●当某一段报文段丢失之后,发送端会一直收到1001这样的ACK,就像是在提醒发送端"我想要的是1001"一样;
●如果发送端主机连续三次收到了同样-个"1 001"这样的应答,就会将对应的数据1001 - 2000重新发送;

这种机制被称为"高速重发控制"(也叫"快重传").

发布了62 篇原创文章 · 获赞 6 · 访问量 4454

猜你喜欢

转载自blog.csdn.net/HU1656/article/details/104610745