网络编程 - TCP协议

目录

一,TCP基本概念

二,TCP协议端格式

 三,TCP原理

3.1 确认应答

3.2 超时重传

3.3 连接管理 - 建立连接+断开连接

3.3.1 三次握手 - 建立连接

3.3.2 四次挥手 - 断开连接 

3.4 滑动窗口

3.5 流量控制

 3.6 拥塞控制

3.7 延时应答

3.8 捎带应答

3.9 面向字节流

 3.10 异常情况的处理


一,TCP基本概念

TCP的特性:

  1.  TCP是有连接的:TCP想要通信,就需要先建立连接,之后才能通信
  2.  TCP是可靠传输:网络上进行通信,A给B发消息,这个消息是不可能做到100%送达的,所以这里的可靠传输是指A给B发消息,A能知道消息是否到达B,如果发送失败,A会采取一些措施,比如:数据重传
  3.  TCP是面向字节流的:TCP是以字节位单位进行传输的
  4.  TCP是全双工的:TCP可以实现双向通信

二,TCP协议端格式

 TCP协议中的源端口,目的端口和校验和与UDP协议中的一样,这里不过多赘述。我们先了解一些简单的,头部长度,保留和选项,其他的后面讲。

  • 头部长度:代表TCP报头的长度,报头最短是20个字节(即没有选项),最长是60个字节(即选项拉满),虽然只有 4 bit ,但是此处的单位是4个字节,所以可以表示60个字节的长度。
  • 选项:英文是option,可选择的。也就是说这个区域可以有,也可以没有,还有一点,选项的单位是4个字节,最大值是 40 个字节。
  • 保留:因为在UDP协议中,它的数据包长度只有64kb,改不了,所以大佬在设计TCP协议的时候,对其进行了优化,如果需要增加长度,就可以使用保留位。

 三,TCP原理

3.1 确认应答

为了实现可靠传输,发送方把数据发给接受方之后,接收方收到数据就会给发送方返回一个应答报文(acknowlege,简写 ack),如果发送方收到应答报文,就知道自己的数据发送成功了。但是在网络通信时,可能会出现下图的情况:

TCP协议要确保两个点:

  1. 确保应答报文和发送方发送的数据能匹配。
  2. 确保当发生 "后发先至" 的情况时,能够让程序仍然能按照正确的顺序读取数据。

 这个时候TCP协议中的序号和确认序号就发挥出作用了。序号和确认序号是相互匹配的。

假设序号从1开始,每次传输的TCP载荷中有1000个字节的数据,如下图:

如果发生后发先至的情况,我们的程序也会根据序号重新调整顺序。确保按照正确的顺序读取数据,这里还需要注意的是:我们如何来区分一个数据报是普通的数据,还是ack应答数据呢?

 ACK这一位是1,就表示当前数据报是一个ack应答报文,此时确认序号字段生效。这一位为0,就表示当前数据报是一个普通报文,此时确认序号字段不生效。

注:确认应答是实现TCP可靠传输最核心的机制!!!

3.2 超时重传

确认应答,描述的是一个比较理想的情况,但是数据在网络传输过程中,也有可能会出现 "丢包" 的情况,这时我们的发送方势必就无法收到ack应答报文了。这里的超时重传机制,就是对确认应答的一个补充。

首先先来了解一下为什么会发生 "丢包" ?我们可以将网络想象成错综复杂的高速公路,在高速公路上有许多的收费站,一到节假日,就会出现堵车。而在网络中,路由器/交换机就类似于收费站,如果数据包太多了,就会在路由器/交换机上出现"堵车",但是路由器对于"堵车"的处理,往往是比较粗暴的,它会将其中大部分的数据包直接丢掉,此时这些数据包就在网络上消失了。

实际上,丢包是一个随机事件,它是由当前的基础设施和网络环境决定的,因此在TCP传输过程中,丢包就存在两种情况:

 而发送方是无法区分这两种情况的,所以发送方都会进行 "重新传输"。发送方发送完数据之后,会等待一段时间,如果这个时间之内,收到了ack,此时一切正常,如果到达这个时间之后,还没收到ack,就会触发重传机制。

