TCP 协议

TCP协议

TCP ,即 Transmission Control Protocol ,传输控制协议。人如其名,要对数据的传输进行一个详细的控制。
TCP协议具有 有连接,可靠传输,面向字节流, 全双工的特点

TCP协议段格式

  • /目的端口号:表示数据是从哪个进程来,到哪个进程去;
  • 32位序号/32位确认号:后面详细讲;
  • 4TCP报头长度:表示该TCP头部有多少个32bit(有多少个4字节);所以TCP头部最大长度是
  • 4位首部长度:描述TCP报头具体的长度(TCP报头长度可变,UDP报头长度不可变,固定8个字节)

注意:4位首部长度的单位不是字节,而是4字节,所以TCP报头最大长度是15 * 4 = 60字节

  • 6位保留位:相比于 UDP 固定的长度,设计 TCP 时,给之后的扩充留了个退路,虽然现在不用,先占个位置,留着有需求了再使用(大概率用不到了)。

6位标志位:

  • URG:紧急指针是否有效
  • ACK:确认号是否有效
  • PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
  • RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
  • SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
  • FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段

  • 16位窗口大小:后面再说
  • 16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光 包含TCP首部,也包含TCP数据部分.
  • 16位紧急指针:标识哪部分数据是紧急数据;
  • 40字节头部选项:暂时忽略,选项之前的部分是固定长度(20字节)(公式 :选项长度=首部长度-20字节)

如果首部长度值是5,表示整个TCP报头长度是20字节(5x4字节)(相当于没有选项)
如果首部长度值是15,表示整个TCP报头长度是60字节(15x4字节)(选项相当于是40字节)

特性详解

确认应答(可靠性机制)

A给B发了个消息,B收到后就会返回一个应答报文(ACK),此时A收到应答之后,就知道刚才发的数据已经顺利被B接收。这个应答就是可靠性的一种一种体现。

ACK就冲当了这一角色。用来区分普通报文和应答报文。

确认应答(ACK)报文是TCP协议中用于确认已经接收到的数据包的一种控制报文。ACK报文通常不包含任何数据载荷,因为它只用于确认接收到的数据包的序列号。

  • 普通报文: ACK这一位为0

  • 应答报文: ACK这一位为1

考虑更复杂的情况

由于网络上可能存在“后发先至”,收到消息的顺序是可能存在变数的,变成了“去吃饭不?:不好”,“去学习不?:好”,这就跟原义不符。

为了解决上述后发先至问题,给传输的数据,和应答报文,各自进行编号。
示例图:

跟对编号就可以很明确的分出是哪个请求的应答.即使出现"后发先至"的情况也没有问题

而这个编号就对应TCP报文结构中的32位序号和32位确认序号

  • 32位序号: 针对请求数据进行编号
  • 32位确认序号: 确认序号的取值,是收到的数据的最后一个字节的序号+1,

实际上,由于TCP是面向字节流的,TCP的序号也是按照字节来编号的

TCP的字节的序号是依次累加的,每个TCP数据报报头填写的序号只需要写TCP数据的头一个字节的序号即可。
TCP知道了头一个字节的序号,再根据TCP报文长度,就很容易知道每个字节的序号。确认序号的取值,是收到的数据的最后一个字节的序号+1。


超时重传(可靠性机制)

超时重传是当发送方发送数据包后,如果在一定的时间内未收到接收方的确认消息(ACK),发送方会认为数据包可能丢失或发生错误,并会重新发送该数据包。

第一种情况:数据丢失

  • 主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B;
  • 如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发

超时时间如何确定?

一般操作系统中有一个配置项,描述超时时间的阈值.

如果第一次出现丢包,超出时间阈值后,进行重传.第二次的超时时间阈值就会比第一次更长.

如果重传几次依旧无法传输,就会发送复位报文(标志位中RST为1)来重置TCP连接

如果还是连不上,就会直接释放连接

第二种情况:ACK丢失

第二种情况重传数据,可能会导致主机B收到很多重复数据。TCP就要对其进行去重以及重新排列。

TCP存在一个“接收缓冲区”这样的存储空间(接收方操作系统内核里的一段内存),每个TCP的socket对象,都有一个接收缓冲区。
主机B收到主机A的数据,其实是B的网卡读到数据,并将这个数据放到B的对应socket的接收缓冲区中,后续应用程序使用getInputStream,进一步使用read从接收缓冲区中读取数据。


TCP使用这个接收缓冲区,根据数据的序列号识别是否有数据重复,如果重复,将后来的数据舍去,并对收到的数据进行重新排序(依赖于TCP报头的序号),排序后队首的序号超过新来的数据的序号,说明序号对应的数据已经被读取。

