TCP协议和相关特性

1.TCP协议的报文结构

TCP的全称为:Transmission Control Protocol
特点:

  • 有连接
  • 可靠传输
  • 面向字节流
  • 全双工

下面是TCP的报文结构:
在这里插入图片描述

  • 源端口和目的端口:

源端口表示数据从哪个端口传输出来,目的端口表示数据传输到哪个端口去。

  • 32位序号和32位确认序号

用来区分应答报文是针对发送方的哪条消息进行的应答。

  • 4位TCP报头长度

表示TCP头部有多少个32bit(4个字节), 范围:0~15
所以报头的最大长度为:15 * 4 = 60字节

  • 6位标志位:每位标志位的值为0或者1,0代表标志位无效,1代表标志位有效。

URG:表示紧急指针是否有效
ACK:应答报文段,如果为1,表示这个报文是一个应答报文
PSH:提示接受端程序从TCP缓冲区把数据读走。
RST:复位报文段,表示重新建立连接
SYN:同步报文段,表示请求建立连接
FIN:结束报文段,通知对方本端要关闭了

  • 16位窗口大小

表示接收缓冲区的空闲空间

  • 16位校验和

发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验的数据不光
包含TCP首部,也包含TCP数据部分

  • 16位紧急指针:

表示哪部分数据是紧急数据

  • 40个字节头部选项:

提供一些特殊的功能或者选项
比如:
MSS (Maximum Segment Size): 最大分段大小
Windows Scale Factor: 滑动窗口的规模因子
Timestamps: 时间戳
NOOP (No Operation): 空操作选项

TCP设置那么多报文段的主要原因就是为了保证数据传输的可靠性。
可靠性是TCP协议的核心,而且TCP不仅想保证可靠性,还想让传输效率更高一点。
都说:”鱼和熊掌不可兼得“,可TCP全都要,可想而知这TCP肯定是个硬骨头。
那么TCP是如何保证可靠性并且提高效率的呢?下面就介绍TCP的一些特性。

2.确认应答

确认应答,简单点说就是,发送方发了一条数据,接收方要确认一下是否收到数据。确认应答是保证可靠性的核心机制。

根据具体的例子会更好理解,这还得从小明和小红的故事说起…

有一天,小明联系小红,请她看电影:

在这里插入图片描述
小红答应了,并且回复了:好呀好呀。
小红的回复就相当于应答报文ACK报文(Acknowledgement)。
通过小红的应答,小明就知道小红收到了消息。

两个人看了电影后,感情进一步提升,小明觉得机会来了,过了几天,又发消息给小红。
在这里插入图片描述
小红原本的回答:
在这里插入图片描述

但是在网络中,先发的消息很可能后到,结果小红的回答变成了:
在这里插入图片描述
这给小明开心坏了,虽然不能看电影,但是可以做自己的女朋友,这不是血赚吗?
在这里插入图片描述
为了解决这个问题就要引入序号确认序号
在这里插入图片描述
这样小明就知道,小红什么意思了。
小明已经哭晕在厕所里了。
在这里插入图片描述
但是TCP里的序号确认序号还跟上面的例子不一样。
下面来看看,TCP的序号确认序号是怎么回事?

TCP为每个字节都进行了编号。
在这里插入图片描述
TCP发送端发送带序号的报文,接收端返回带确认序号的报文。
在这里插入图片描述

  • ACK应答报文的确认序号跟发送过来的序号是不一样的。
  • ACK应答报文的序号是:发送方发送过来的所有数据,最后一个字节的下一个字节序号。
    就拿上面的例子来说,发送方总共发了100个字节,这一百个字节最后一个字节的序号是100,所以应答报文返回的是第100字节后的一个字节,确认序号就是101了。
    在这里插入图片描述

上面例子中,应答报文确认序号的含义:

  1. 小于101序号之前的数据已经收到
    在这里插入图片描述

  2. 向发送方索要从101序号开始的数据

3.超时重传