这里的等待时间是不确定的:

1)初始的等待时间始可配置的,不同系统上的都不一定一样

2)等待的时间也会动态变化,每多经历一次超时,等待的时间就会变长。而当时间长到一定的程度,就认为数据不可能传输成功了,就会触发TCP的重置连接操作(假设丢包概率为10%,第一次失败概率为10%,第二次失败概率就是1%,当出现多次丢包时,就说明丢包的概率远远大于10%,很可能出现了严重的网络故障,这时候传得再快也没用)

实际上,如果是 ack 丢了触发超时重传还会导致一个严重的问题 —— 就是接收方会收到两份一模一样的数据,比如你用线上支付时,ack丢了触发超时重传,你就会多付一份钱。但是在实际生活中却不会发生这种情况,这是为什么呢?

因为TCP协议已经帮我们把这个问题给解决掉了,TCP中存在一个 "接收缓冲区" (这是一块内存空间),它会保存当前已经接收到的数据,以及数据的序号,如果发送方发来的数据,是已经在接收缓冲区中存在的,是重复数据(根据序号来判断是否是重复数据),接收方会直接把这个后来的数据丢掉,确保程序进行 read 的时候不会读到重复数据。

此外,接收缓冲区也能进行重新排序,确保发送顺序和应用程序读取的顺序是一致的。

3.3 连接管理 - 建立连接+断开连接

这个机制是面试中对经典的问题:三次握手和四次挥手,下面我们来详细的讲一讲:

3.3.1 三次握手 - 建立连接

TCP这里的握手就是给对方发送一个简短的,没有业务数据的数据包,通过这个数据包来唤起对方的注意,从而触发后续的操作。(注:这里的握手操作不是TCP独有的,计算机中的很多操作都涉及到 "握手")

TCP的三次握手,是TCP在建立连接的过程中,需要通信双方一共打 "三次招呼" 才能完成连接,如下图所示:

 此时,握手完成,A 和 B 都记录了对方的信息,建立连接的过程,实际上是通信双方都给对方发起 syn,也都要给对方反馈 ack,一共是 4 次握手,但是中间两次,可以合并成一次,所以叫做 “三次握手”。为什么可以合并?因为 ack 和 syn 都是内核触发的,是同时触发,所以可以合并。

那么我们如何来区分一个数据包是不是 syn 呢?和 ack 一样,TCP协议中也有一块空间:

SYN这一位是1,就表示当前数据报是一个syn同步报文段。这一位为0,就表示当前数据报是一个普通报文。如果 SYN 和 ACK 同时为 1,就表示当前数据包既是一个 syn 同步报文段,也是一个 ack 应答报文。

了解三次握手的流程之后,这里还有两个问题:1. 三次握手解决了什么问题?2.为什么需要三次握手来建立连接,两次握手行不行?

1)TCP协议就是为了实现 "可靠传输",而 确认应答 和 超时重传 有一个大前提,就是当前的网络是通畅的,如果当前网络已经存在重大故障,那么可靠传输就无从谈起。而三次握手就是来检查当前网络是否是通畅的。

2)三次握手是为了让发送方和接收方都能确认自己的发送能力和接收能力均正常,靠两次握手实现不了,看图就理解了:

 总结三次握手的作用:

  1. 确认当前的网络是否畅通
  2. 让发送方和接收方都能知道双方的发送能力和接受能力均正常
  3. 让通信双发,在握手过程中,针对一些重要的参数进行协商

3.3.2 四次挥手 - 断开连接 

 与三次握手不同,此处的四次挥手,能否将中间的两次交互合二为一?答案是不一定:

  • 不能合并的原因:ACK 和 FIN 的触发时机是不同的,ACK 是内核响应的,B一接收FIN,就会立即返回 ACK,而 FIN 是应用程序的代码触发,B 这边调用 close 方法才会触发FIN,而在执行 close 代码之前,还需要多长时间是不确定的,所以不能合二为一。
  • 可能合并的原因:TCP中还有一个机制,延时应答(后面讲),能够拖延 ACK 的回应时间,一旦 ACK 滞后了,就有机会和下一个 FIN 合并了。

 这里还有一个注意点 —— 上图中存在TIME_WAIT 状态(哪一方主动断开连接,哪一方就会进入TIME_WAIT),为什么A接收到FIN,发送 ACK 之后不立即关闭呢?