小结:由于去重和重新排序机制(都依赖于TCP报头的序号)的存在,发送方只要发现ACK没有按时到达,就会重传数据,即使重复了,顺序乱了,接收方都能很好地处理。

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

  • 其中确认应答描述的是传输顺利的情况。
  • 超时重传描述的是传输出现问题的情况。

连接管理 (可靠性机制)

三次握手

三次握手是在TCP协议中建立一个可靠的连接所使用的一种机制。它由发送方和接收方之间进行的三次通信组成,用于确保双方都愿意建立连接,并同步各自的初始序列号。

  1. 第一次握手(SYN):发送方向接收方发送一个带有SYN标志的数据包(SYN包),请求建立连接。发送方会随机选择一个初始序列号,并将它放在SYN包中的序列号字段中发送给接收方。
  2. 第二次握手(SYN+ACK):接收方收到SYN包后,会向发送方发送一个带有SYN和ACK标志的数据包(SYN+ACK包),表示接受建立连接的请求,并回复确认号(ACK)和自己的初始序列号。接收方还会随机选择一个初始序列号,并将它放在SYN+ACK包中的序列号字段中发送给发送方。
  3. 第三次握手(ACK):发送方收到SYN+ACK包后,会向接收方发送一个带有ACK标志的数据包(ACK包),确认接收方的确认号,并发送自己的确认号。接收方收到ACK包后,会确认发送方的。

为什么要建立连接以及建立连接的意义:

  1. 检查一下当前的网络情况是否畅通
  2. 三次握手也是在检查通信双方的发送能力和接收能力是正常的
  3. 三次握手过程中,也在协商一些重要的参数

两个重要的TCP状态:

  1. LISTEN:表示服务器正在监听来自客户端的连接请求。服务器在LISTEN状态下,等待客户端发起连接请求。
  2. ESTABLISHED:表示TCP连接已经建立,双方可以进行数据的传输。在ESTABLISHED状态下,双方可以互相发送数据包。

问题一:为什么要将中间两次交互合并?
答案:和封装分用相关,封装分用两次比封装分用一次成本更高(都是由内核发出和快)。

问题二:如果是两次握手能否建立连接的过程?
答案:不可以。
第一次握手:客户端发送网络包,服务端收到。
服务端可知:客户端的发送能力,服务端的接收能力正常。


第二次握手:服务端发送网络包,客户端收到。
客户端可知:客户端的发送能力,接收能力正常。服务端的发送能力,接受能力正常。但是此时服务端并不知道自己的发送能力是否正常。


第三次握手:客户端再次发送网络包,服务端收到。
服务端可知:客户端发送能力,接受能力正常。服务端的发送能力,接受能力正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常。

 

四次挥手

四次挥手和三次握手非常类似,都是通信双方各自向对方发起一个断开连接的请求,在各自给对方一个回应。

以下是四次挥手的步骤:

  1. 第一次挥手(FIN):发送方向接收方发送一个带有FIN标志的数据包(FIN包),表示发送方已经完成数据的发送,希望关闭连接。发送方不再发送数据,但仍然可以接收数据。
  2. 第二次挥手(ACK):接收方收到FIN包后,向发送方发送一个带有确认号(ACK)的数据包,表示已接收到发送方的关闭请求。接收方仍然可以发送数据。
  3. 第三次挥手(FIN):接收方向发送方发送一个带有FIN标志的数据包(FIN包),表示接收方也希望关闭连接。接收方停止发送数据,但仍然可以接收数据。
  4. 第四次挥手(ACK):发送方收到FIN包后,向接收方发送一个带有确认号(ACK)的数据包,表示已接收到接收方的关闭请求。发送方不再发送数据,也不再接收数据。

两个重要的TCP状态:

  1. CLOSE_WAIT:表示TCP连接的一方已经收到了对方的连接终止请求(FIN包),并发送了确认(ACK包)。在CLOSE_WAIT状态下,接收方等待应用层处理完数据后的连接关闭。
  2. TIME_WAIT:表示TCP连接的一方已经发送了连接终止请求(FIN包),并收到了对方的确认(ACK包)。在TIME_WAIT状态下,发送方等待一段时间后,保持连接状态清理(ACK包没有丢包),并释放资源。

问题一:为什么三次握手的中间两次j交互能够合并,而四次挥手不能合并?
答案:

三次握手这三次交互过程,是纯内核中完成的,服务器的系统内核收到SYN之后,就会立即发送ACK,也会立即发送SYN,同一时机,所以能够合并。