在讲超时重传之前还得知道为什么要超时重传,原因是丢包。
像平时打游戏,丢包就会变得一卡一卡的,那为什么会丢包呢?
网络中的数据是要经过很多设备的,各个设备不断转发,数据最终才传到目标主机那里去。
在这里插入图片描述
某一个时刻,某个设备,流量达到峰值的时候,数据再传过来,就可能导致丢包了。
1.接收方没收到发送方的数据。
在这里插入图片描述

发送方在一定的时间间隔没有收到ACK应答报文,就会重新发送数据。

2.接收方收到了数据,但是发给发送方的ACK应答报文,丢了!
在这里插入图片描述

接收方收到了数据,但是ACK应答报文丢了,发送方由于收不到ACK应答报文,然隔了一段时间又重新发数据给接收方。

注意:
在第2种情况下,接收方收到了两次数据,TCP会在接收缓冲区里根据收到数据的序号,把数据去重。


如果发生了多次丢包,多次重传,每次重传的时间间隔都会变大,而且累计到一定的次数就会认为接收端异常,直接关闭连接。

这里拿具体一点的数字举例子,比如:
从间隔1S重传,到间隔2S重传,到间隔4S重传…
重传的次数累计到了10次,直接关闭连接。

4.连接管理(三次握手,四次挥手)

在进行数据传输之前,TCP还必须进行连接,进行三次握手。
握手(Handshake)指的是通信双方,进行一次网络交互。
三次握手指的是,主机与主机之间进行三次交互,互相记录对方的信息,这就建立了连接。

TCP协议报头中,有这六个标志位,一个标志位占1位。
在这里插入图片描述
这六个报文段默认为0,如果为1,则有特殊含义。

SYN这一位为1,则表示当前TCP数据报是同步报文,表示申请连接
ACK这一位为1,则表示当前TCP数据报是应答报文

三次握手:
在这里插入图片描述

  • A向B发出SYN报文,第一次握手
  • B收到A发来的SYN同步报文,然后返回SYN+ACK报文,这个报文中SYN和ACK的标志为都为1。这是第二次握手
  • A收到SYN + ACK报文,再返回ACK应答报文,第三次握手

这三次握手的过程确定了双方的接收能力和发送能力,是否正常。
如果都没问题,接下来就可以传输数据了。

注意:
三次握手的过程全都由内核来完成,应用程序干预不了。
SYN报文和ACK报文都是由内核自动发送的。
当我们在进行TCP网络编程时,还没socket.accept()时,连接早已建立好了。
执行socket.accept()的时候才从操作系统内核把连接拿出来。


断开连接的时候要进行四次挥手。
在这里插入图片描述

  1. A向B发送FIN报文,第一次挥手
  2. B收到A的报文,并且回复一个ACK报文,第二次挥手
  3. 过了一段时间后,B向A发送FIN报文,第三次挥手
  4. A收到B的报文,并且回复一个ACK报文,第四次挥手

注意:
1.四次挥手中发送FIN报文是由应用程序来控制的,
    ~~~    当执行socket.close()的时候,才会发送FIN报文。
2.ACK报文则是由内核自动发送的。

5.滑动窗口

TCP不仅要保证可靠性,还要保证效率。
为什么需要滑动窗口?且看下图:
在这里插入图片描述
A发送数据,然后等B端的ACK, 假如等了0.3s,0.3s过后发送端收到了ACK了,再发送下一个数据。在此期间,A端白白等了0.3秒。是不是可以利用这0.3秒的时间,再发几条数据呢?


所以就有了滑动窗口,就是批量传输,批量传输不是无限地发送数据,而是发送到一定的程度,就等待ACK。而且是发送方收到一个ACK就立即发送下一条数据,发送方批量等待的数据是保持不变的。发送方批量等待的数据的数量,就称为窗口大小

举个例子:

1.A向B发送四个段的数据,每段1000个字节。
第一段:第1-第1000字节
第二段:第1001-2000字节
第三段:第2001-3000字节
第四段:第3001-4000字节
这四段数据无需等待ACK,直接发送。
在这里插入图片描述
2.B收到数据后,返回第一个ACK。

在这里插入图片描述
3.A收到ACK后,继续发送数据。

在这里插入图片描述

4.B发送第二个ACK。
在这里插入图片描述