TIME_WAIT 存在的意义就是为了防止最后一个ACK出现丢包,留下的后手。如果最后一个ACK丢了,站在B的角度,B就会触发超时重传,重新发送FIN,如果 A 没有 TIME_WAIT 状态,就意味着 A 已经关闭了,B就永远不可能收到 ACK 了,在多次触发超时重传后B也会关闭,但这样太浪费时间了。而如果 A 有 TIME_WAIT 状态,在放送ACK后,A会进入等待,等待的这个时间就是为了处理B重新发送的FIN,如果有重传的FIN,A会继续返回ACK。

TIME_WAIT 的等待时间是多长:假设网络上两个节点的通信消耗的最大时间是 MSL,那么此时的TIME_WAIT就是 2 MSL.

3.4 滑动窗口

前面的三个机制,都是在保证TCP协议的可靠传输,而TCP协议的可靠传输是会影响传输效率的,滑动窗口就是在保证可靠传输的前提下,提高传输效率的一个机制。

滑动窗口是通过缩短确认应答的等待时间来提高效率的,我们知道正常传输数据是:每收到一个应答报文,再发送下一个数据,在这个过程中,等待的时间比较长。而滑动窗口使用批量传输数据的方式,不用等待 ACK,直接发送下一个数据,如下图:

 这个窗口是有上限的,达到上限后,会统一等待ack,这个上限就是窗口的大小,之后就是返回一个ack,就再发送一个数据,从整体看就像一个滑动的窗口。

上述的滑动窗口中,确认应答是可以正常工作的,但是,如果出现了丢包怎么办?这里的重传,相比于超时重传又有所不同:

情况一:ack丢了

这种情况不需要任何重传,确认序号的含义是,当前序号之前的数据,已经确认收到了,你应该从确认序号这里继续发送,比如:上图中返回的确认序号1001丢了,但是返回的确认序号2001没丢,那么2001之前的数据都会确认为传输成功了,涵盖了1001的情况。

情况二:数据包丢了

 由于1001~2000这个数据没了,此处的ack仍然索要 1001,无论传过来的是几,都索要 1001。当 A 这边发现 B 连续几个ack都在索要 1001,A就知道1001这个数据丢了,就会重传 1001~2000的数据,重传之后,B 就会接着索要 5001 了。上述重传过程比较快,没有冗余操作,也称为快速重传。

注:这里的滑动窗口不是一直使用,如果通信双方,传输的数据量比较小,也不频繁,就是普通的确认应答;如果通信双方,传输的数据量比较打,也比较频繁,就会进入滑动窗口模式,按照快速重传的方式处理。

3.5 流量控制

滑动窗口的大小与流量控制有关,虽然窗口越大,传输的效率就越大,但是传输效率也会受到处理效率的制约,如果传输的速度太快,就可能会导致接收方处理不过来了,此时,接收方就会出现丢包,发送方还得重传,这就有点得不偿失了。所以发送方的发送效率,不能超过接收方的处理效率。

那这里就出现了一个问题:用什么来衡量接收方的处理能力?之前提过,TCP有一个接收缓冲区,A 发给 B 的数据,会先到达 B 的接收缓冲区,然后 B 再调用 read 这样的方法,把数据从缓冲区中读取出来,再进一步进行处理(一旦数据被 read,就可以从缓冲区中删除了),那么这里我们就可以把接收方,缓冲区剩余的空间大小,作为衡量处理能力的指标,剩余的空间越大,处理能力就越强;剩余空间越小,处理能力就越弱。

接收方每次收到数据后,都会把接收缓冲区的剩余空间大小通过ack应答报文返回给发送方,发送方就可以通过这个数值来调整下一轮的发送速度,如下图所示:

