计算机网络原理--传输层协议(TCP协议十大特性)

 

目录

1.认识TCP协议

TCP的协议段格式

2. 确认应答机制

3.超时重传

4.连接管理

 三次握手

四次挥手

5.滑动窗口

6.流量控制

7.拥塞控制

8.延时应答

9.捎带应答

10.面向字节流

11.TCP异常情况


1.认识TCP协议

TCP协议是传输层的另一个重点协议

TCP,即Transmission Control Protocol,传输控制协议

TCP的协议段格式

我们看一下TCP的协议段格式

源端口号和目的端口号

同UDP协议,都是标识两端主机上进行通信的不同的应用程序

序列号和确认号

在下面的TCP重要机制解释中解释

4位首部长度

4位表示此处的首部长度是4bit位(0~15)首部长度的单位不是字节,而是4字节,一个TCP的报头长度是可变的,不像UDP中是固定的8个字节.

首部长度描述了TCP报头具体多长,表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是 15 * 4 = 60.

首部字节 - 20 得到的就是选项的长度,如果首部长度值为5,表示整个TCP报头是20字节(相当于没有选项.如果首部长度值为10,那整个报头就是40字节,选项长度为40-20=20字节)

保留位:是为了以后的扩展而设置的,对于网络协议来说,扩展升级是一件成本极高的事,就像UDP来说,报文长度事2字节,一个包最多传输64kb数据 .能不能升级一下?支持更大的长度呢.

理论上是可行的,但是操作成本极高,并且营销问题是最主要的..全世界所有的设备的操作系统里支持的就是2字节长度的UDP.要想进行升级,就得让操作系统同步升级,支持4字节的UDP..

这样的成本是接受不了的

如果引入了保留位.升级操作就会降低不少成本,如果TCP后续引入新功能了,就可以使用这些保留位的字段,此时对于TCP的报头影响是比较小的,也能兼容老的设备

 6位标志位:下文的TCP机制中详述

URG:紧急指针是否有效

ACK:确认号是否有效

PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走

RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段

SYN:请求建立连接,把携带SYN标识的称为同步报文段

FIN:通知对方,本端要关闭了,称携带FIN标识的为结束报文段

16位窗口下文详述,16位校验和同UDP校验和的作用 .16位紧急指针:标识哪部分数据是紧急数据.头部选项:暂时忽略


TCP对数据传输提供的管控机制体现在两个方面:安全和 效率,和多线程相似,在保证安全的前提下,尽可能地提升传输效率.接下来学习TCP中的重要机制

2. 确认应答机制

确认应答机制是实现可靠传输的最核心机制,可靠机制中的可靠不是说数据传送的很可靠,百分百传送到位,而是尽力传输数据,因为如果出现断网了,不可能传输成功.可靠是通过确认应答保证的,也就是发送数据后,接收方会返回一个应答报文,就可以让发送方清楚的知道数据是否传输成功

具体例子:

这个是比较简单的情况,一发一收 .A给B发送一个数据,B接收到数据后立即返回一个ACK( Acknowledge character.即是确认字符),也就是应答报文,来告诉A,数据传送成功了

还存在发送方连续发多条数据的情况.这种情况会存在"后发先至"的情况,那么收到数据的顺序是存在变数的,何况传输路径上的交换机路由器的转发速率也不同,到达情况就更存在变数了

这个现象是客观存在的,我们引入了序号来避免因为乱序带来的歧义

方法是对发送的数据和返回的应答报文都编号

 引入序号后,就不怕数据顺序错乱了,即使错乱了也可以通过序号来区分当前的应答报文是针对哪个数据进行的..

上述只是为了说清楚确认应答机制.

实际上TCP的序号并不是按照"一条两条"这样的方式来进行编号的,TCP是面向字节流的,TCP的序号也是按照字节来进行编号的!

 

TCP的字节的序号是依次累加的,这个依次累加的过程对于后一条数据来说,起始字节的序号就是上一个数据的最后一个字节的序号.每个TCP数据报报头填写的序号只需要写TCP数据的头一个字节的序号即可