5.A收到第二个ACK,继续发送下一条数据。
在这里插入图片描述
一直下去如此往复:
在这里插入图片描述

注意:

  • 在此过程中,发送方等待应答的数据一直都是4000个字节,即窗口大小一直保持在4000个字节。
  • 操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;
  • 窗口越大,则网络的吞吐率就越高

如下图:
在这里插入图片描述
当B发给A一个ACK,A收到ACK之后,就会继续发送数据,期间这个窗口的大小是不变的,而且是滑动的。


继续滑动:
在这里插入图片描述


批量传输的过程中,出现丢包该咋办?毕竟TCP还是以可靠性为主的,保证可靠性是TCP至始至终的目标。
在这里,丢包可分为两种情况:
1.接收方收到了数据,但是返回给发送方的ACK丢了。
在这里插入图片描述
这种情况下无所谓,等到下一次ACK成功发送的时候,发送方根据确认序号就知道数据有没有发送成功。

在这里插入图片描述
虽然确认序号为1001的ACK丢了,但是确认序号为2001的ACK发送方收到了。

发送方根据这个确认序号就知道第2001字节之前的数据,已经发送成功了。
这里假设发送方窗口大小为4000字节,发送方继续发送数据,
上图中一次发了两条数据:4001-5000字节和5001-6000字节。

发送方原本的窗口:
在这里插入图片描述
滑动之后的窗口:
在这里插入图片描述
2.发送方向接收方发送的包丢了。
在这里插入图片描述

接收方所做的处理。
在这里插入图片描述

如上图,发送方发送的1001-2000字节的数据丢了。
这里接收方不会因为接收到的是2001-3000字节就返回确认序号为3001的ACK。
而是继续返回确认序号为1001的ACK。

当返回了三次确认序号为1001的ACK之后,发送方就知道了,1001-2000字节的数据可能丢了。然后,发送方继续发送1001-2000字节的数据。
在这里插入图片描述

接收方返回确认序号为7001的ACK。
在这里插入图片描述
这里不是返回确认序号为2001的ACK,因为接收缓冲区里已经有了第7001字节之前的数据。
在这里插入图片描述

6.流量控制

在滑动窗口中,我们说,窗口越大效率越高。但是,如果一下子发太多了,把接收方缓冲区直接干满了。接下来如果继续发送数据,就会丢包。所以流量控制就是为了,防止数据发送太快,而导致丢包。

在TCP报文结构中有16位窗口大小的报文段。
在这里插入图片描述
当ACK这个标志位为1的时候,窗口大小就会生效,这个窗口大小表示的是接收方缓冲区里还剩的大小,0~65535字节。

下面具体说明:
1.发送方批量发送数据,接收方把数据放到接收缓冲区中。
在这里插入图片描述
2.接收方返回ACK,窗口大小为1000,发送方继续发送数据,接收方再把数据放到缓冲区中。
在这里插入图片描述

3.此时缓冲区满了,返回的ACK中窗口大小为0,发送方不会发送数据了。
在这里插入图片描述
4.一段时间过后,发送方会发送窗口探测报文,然后接收方继续返回缓冲区中的窗口大小。
在这里插入图片描述

5.缓冲区一部分数据被取走了。
在这里插入图片描述

6.发送方继续发送探测报文,得知缓冲区有空位了。
在这里插入图片描述
7.继续发送数据。
在这里插入图片描述

7.拥塞控制

滑动窗口的大小取决于流量控制和拥塞控制。
流量控制衡量了接收方的处理能力。拥塞控制衡量了传输路径的处理能力。

在网络中数据是要经过很多节点的。
在这里插入图片描述
如果中间任何一个设备达到处理能力上的瓶颈,都会影响整个网络的传输。

拥塞控制要做的就是衡量中间经过的路径,这些路径上有多少个节点,每个节点的处理能力情况等等。
这里要清楚两个概念,拥塞窗口和流控窗口。
这里所说的窗口不是TCP报文中的那个窗口大小,而是发送方发送完数据,发送方等待等待ACK的数据的数量。拥塞窗口是在拥塞控制试出来的窗口,流控窗口是流量控制中得出的窗口。

实际发送方的滑动窗口大小=min(拥塞窗口,流控窗口)