如果等待的时间超过了重发超时的时间,就会周期性的发送一个"窗口探测包",并不携带业务数据,就是为了触发 ack,为了查询当前接收方的接收缓冲区剩余空间。

TCP中的窗口大小就是用来存储接收缓冲区剩余空间大小的,但是这不代表接收缓冲区最大为64kb,因为再选项中也有一些参数是来存储接收缓冲区的。

 3.6 拥塞控制

流量控制,考虑的是接收方的处理能力,但是在网络传输过程中,不仅仅只有发送方和接收方参与,还有中间的路由器/交换机等也会参与,所以我们还需要考虑这些路由器/交换机的效率,但是这些节点,结构更加复杂,也就无法进行量化,因此我们使用 "实验" 的方式,来找到合适的值。

实验过程如图所示:

 

 注:流量控制和拥塞控制都是在限制滑动窗口的大小,最终发送的窗口大小,取 min(流量控制,拥塞控制)。

3.7 延时应答

正常情况下,A 把数据发给 B,B 就会立即返回 ack 给 A,但是也有时候,A 传输给 B ,此时 B 会等一会再返回 ack 给 A,这就是延时应答。

延时应答本质上也是为了提高传输效率,延时返回ack,就意味着接收方有更多的时间去处理数据,也就能读取更多数据,接收缓冲区的剩余空间就会更大,返回的窗口大小也会更大,从另外一个角度变相的增大了滑动窗口的大小。

3.8 捎带应答

捎带应答是在延时应答的基础上,进一步提高效率,在网络通信中,往往是这种 "一问一答" 这样的通信模型。

通过延时应答和捎带应答,ack 和 响应 就可以合并成一个数据包,一起返回给 A 了,与三次挥手类似。 (这里的需求和响应是业务上的数据)

3.9 面向字节流

这里有一个严重的问题——粘包问题(这个问题不是tcp独有的,而是面向字节流的机制都会有类似的问题)。什么是粘包问题,举一个例子:A 向 B 传输了三个数据,aaa,bbb,ccc,而储存到B接收缓冲区的是 aaabbbccc,B无法区分A传输了几个数据,以及每个数据的内容。

如何解决粘包问题?核心思路:通过定义好的应用层协议,明确应用层数据包之间的边界。有两种方式:1)引入分隔符 2)引入长度

1)假设分隔符为 \n

 2)引入长度

 3.10 异常情况的处理

在使用TCP的过程中,出现意外,会如何处理?

1)进程崩溃

就是进程异常终止了,文件描述符表也就释放了,此时会触发FIN,对方收到后,会返回FIN和 ACK,这边再返回FIN,就是正常的四次挥手断开连接(TCP的连接独立于线程之外)

2)主机关机(主动关机)

在关机过程中,会先触发强制进程终止操作,和1)相同,但是不仅仅是进程没了,整个系统也可能关闭了,如果在系统关闭之前,对端返回的FIN和ACK到了,此时系统可以返回ACK,进行正常的四次挥手。如果系统已经关闭了,FIN和ACK来迟了,系统无法返回ACK,站在对端的角度,以为是FIN丢包了,就会重传FIN,重传几次FIN都没响应,自然就会放弃连接。

3)主机掉电(非正常)

此时是一瞬间的事,来不及关闭进程,也来不及发送FIN

1. 接收方掉电,发送方在发送数据,等待ack,触发超时重传,多次重传后会触发TCP来连接重置功能,发送 "复位报文段",如果复位报文段发送之后还是没有响应,就会释放连接。

2. 发送方掉电,接收方在接收数据,接收方会一直等待数据,这时接收方无法区分是对方没发消息,还是对方挂了,TCP提供了心跳包机制:接收方也会周期性的给发送方发起一个特殊的,不携带业务数据的数据包,并且期望对方返回一个应答,如果对方没有应答,并且重复多次之后,仍然没有,就视为对方挂了,此时就可以单方面释放连接了。

4)网络断开 —— A给B发送数据

A就相当于  3)1

B就相当于  3)2

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶祇秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值