TCP知道了头一个字节的序号,根据TCP的长度就知道每个字节的序号了

3.超时重传

上述确认应答是传输成功的情况,成功了就有ACK报文,要是没成功呢?

这就是传输过程中的丢包,丢包涉及到两种情况:

1.发送的数据报丢了

2.返回的ACK丢了

 这两种情况都是丢包,丢包是一个小概率事件,此时如果重新发送该数据,传输成功的可能是很大的

因此TCP引入了重传机制,在丢包的时候,就要重新发送一次丢失的数据

那么,到底是数据丢了,还是ACK还在返回的路上呢?TCP引入了一个时间阈值,发送方发送数据之后,就会等待ACK,此时开始计时,如果超过了时间阈值还没有ACK回来,就认为不管是数据丢了,还是ACK在返回的路上,都是丢包!

还有个问题,如果由于重传,导致接收方重复的数据都到了多次,应该怎么解决

TCP对于这样的重复传输的数据 进行了去重,TCP存在一个"接收缓冲区" 这样的存储空间,是操作系统内核中的一段内存,每个TCP的socket对象都有一个接收缓冲区,B收到A的数据,其实是B的网卡读到了数据,然后把数据放到B对应的接收缓冲区中,就像一个阻塞队列,发根据数据的序号,TCP很容易就能识别当前接收缓冲区里的两条数据是否是重复的,如果重复,直接将后来的数据丢弃!保证了程序调用到的read读取的数据是不重复的..前面说的数据会后发先至,顺序不同,这个缓冲区还会对数据进行排序,保证使用read读到的数据是有序的(和发送的数据顺序相同)

那么如果重传,又i出现丢包了呢?

传输成功概率是90%,三次连续丢包概率是0.1%

这时我们就认为是网络出现了故障,TCP会尝试重置连接(相当于断开连接),如果重置失败,就彻底断开链接了.重传的时间也不同,重传的轮次越大,超时时间间隔就越大


TCP可靠传输是通过 确认应答+超时重传来体现的

确认应答是描述传输顺利的情况,超时重传是描述传输出现故障的情况

两者相互配合共同支撑TCP的可靠性传输


4.连接管理

这里就是经典的TCP三次握手建立连接,四次挥手断开连接

 三次握手

看下图理解三次握手

这里的三次握手,实际上是四次..因为各自向对方发送建立连接请求, 并且再次向对方返回一个ACK.有四次信息交互

这里中间两次交互合并成了一次,构成了"三次握手"

中间两次发送,是必须合并的,因为封装分用一次一定比封装分用两次的成本更低.

三次握手还有一个重要作用就是确认双方各自的接收和发送能力,一定程度上也保证了TCP的可靠传输

三次握手意义:
1.通信双方建立连接,建立对对方的"认同"

2.验证通信双方各自接收发送信息的能力

3.握手过程中双方要协商一些重要的参数(TCP通信过程中,有些数据,需要通信双方相互同步,这时就需要有这样的交互过程,恰好可以利用三次握手来完成数据同步)

建立连接过程中主要认识两个状态:

LISTEN:服务器的状态

表示服务器已经准备就绪,随时可以有客户端来建立连接了.

ESTABLISHED:客户端和服务器都有的状态,表示连接已经建立完成了,可以正常通信 

SYN就是同步报文段,是客户端主动给服务器发送的建立连接的请求

四次挥手

和建立连接时的握手很相似,都是通信双方各自向对方发送一个断开连接的请求,再各自给对方一个回应

不同的是,三次握手中的中间两次交互是同一时机的,所以为了降低成本合并为了一次握手

这里不能合并,因为中间两次交互时机不相同

三次握手中的中间两次交互合并是因为交互过程是纯在内核中完成的,应用程序感知不到也无法干预.服务器收到syn之后就会立即发送ack,也会立即发送syn.