下面是拥塞控制的具体过程,也是拥塞窗口的变化过程:
1.刚开始传输,会给一个非常小的窗口,也叫做慢开始。
在这里插入图片描述
2.拥塞窗口指数级增长,可以短时间到达一个比较大的值,快速接近当前网络传输路径的能力瓶颈。
在这里插入图片描述
3.指数级增长变为线性增长,避免一下子超过网络能力上限,可以使得传输速度逐渐接近上限。
在这里插入图片描述
4.增长到一定程度,出现丢包,就认为当前窗口的大小已经达到了路径的传输上限。
在这里插入图片描述
5.立即把窗口大小降到一个很小的值,继续上述过程。
在这里插入图片描述

8.延时应答

延时应答是啥呢?就是字面上的意思,等一会再应答,即ACK不会立即发,等一会再发。
TCP中决定传输效率的关键元素就是发送方的窗口大小,而流量控制和拥塞控制共同决定了这个窗口的大小。就可以从这两个方面进行优化。而延时应答就是对流量控制做了优化。

  • 发送方不停地发数据,发到接收方的接收缓冲区里,同时应用程序也在不停地从接收缓冲区里取数据。
    在这里插入图片描述

假设接收缓冲区的大小是128KB,发送方发送了64KB的数据,那么缓冲区里还剩64KB的数据。

如果立即返回ACK,ACK报文里的窗口大小就是64KB。
发送方最多发送64KB的数据,发送完就又要等下一个ACK了。

如果等一会返回ACK,因为应用程序一直在从缓冲区里拿数据,所以这次返回的ACK中的窗口大小,大概率比64KB要大,发送方就可以发多一点数据。

9.捎带应答

捎带应答,顾名思义:应答的时候带了一点东西,就叫捎带应答。
而且捎带应答也是会延时一点时间再应答的。
如下图:客户端之间的通信一般都是一问一答的。

在这里插入图片描述

本来ACK和应用程序的应答是不同时机的,但是如果ACK延时一点发送就有可能和应用程序的应答合成一个报文,这样子效率就比分两次发要高一点。

在这里插入图片描述

10.面向字节流

因为TCP是面向字节流的,这会导致粘包问题。要明白粘包问题是啥,还得看具体的例子。
如下图所示:
TCP会根据序号把数据在缓冲区里拼起来,缓冲区里的数据是一长串。
在这里插入图片描述
从小红的角度看的话,他是知道小明要干什么的。因为每次小明发的消息都是以小红,小红开头的,而小明就蒙了,这一长串到底是啥意思。这就是粘包问题。

应用层拿到接收缓冲区的数据时,无法区分一个完整的应用层数据报。很容易就会拿错应用层数据报。

解决方法:
1.定义分隔符
2.约定一个应用层数据报的长度。

采用定义分隔符法,拿上面的例子说明,如果小明和小红每次发送数据是都会加一个\n,那么就很好区分一个应用层数据报了。
在这里插入图片描述
在这里插入图片描述
小明看到\n就知道,当前这个数据报到哪里结束了,也就明白对方什么意思了。

平常看到的jsonxml是用分隔符来实现的。

11.异常情况

1.进程关闭/进程崩溃。
进程没了socket是文件,随之也被关闭,但是连接还在,仍然可以进行四次挥手。

2.主机正常关机。
正常流程关机会杀死所有的用户进程。也会触发四次握手,如果发送端的FIN报文发过来了,但是还没来得及返回ACK报文就关机了,那么发送端会重新发送FIN报文,发送几次之后都没有ACK返回,发送方就会重置连接,如果还不行,干脆就释放连接了。

3.主机突然断电。

  • 断电的是接收方。
    发送方:收不到ACK=>重传=>重置连接=>释放连接。

  • 断电的是发送方。
    接收方不知道发送方发生什么情况了。是没来得及发送新的数据还是其他情况。
    TCP内置了心跳包。这个心跳包是周期性发送的。接收方会定期发送一个心跳包,如果发送方没有回应,接收方就能及时判断发送方是不是挂了。

4.网线断开。
网线断开和断电的情况是一样。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值