四次挥手中FIN的发起,不是由内核控制的,而是由应用程序调用socket的close方法(或者进程退出)才会触发FIN,ACK则是由内核控制,当接收到发送方发来的FIN之后,就会立即返回ACK,这两者之间通常情况下会有个时间差,因此无法合并。

问题二:服务器上出现大量的 CLOSE_WAIT 状态,是什么原因?

答案:服务器没有正确的关闭 socket,导致四次挥手没有正确完成。这是一个 BUG。只需要加上对应的 close 即可解决问题

出错重传

被动断开方发送FIN+ACK报文后,其主动方的ACK响应报文有可能丢失,这时候的被动断开方处于LAST-ACK状态的,由于收不到ACK确认被动方一直不能正常的进入CLOSED状态。在这种场景下,被动断开方会超时重传FIN+ACK断开响应报文,如果主动断开方在2MSL时间内,收到这个重传的FIN+ACK报文,会重传一次ACK报文,后再一次重新启动2MSL计时等待,这样,就能确保被动断开方能收到ACK报文,从而能确保被动方顺利进入到CLOSED状态。只有这样,双方都能够确保关闭。反过来说,如果主动断开方在发送完ACK响应报文后,不是进入TIME_WAIT状态去等待2MSL时间,而是立即释放连接,则将无法收到被动方重传的FIN+ACK报文,所以不会再发送一次ACK确认报文,此时处于LAST-ACK状态的被动断开方,无法正常进入到CLOSED状态。

滑动窗口 (提高效率)

上述讨论的确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做使得性能较差。

此时就要引用滑动窗口机制来提高性能。
具体操作:批量发送,批量等待,使用一份等待时间来等待一组数据的多个ACK,本质上就是降低确认应答等待ack消耗的时间

窗口大小:无需等待确认应答而可以继续发送数据的最大值,上图窗口大小就是4000;

  • 发送前四个段的时候,不需要等待任何ACK,直接发送;
  • 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;

下面是针对丢包的两种情况进行的讨论
情况一:数据包已经抵达,ACK丢了。

如上图第一个ack丢了,不用做任何处理也没关系,对于可靠传输没有任何影响.右边ack的数字,1001表示1001之前的数据都受到了,2001表示2001之前的数据都受到了,3001表示3001之前的数据都受到了.(后者包括前者).

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

情况二:数据包就直接丢了。

  • 当某一段报文段丢失之后,发送端会一直收到1001这样的ACK,是在提示发送端1001-2000的数据并没有接收到,应将对应的数据1001-2000重新发送。
  • 这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了,被放到接收缓冲区中。

 上述的重传过程,效率也是比较高的,并没有耽误后续数据的发送,这个称为"快速重传".

流量控制(可靠性机制)

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

  • 窗口太大,会消耗大量的系统资源
  • .窗口太大,完全不等待ack,就无法保证可靠性
  • .要考虑接收方的处理能力

问题二:TCP协议段格式中,16位窗口大小是否意味着窗口大小最大是64kb呢?
答案:不是,TCP通过在选项部分引入窗口扩展因子M,使得实际窗口大小是 窗口字段的值左移 M 。

流量控制的工作就是根据接收方的处理能力(通过查看接收方接受缓冲区的剩余大小),来协调发送方的发送速率 

接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段,通过ACK端通知发 送端;

如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一 个窗口探测数据段,使接收端把窗口大小告诉发送端。

发送端给接收端发送数据,接收端查看自身接收缓冲区剩余大小,并将这个值通过ACK报文返回给发送端,发送端会根据这个数字来确认下一轮发送的窗口大小。

当窗口大小为0时,发送方就要暂停发送,暂停发送的等待过程中,会给接收端定期发送窗口探测报文,这个报文不携带具体的业务数据,只是为了触发ack,查询窗口大小。

注意:窗口大小是“发送方”的概念,是通过接收方的ack报头里的窗口大小字段来告诉给发送方的。

拥塞控制(可靠性机制)

虽然 TCP 有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大 量的数据,仍然可能引发问题。
因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发 送大量的数据,是很有可能引起雪上加霜的。
流量控制考虑的是接收方的处理能力,接下来讲述的拥塞控制则是考虑传输过程中中间节点的处理能力,二者共同决定发送方的窗口大小(二者较小值)
TCP 引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传 输数据;
  • 0轮时窗口大小是1单位,已非常慢的速度发送数据,如果传输顺利,就使窗口大小扩大一倍。
  • 初始阶段,由于初始窗口比较小,每一轮不丢包就会使窗口大小扩大一倍(指数增长)。
  • 当增长速率达到阈值后,此时指数增长,就转化为线性增长(前提是不丢包的情况)。
  • 当传输过程中一旦出现丢包,说明此时发送的速率已经接近网络的极限,这时就把窗口大小一下缩成很小的值(重复上述指数增长和线性增长的过程)
  • 拥塞窗口不是固定数值,而是一直动态变化的。