FIN的发起不是由内核控制的,而是由应用程序控制,调用socket的close方法(或者进程退出) 才会触发FIN

ACK则是由操作系统内核控制的,收到FIN之后立即返回ACK!但是服务器的应用程序并不一定结束,需要执行到对应的close方法才触发FIN

所以这两次交互的时机是不同的,中间隔了一个时间差.不能合并为一次挥手

断开连接过程中的主要状态:

CLOSE_WAIT:出现在被动发起断开连接的一方(服务器或客户端),等待关闭(等待调用socket的close方法关闭)

TIME_WAIT:出现在主动发起断开请求的一方,假设客户端发起的,客户端进入TIME_WAIT时,四次挥手相当于已经完了,但是还没有断开连接!TIME_WAIT这个状态就是保持连接先不要释放!

原因:最后一个ACK刚发出去,万一丢包了呢,会保持一段时间,没有收到重传的FIN,就认为最后一个ACK没丢,就彻底释放连接 

如果恰好ACK丢了,重传的FIN也丢了,那么就也会释放连接了,这种情况是很不幸的...

站在服务器角度,如果ACK没传过来,就认为是丢包了,FIN没发过去,要进行重传,如果连接已经断开,那就重传不了了..保持一段连接时间,才能继续重传.当然站在客户端角度,进入保持连接状态时,就已经四次挥手结束了,看起来是完成工作了,如果出现丢包情况,客户端还得做额外的工作!TIME_WAIT就是为了这个工作进行保障的!

TIME_WAIT具体保持多长时间呢?

2MSL.如果经历2MSL还没收到重传的FIN,就认为上个ACK顺利到达,服务器没有重传的FIN,就断开链接了..

MSL是一个经验值,1MSL=60s

5.滑动窗口

确认应答,超时重传,连接管理都是给TCP的可靠传输提供支撑的,但是引入了可靠性,也会付出一定的代价.传输效率就降低了.

UDP传输是不可靠的,但是传输效率高,两者是冲突的

为了尽可能提高TCP传输的效率, 引入了滑动窗口机制

滑动窗口本质是降低了确认应答机制中等待ACK的时间..集体的措施就是:批量发送,批量接受

把多份等待时间合并成一份(不等待的批量发送数据,使用一份的等待时间来等待一组数据的多个ACK),把能不用等待就能直接发送的数据的最大的量,称为"滑动窗口的大小"

发送前四个段的时候,不需要等待任何ACK,直接发送

收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据(4000~5000);依次类推

操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从缓冲区删掉

窗口越大,则网络的吞吐率就越高

那么在发送过程中也会出现丢包的情况,分为两类

1.ACK丢失

ACK丢失后,不需要做任何的处理,在之前讲给数据编序号时,序号的定义就是,表示从序号往前所有的数据都确认到达了.也就是当收到2001这个ACK时,代表是1~2000就都传输成功了,2001这个ACK涵盖了上一个1001的ACK的信息

2.数据丢失

 当某一段数据(1001~2000)丢失之后,发送端会一直收到1001的ACK,当连续三次收到i昂痛的ACK时,就对该数据进行重传,传输成功之后,再次返回的ACK就是7001了,代表7001之前所有的数据都已经传输成功!数据被放在接收端操作系统内核的接收缓冲区中了

6.流量控制

流量控制是一种干预发送窗口大小的机制

滑动窗口越大,传输效率越高,但是窗口也不能无限大

窗口太大,完全不等待ACK,可靠性失去保障

会消耗大量的系统资源

发送速度太快了,接收方会处理不过来

所以要对窗口进行控制

显然接收方的处理能力是一个重要依据,发送的速度不能超过处理速度

流量控制就是根据接收方的处理能力协调发送方的发送速率

 如何告诉发送方窗口大小呢?

当A给B发送一个数据后,将B的处理能力看作一个水池,B会计算剩余空间,将这个值通过ACK返回给发送方,A就根据这个值来确定发送速率,也就是窗口的大小

