🌈前言
这篇文章给大家带来传输层中TCP协议学习!!!
🌸1、基本概念
-
TCP是隶属于传输层的协议,它的主要功能是实现是让应用程序之间可以相互通信
-
TCP全称为 “传输控制协议(Transmission Control Protocol”)主要对数据的传输进行一个详细的控制
-
TCP协议是一个可靠的(确认应答机制、超时重传等等)、面向连接的(通信前先建立连接)、基于字节流(流式IO)进行网络数据传输的网络通信协议
-
TCP在传输过程中可以正确的处理丢包、数据包乱序的异常状况;还能有效的利用宽带,缓解网络拥堵
🌺2、TCP协议报文结构
TCP协议是如何封包的呢?
-
封包的本质是:将TCP报头对象拷贝到应用层协议报文的前面即可,这样就完成了封包了
-
后续添加新的报头时,只要将缓冲区的指针移动到TCP头部然后加上新报头长度的大小,最后填充就行了
TCP是如何解包的呢?
-
解包的本质是:将TCP报头和TCP选项在数据包中去除即可
-
TCP报头中有一个"4位首部长度"字段,它标识了这个报头的长度(报头 + 选项),只要拿到首部长度然后根据首部长度去掉选项,剩下的就是有效载荷了
-
有效载荷包含了上层应用所需传输的数据,比如HTTP请求或响应内容
TCP协议是如何进行分用的呢?
-
TCP报头的前32位属性字段名是跟UDP一样,都是源端口号和目的端口号
-
分用的本质是:通过TCP报头里面的目的端口号字段找到应用层的具体协议,并且传递给上层应用程序进行处理
-
注意:这里没有说IP地址,因为IP地址是用来找网络中唯一主机的,现在已经找到了,通过目的端口号就能标定主机中唯一的进程了
下图为TCP报文的组成结构,里面包含了不同的属性字段:
🍨2.1、源端口号和目的端口号
-
源/目的端口号: 表示数据是从哪个进程来,到哪个进程去
-
16位源端口号:标识发送端主机上进行网络通信的某个进程(具有唯一性)
-
16位目的端口号:标识接收端主机上进行网络通信的某个进程(具有唯一性)
🍩2.2、4位首部长度
概念
-
TCP报头的标准长度是20个字节(不包括选项字段长度)
-
那么我们如何确定选项的大小呢? 答案就是:4位首部长度
-
4位首部长度:表示该TCP头部有多少个32位bit(4字节);4位的的取值范围是[0, 15](0000 - 1111)可以得出TCP报头最多有15 * 4 = 60个字节的数据
-
TCP报头标准长度为20字节,所以4位首部长度的值至少为5(0101)
-
我们解包时,就是通过四位首部长度来提取选项字段的!!!
🍪2.3、32位序号和确认序号(重点)
TCP可靠性问题
-
什么是不可靠:网络数据传输过程中导致丢包、数据包乱序、校验失败等问题
-
什么是可靠性:网络传输过程中不会出现丢包、乱序、校验失败等异常问题,确保数据包完整的到达对端
-
那么TCP是如何解决丢包问题的呢? 怎么确定一个报文是丢了还是没丢呢?
确认应答机制
-
TCP中的32位序号和32位确认序号就是为了防止丢包准备的
-
确认应答机制:数据在网络传输过程中没有发生丢包,完整的被对端收到,并且得到对端的应答
-
确认应答机制不能保证双方的应答,因为这是不可能的,永远只有一条消息是没有应答的,只能保证单向的应答
32位序号和确认序号
首先建立一个共识:TCP进行通信时,发出去的报文一定携带TCP报头,哪怕不携带数据
-
TCP是如何实现确认应答机制的呢? 答案是通过序号和确认序号实现的
-
实现原理:发送端给对方发送消息时会携带序号字段,接收端收到后回复消息会更新确认序号,回复给发送端后,发送端根据自己的序号和对方的确认序号可以判断数据是否应答(发送端到接收端的数据)
-
通俗的话说就是:网络传输时,发送端发送的数据,根据自身的序号和对方回复的确认序号可以确保单端的数据是否应答
-
比如:发送端要发送100个字节数据,携带的序号是1,接收端收到数据后,会更新自己的确认序号为101,回复给发送端后,发送端看到确认序号比自己的序号大于100,表示没有丢包
为什么TCP报文中需要二个不同的序号来完成确认应答机制呢?⭐⭐⭐⭐⭐
-
按照上面的说法,一个也能实现确认应答机制,为什么出现了二个不同的序号
-
我们都知道TCP在通信时是全双工的,意味着双方都能够进行发消息和收消息
假设如下图:
- 如果发送端发送消息,接收端应答消息并且发送新的消息呢???
总结:
-
只有单个序号只能保证一端到另外一端的数据应答
-
如果对端应答并且携带了新的消息,那么就不能保证对端到发送端的应答了
-
可以得出:发送端序号和对端确认序号可以保证发送端到对端数据的应答;对端序号和发送端确认序号可以保证对端到发送端的应答
-
对端给发送端发送消息,也要更新响应报文的序号,发送端收到报文更新确认序号然后回复给对端,对端比较自己的序号和发送端序号是否符合就能判断是否应答
🍫2.4、16位窗口大小
发送缓冲区和接收缓冲区
首先建立一个共识:这里的TCP协议发送缓冲区和接收缓冲区都是在内核中定义的
-
TCP需要保证报文的可靠性,需要发送缓冲区做各种可靠性的策略,而UDP不需要保证数据完整到达,所以没有发送缓冲区
-
缓冲区本质是一段连续的内存,它可以集中的处理数据刷新,减少I/O次数,从而达到提高整机的效率~!
-
发送缓冲区:当我们在应用层调用write、send系统函数向套接字写入数据时,进程会从用户态变为内核态,并且会将数据拷贝到内核的发送缓冲区中(用户的数据拷贝到内核中)
-
接收缓冲区:当我们在应用层调用read、recv系统函数向套接字接收数据时,进程会从用户态变为内核态,并且将内核中接收缓冲区的数据拷贝到用户所设置的buffer中存储着(内核数据拷贝到用户中)
-
数据被拷贝到内核的缓冲区中,就归OS管了(OS实现了传输层和网络层),用户不会再过问,什么时候传输是根据指定传输层协议来确定的
-
注意:write、send、read、recv都是面向字节流的,他们本身是不携带缓冲区的函数,但是它们向指定的套接字写入或接收数据,函数会根据传输层不同的协议放到缓冲区或从缓冲区中读取数据
16位窗口大小
-
缓冲区是有大小的,如果发送的数据太快或数据太大超过缓冲区大小,都会导致数据被丢弃(丢包),那么我们就要有个字段来获取对端的缓冲区接收能力(剩余空间大小)
-
16位窗口大小:用于填充缓冲区剩余空间大小的属性字段;可以让发送端智能的根据对端的接收能力来动态的调整发送的速度或数据的大小
-
如果服务器给客户端发报文,那么只能由服务器填充自己的窗口大小,对方就知道自己的缓冲区接收能力了
-
流量控制:不管是服务器给客户端发信息还是客户端给服务器发信息,只要有窗口大小存在,就能解决发送数据太快或太大,导致对端缓冲区已经接收不了数据还一直发的问题!!!
🍬2.5、常见的6位标记位
概念
-
我们都知道TCP协议在通信前要建立连接(三次握手)后才能正常通信,通信完后要进行断开连接(四次挥手)
-
服务器每次要处理那么多报文(连接报文、通信报文、断开连接报文等),需要对报文进行类别的(根据不同类型的报文用不同的逻辑处理它)
-
6位标记位:就是来标记报文类型的!!!
✨2.5.1、SYN和FIN标记位(三次握手和四次挥手)
概念
-
SYN:只要是建立连接请求的报文,SYN就要被设置为1;携带SYN标记位的报文称为同步报文段(连接请求报文)
-
只要是断开连接请求的报文,FIN就要被设置为1;携带FIN标记位的报文称为结束报文段(断开连接请求报文)
-
SYN和FIN标记位不可能被同时设置为1,因为不可能同时进行连接和断开连接
✱2.5.2、ACK标记位(确认标记位)
概念
-
ACK:确认标记位,表示该报文是对历史报文的确认(根据确认序号来进行确认),表示发送端发送的报文已经被对端收到,应答报文携带ACK标记位
-
历史报文:发送端发送数据给对端,对端应答发送端的报文就是”历史发送的报文“
-
一般在大部分正式通信情况下,ACK都是1
✴2.5.3、PSH标记位(数据推送标记位)
概念
-
PSH:提示接收端应用程序立刻从缓冲区把数据读走
-
应用层中的write、recv系统函数,在读取内核缓冲区时会自动判断是否存在数据,如果没有数据会一直阻塞,反之读取数据到用户设置的缓冲区中
-
TCP缓冲区中有一个接收数据的低水位线(比如有100字节,低水位线为20字节),只要传输的数据超过20字节,就会被上层给读取
假设
假设应用层一直非常的忙,没有时间读取TCP接收缓冲区里面的数据
-
如果TCP接收缓冲区满了,并且应答了一个窗口大小为0的报文,那么发送端只能等待对端读取完数据才能发送
-
如果等待了很久对端还是没有读取数据,那么发送端可以发送一个带有PSH标记位的报文给对端,催促对端应用层赶紧把缓冲区数据读取完
✵2.5.4、URG标记位(紧急指针标记位)和16位紧急指针
前言
-
报文在传输的过程中,是可能乱序到达的,它是不可靠其中的一种行为,TCP必须让我们发的报文按序到达
-
如果数据必须在TCP中进行按序到达,那么如果有一部分TCP报文优先级更高(PSH报文),但是序号比较晚,就无法做到报文被优先紧急处理
-
TCP是根据16位序号来实现按序达到的,因为序号可以确定每个报文中数据发送了多少字节(比如第一个报文发送了100字节,那么第二个报文序号就是101)
概念
-
URG:只要是发送紧急数据,就要把URG标记位置1;URG标志设置为1时,TCP首部紧急指针字段才有效,默认为0时,紧急指针无效
-
紧急指针:该字段中保存着一个正的偏移量,通过这个偏移量和序号相加,可以找到数据(有效载荷)中的紧急数据
-
紧急报文发送给对端是不会经过缓冲区保存按序等待读取的,会直接交付上层读取(紧急数据只有一个字节,其余数据要进入接收缓冲区)
🍺2.6、三次握手和RST标记位
概念
Server是服务器,Client是客户端
-
TCP是面向连接的协议,通信前需要建立连接(三次握手)
-
原理:Client向Server发送连接请求报文(携带SYN标记位),Server收到报文后应答连接请求报文(携带SYN+ACK标记位),Client收到报文后创建连接对象并且发送应答报文(携带ACK),Server收到应答报文也创建连接对象,到此,就完成了三次握手
-
只要最后一次握手Server端收到Client的应答报文,Server就会创建连接对象,表明建立连接成功了!!!
注意
-
TCP是保证数据完整的被对端收到,但是三次握手不一定会成功
-
第三次握手时Client发送的应答报文可能会丢包,没有被Server收到,Client已经处于连接成功状态,但是Server还未完成连接
原理
-
Client与Server建立好连接后,Server需要对连接进行管理,因为如果来了成千上万个连接,Server就会分不清谁是谁了
-
管理的本质就是先描述(连接的属性结构体),再组织(高效数据结构进行增删查改)
-
可以得出双方建立连接,需要花费时间和内存的,特别是Server还要管理连接对象,而Client只需创建好连接对象就行了
为什么要进行三次握手呢?一次二次不行吗?
一次握手
-
一次握手:一次握手是完全不行的,因为极其容易受到服务器攻击(SYN泛洪攻击),因为一次握手只要Client给我Server发送一个连接请求报文就能完成连接的建立了
-
但是如果Client发送完连接请求报文后就不管了,也不创建连接对象,但是Server会创建连接对象并且管理起来
-
如果Client循环式的发送SYN报文,那么Server的内存就会一下子被填满了,最后发生崩溃(创建连接对象要消耗内存)
二次握手
-
二次握手:跟一次握手一样不行,因为二次握手是由Server端最后应答连接报文给Client(意味着要先创建连接对象)
-
如果Client无视Server发送的应答连接报文或者直接丢弃,那么Server端维护的连接对象也就没有意义了,白白浪费资源
-
如果Client还是循环的发送SYN报文,那么服务器内存也会被一下子填满,导致崩溃
三次握手
-
前面的一、二次握手都不行,是因为Server端先认为自己建立连接成功(创建连接对象并且管理),只要Client不建立连接或忽略应答连接报文,那么Server就浪费资源了
-
三次握手:第三次握手由Server来结束握手,意味只要Server收到Client的应答报文,Server才会认为自己连接成功,但是Client在之前就认为自己建立连接成功了
-
好处就是Client如果想对Server进行攻击,循环发送连接报文,那么Client会先建立连接,而Server最后建立连接,双方都消耗了OS的内存资源
-
三次握手在Server收到ACK报文之前,都维持着一个半连接的方式(只有Client认为自己连接成功),只要Server收到ACK报文,那么就完成了一个完整的连接
✶2.5.5、RST标记位(复位标记位)
概念
-
RST:对方要求重新建立连接;携带RST标识的称为复位报文段
-
作用:发送带有RST标记位的报文,表示叫对方关闭连接,并且进行重新建立连接
-
用途:双方在建立连接失败或出现严重丢包等等时,就会给对端发送带有RST标记位的报文,表明要求重新建立连接或连接复位
假设
-
如果Client在第三次握手中发送的ACK报文没有被Server收到,但是Client认为自己建立连接成功了,但是Server认为还未建立连接
-
Client认为自己建立连接成功,向Server发送数据报文,Server收到报文后,想着自己还没有建立连接成功,说明最后一次ACK报文丢包了
-
Server会给Client发送的数据报文进行应答,并且把应答报文中的RST标记位也置为1,表示要求Client断开当前连接,并且重新建立连接
🌺3、TCP机制
🍨3.1、确认应答机制
概念
-
确认应答机制:主机A向主机B发送数据,主机B给主机A应答并且设置ACK标记位为1
-
ACK标记位设置为1的意思是对历史接收的报文的应答
序号问题
-
缓冲区是一段线性的内存,发送端将发送的数据拷贝到缓冲区中,也就意味着可以像数组一样用下标来进行访问
-
TCP每次发送报文都会设置序号(数据从哪里开始发送,也就是从哪个下标开始发送),每一个