延时应答(提高效率)

延时应答是指在TCP通信中,当一方发送数据给另一方时,接收方需要向发送方发送一个确认应答,表示已经成功接收到数据。

延时应答是指接收方在接收到数据后,不立即发送确认应答,而是等待一段时间后再发送确认应答。

如下图:

  • 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小。
  • 假设接收端缓冲区为1M。一次收到了500K的数据;如果立刻应答,返回的窗口就是500K
  • 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;
  • 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过 来;
  • 如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是 1M(窗口变大);

一定要记得,窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况 下尽量提高传输效率;
那么所有的包都可以延迟应答么?肯定也不是;
  • 数量限制:每隔N个包就应答一次;
  • 时间限制:超过最大延迟时间就应答一次

具体的数量和超时时间,依操作系统不同也有差异;一般N2,超时时间取200ms

 捎带应答(提高效率)

捎带应答是指在TCP通信中,接收方发送确认应答时,可以同时携带自己发送的数据。也就是说,在发送确认应答的同时,可以将自己需要发送的数据一起发送出去。

在网络通信中,典型的通信模型是一发一收

在TCP中,只要把数据发送过去,就会立即由内核返回一个ack报文.响应数据则是由应用程序里进行负责传输.

由于上述两个操作是不同时机传输的,原本是不能把这两个操作合并的,但是因为"延时应答"的存在,会等一会,因此就把上述两操作合并了

捎带应答的主要目的是为了减少网络传输的延迟和减少网络负载。在TCP通信中,接收方发送确认应答时会占用网络资源,而且会增加延迟。通过捎带应答,接收方可以在发送确认应答的同时,将自己需要发送的数据一起发送给发送方,减少了网络传输的次数和延迟。

面向字节流(编程注意事项)

面向字节流是指TCP协议在传输数据时将数据视为连续的字节流进行处理,而不是将数据分割成固定大小的块进行传输。

在TCP通信中,发送方将待发送的数据按照字节流的方式发送给接收方,接收方按照相同的字节流方式接收数据,并将数据重新组装成原始的数据块。

粘包问题是指在TCP通信中,发送方将多个小的数据包连续发送给接收方时,接收方可能会将这些数据包合并成一个大的数据包,导致数据的粘连,造成数据解析错误。

就比如我们看一篇没有标点符号的文章,对于那些字是一句话是有很多看法的.TCP也是如此,无法确定哪些是一个完整的应用层数据报。

要想解决"粘包问题",有两种办法:

  • 通过分隔符,约定某个符号作为包的结束标记
  • 通过指定包的长度,比如在数据包的开头位置声明长度


上述方法在自定义的应用层协议,就有典型的实现:

xml: 分隔符就是结束标签
json: 分隔符就是}
protobuf: 通过声明长度来确定边界
http: 分隔符+长度

TCP的连接异常处理(异常处理)

TCP协议在连接异常处理方面主要涉及以下几个方法:

  1. 程序崩溃
  2. 正常关机
  3. 主机突然关机
  4. 网线断开

程序崩溃就是进程异常退出,操作系统会回收进程的资源,包括释放文件的描述符表,相当于调用了socket里的close方法,进而触发FIN报文进行四次挥手。

正常关机,系统会强制结束所有进程,那么就和程序崩溃的情况是一样的,进行四次挥手。

主机突然关机:

  • 如果是接收方突然关机,发送方并不知道,就会继续发送数据,但发送方收不到ack报文,就会触发超时重传,如果重传几次后,依旧没有应答,就会重置连接,最后放弃连接。
  • 如果是发送方突然关机,接收方就只能等着,等一阵之后,就会发送一个"心跳包",确认连接是否正常

心跳包通常是一个小的数据包,由发送方定期发送给接收方。接收方在收到心跳包后,会立即发送一个确认应答给发送方,表示连接仍然活跃。如果发送方在一定时间内没有收到接收方的确认应答,就可以认为连接已经失效,可以进行相应的处理,如关闭连接或重新建立连接。

网线断开:

网线断开与主机突然关机的处理方式相同,分两种情况处理,接收方突然关机和发送方突然关机。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值