回忆我们的TCP首部中,有一个16位窗口字段,就是存放了窗口 大小信息(报文是ACK的时候,这个字段才有效),16位是否意味着窗口最大是64kb呢?其实不是这样,TCP为了扩大窗口,在选项部分引入了窗口扩展因子.如果窗口大小是64,扩展因子为2.那么64<<2=256kb.

滑动窗口大小是不固定的随着传输过程动态调整

当窗口大小为0,就要停止发送,等待过程中,会给接收方定期发送窗口探测报文*(不携带任何业务数据),只是为了触发ack查询窗口大小

7.拥塞控制

流量控制和拥塞控制共同决定滑动窗口大小是多少

流量控制考虑接收方的能力

拥塞控制考虑传输过程中中间节点的处理能力

流量控制只考虑了接收方的处理能力,没考虑中间节点的传输速率.

网络上有大量的计算机,也传输着大量的数据,网络状态被来就很拥堵,不清楚网络的状态情况下..如果贸然发送大量的数据,就会引起严重后果

TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据

慢启动只是初始比较慢,增长速度非常快

0轮时,窗口大小是1,以非常慢的速度发送数据,是1单位的数据

传输顺利,然后扩大窗口为原来的1倍,2,继续传输

......

初始阶段,由于初始窗口小,每一轮窗口都指数增长

当增长率达阈值之后,指数增长变为线性增长(第四轮),增长的前提是不丢包

线性增长过程中,一旦丢包了.说明发送速率到了网络中间节点承受的极限了,就把窗口大小缩成1

阈值减为最大阈值的一半,继续刚才的慢启动传输...拥塞窗口是一直变化的,随着时间的推移,逐渐达到一个平衡状态

当TCP开始启动的时候,慢启动阈值等于窗口最大值(24)

每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1

少量的丢包,我们仅仅是触发超时重传

大量的丢包,我们就认为网络拥塞

当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降

8.延时应答

延时应答也是提升效率的机制

如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小.如果收到数据之后,不是立即返回ACK,而是等待接收方消耗一些数据,剩余空间就更大了,返回的窗口就更大了!窗口越大,网络吞吐量就越大,传输效率就越高

延时应答也有限制

数量限制:每隔N个包就应答一次(一般取2)

时间限制:超过最大延迟时间就应答一次(一般200ms)

9.捎带应答

也是提高效率的方式,在延时应答的基础上引入的捎带应答

"一问一答"是典型的客户端服务器程序,请求和响应

 本来是四次交互,ACK是内核立即返回的,基于TCP的延时应答机制,在等待ACK的过程中B就要发送数据给A了.就可以让这个业务数据捎带着ACK一同发送给A了

10.面向字节流

TCP面向字节流的特性引入了"粘包问题"

由于TCP是面向字节流的,导致一次读1个字节或N个字节都是可以的

在TCP的协议头中,没有如同UDP一样的 "报文长度" 这样的字段,但是有一个序号这样的字段

站在传输层的角度,TCP是一个一个报文过来的,按照序号排好序放在缓冲区中

站在应用层的角度,看到的只是一串连续的字节数据

一次读到的数据可能是半个应用层的数据报,可能是一个应用层的数据报或多个应用层的数据报

那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个 完整的应用层数据包

 那么如何避免这个问题?

1.对于定长的包,保证每次都按固定大小读取即可(要约定好每个包的长度)

2.对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议,是程序猿自己来定 的,只要保证分隔符不和正文冲突即可)

11.TCP异常情况

传输过程中出现了不可抗力导致TCP异常

进程终止:进程终止会释放文件描述符,仍然可以发送FIN,进行四次挥手。和正常关闭没有什么区别

机器重启:和进程终止的情况相同

机器掉电/断网: 接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会 进行reset(重置)。即使没有写入操作,TCP自己也内置了一个保活定时器(心跳包),会定期询问对方是否还在。如果对方不在,也会把连接释放

猜你喜欢

转载自blog.csdn.net/chenchenchencl/article/details/128998481