网络编程 - TCP协议

一,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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/109686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树模型(三)决策树

决策树是什么?决策树(decision tree)是一种基本的分类与回归方法。 长方形代表判断模块 (decision block),椭圆形成代表终止模块(terminating block),表示已经得出结论,可以终止运行。从判断模块引出的左右箭头称作为分支(branch)…

【AI视野·今日CV 计算机视觉论文速览 第268期】Mon, 16 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Mon, 16 Oct 2023 Totally 61 papers 👉上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Vision-by-Language for Training-Free Compositional Image Retrieval Authors Shyamgopal Karthik, Karsten Roth, Massi…

ESP RainMaker 客户案例 #2|PitPat

PitPat 是美国领先的健身品牌,致力于通过游戏化的方式改变人们的锻炼习惯,增强健康。该品牌通过智能设备和相关的移动应用程序为从事家庭锻炼的个人提供虚拟跑步体验。目前,PitPat 针对不同受众群体,开发了Superun,Dee…

从入门到进阶 之 ElasticSearch 文档、分词器 进阶篇

🌹 以上分享 ElasticSearch 文档、分词器 进阶篇,如有问题请指教写。🌹🌹 如你对技术也感兴趣,欢迎交流。🌹🌹🌹 如有需要,请👍点赞💖收藏&#…

【Electron】Not allowed to load local resource

问题描述 使用 audio 标签播放音频文件,控制台报错 Not allowed to load local resource。 原因分析 通常是安全策略所引起的。Electron 默认情况下禁止加载本地资源,以防止潜在的安全风险。 解决方案 在 main.js 中找到创建 BrowserWindow 实例的代码…

latex:使用中文字体

解决方案 我这里使用的是gbsn(其他的字体我不知道,如果有补充请评价),详细说明如下:

Python学习基础笔记七十二——IDE集成开发环境

集成开发环境,英文缩写是IDE。 IDE可以帮你更高效地开发项目代码。因为它提供了非常实用的功能,比如项目文件管理、语法高亮、代码导航、自动补齐代码、语法静态检查、调试、版本控制等等。 两款IDE:Pycharm和VSCode。 pycharm中的代码文件都…

万界星空科技/生产制造执行MES系统/开源MES/免费MES

开源系统概述: 万界星空科技免费MES、开源MES、商业开源MES、市面上最好的开源MES、MES源代码、免费MES、免费智能制造系统、免费排产系统、免费排班系统、免费质检系统、免费生产计划系统、免费数字化大屏。 万界星空开源MES制造执行系统的Java开源版本。开源mes…

Qtday2

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {//窗口设置ui->setupUi(this);this->setWindowTitle("原神");this->setWindowIcon(QIcon(":/picture/genshi…

在数组中合并相同id数据,并且数据中某一字段不一致也统一合并进去

封装的合并的函数 function formateArray(data:any){// ts-ignorelet res data.reduce((ac,a) > {// ts-ignorelet index ac.findIndex(x > x.id a.id);index -1 ? ac.push({...a}) : ac[index] {...ac[index],...a};return ac;},[])return res;}使用 allData 原始…

【jmeter】接口测试流程

1、Jmeter简介 Jmeter是由Apache公司开发的一个纯Java的开源项目,即可以用于做接口测试也可以用于做性能测试。 Jmeter具备高移植性,可以实现跨平台运行。 Jmeter可以实现分布式负载。 Jmeter采用多线程,允许通过多个线程并发取样或通过独…

Cron表达式每隔两小时执行一次

Cron表达式每隔两小时执行一次 0 0 */2 * * ?验证正确性

算法刷题总结(全)

刷题总结 by lds 2023-9-5 文章目录 1.数组/字符串1.1 合并两个有序数组【easy】1.2 移除元素【easy】1.3 删除有序数组中的重复项【easy】1.4 删除有序数组中的重复项II【mid】1.5 多数元素【easy】1.6 大数相加---【美团面试手撕题目】1.7 轮转数组【mid】1.8 买卖股票的最佳…

掌握C++魔法:深入解析类与对象(上篇)

W...Y的主页 😊 代码仓库分享 💕 🍔前言: 之前我们学习了从C语言转到C后我们需要知道的一些关键改动与变化。今天我们就要学习C独有的类与对象。在谈论类与对象之前我们先说一下什么是面向对象的C,什么是面向过程的C语…

NSDT孪生编辑器助力智慧城市

技术有能力改变城市的运作方式,提高效率,为游客和居民提供更好的体验,实现更可持续的运营和更好的决策。 当今城市面临的主要挑战是什么,成为智慧城市如何帮助克服这些挑战? 我们生活在一个日益城市化的世界&#xf…

【复盘】记录一次数据库连接资源占用完毕

背景 因为历史原因项目使用的是JDBC原始SQL,然后进行拉去三方数据进行解析分析。跑了一会之后发现影响到了线上业务,连接错误。 一查看其实就是刚才跑的定时导致的,分析了下没有及时释放数据库连接。导致资源耗尽。数据库异常。 所以这里其实…

Java身份证OCR识别 - 阿里云API【识别准确率超过99%】

1. 阿里云API市场 https://market.aliyun.com/products/57124001/cmapi00063618.html?spm5176.28261954.J_7341193060.41.60e52f3drduOTh&scm20140722.S_market%40%40API%E5%B8%82%E5%9C%BA%40%40cmapi00063618._.ID_market%40%40API%E5%B8%82%E5%9C%BA%40%40cmapi0006361…

美国科技消费品公司Society Brands完成2500万美元融资

来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,总部位于美国俄亥俄州坎顿的科技消费品公司Society Brands今日宣布已完成2500万美元融资。 本轮融资由Gullane Capital领投,Callais Capital和North Coast Ventures跟投。 该公司打算利…

【一文清晰】单元测试到底是什么?应该怎么做?

我是java程序员出身,后来因为工作原因转到到了测试开发岗位。测试开发工作很多年后,现在是一名自由职业者 1、什么是单元测试 2、该怎么做单元测试 一、什么是单元测试? 单元测试(unit testing),是指对软件…

手机拍照转机器人末端坐标(九点标定法)

1.打印标定纸,随机九个点 2.让UR机器人末端分别走到P1-P9九个点 在图示位置读取九个点的X,Y坐标 3.手机拍照(固定点) 测试可以随机拍一张,实用的话需要固定手机的拍照位置,得到的图片如下: 4.…