计算机网络(6) TCP协议

news2025/9/20 0:08:08

TCP(Transmission Control Protocol,传输控制协议)是互联网协议套件中一种核心协议。它提供面向连接的、可靠的字节流传输服务,确保数据从一端正确无误地传输到另一端。TCP的主要特点包括:

  1. 可靠性:TCP使用确认、重传、序列号等机制,确保数据包的可靠传输。
  2. 顺序传输:TCP保证数据按发送顺序到达接收方。
  3. 流量控制:TCP采用滑动窗口机制进行流量控制,防止发送方过快发送数据,导致接收方来不及处理。
  4. 拥塞控制:TCP检测网络拥塞情况,并通过调整发送速率来避免拥塞。

一.连接建立(三次握手)

1.1三次握手建立连接

 

TCP三次握手执行过程:

1)首先,服务端和客户端都是处于CLOSED状态的,然后服务端启动,监听端口,状态变为LISTEN(监听)状态;

2)客户端为了请求资源,发送连接,发送同步序列号SYN,此时客户端就变成了SYN-SEND状态

3)服务端接收到客户端请求之后,发送SYN和ACK,然后服务端状态就变成SYN-RCVD状态

4) 客户端接收到信息之后,再次发送ACK,然后变成ESTABLISHED(已确认)状态,服务端接收到返回信息后,状态也变成ESTABLISHED(已确认)状态

 

1.2TCP协议为什么需要三次握手?

知道了TCP的三次握手的基本工作原理之后,就可以解释为什么TCP需要三次握手?为什么不设计成两次握手就可以?

原因:避免重复链接

其实在RFC 793 Transmission Control Protocol里就有指出为什么要三次握手的原因:

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

翻译为中文大致意思是主要原因是为了防止旧的重复连接引起连接混乱问题。

比如在网络环境比较复杂的情况,客户端可能会连续发送多次请求。如果只设计成两次握手的情况,服务端只能一直接收请求,然后返回请求信息,也不知道客户端是否请求成功。这些过期请求的话就会造成网络连接的混乱。

所以设计成三次握手的情况,客户端在接收到服务端SEQ+1的返回消息之后,就会知道这个连接是历史连接,所以会发送报文给服务端,告诉服务端。

所以TCP设计成三次握手的目的就是为了避免重复连接。

然后可以设计成四次握手?五次握手?不可以?

答案是也是可以的,不过为了节省资源,三次握手就可以符合实际情况,所以就没必要设计成四次握手、五次握手等等情况。

1.3四次挥手关闭连接

1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。

2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

 

1.4模拟三次握手过程中的序列号

TCP连接的建立是通过三次握手来实现的:

序号	    方向      seq             ack       SYN      ACK
----------------------------------------------------------
1       A->B    10000(ISN)	     0          1        0
2       A<-B    20000(ISN)   10000+1=10001	1        1
3       A->B    10001        20000+1=20001  0        1

解释情况如下:

1: (A) –> [SYN] –> (B)

A向B发起连接请求,以一个随机数初始化A的seq,这里假设为10000,此时ACK=0

2: (A) <– [SYN/ACK] <–(B)

B收到A的连接请求后,也以一个随机数初始化B的seq,这里假设为20000,意思是:你的请求我已收到,我这方的数据流就从这个数开始。B的ACK是A的seq加1,即10000+1=10001

3: (A) –> [ACK] –> (B) A收到B的回复后,它的seq是它的上个请求的seq加1,即10000+1=10001,意思也是:你的回复我收到了,我这方的数据流就从这个数开始。A此时的ACK是B的seq加1,即20000+1=20001

1.5 数据传输过程

序号     方向       seq      ack                    数据长度    数据包长度
---------------------------------------------------------------------------
23       A->B	   40000     70000                    1460         1514
24       A<-B      70000     40000+1514-54=41460      0	           54
25       A->B      41460     70000+54-54=70000        1460         1514
26       A<-B      70000     41460+1514-54=42920      0            54

解释:

23:B接收到A发来的seq=40000,ack=70000,size=1514的数据包

24:于是B向A也发一个数据包,告诉A,你的上个包我收到了。A的seq就以它收到的数据包的ack填充,ack是它收到的数据包的seq加上数据包的大小(不包括:以太网协议头=14字节,IP头=20字节,TCP头=20字节),以证实B发过来的数据全收到了。

25:A在收到B发过来的ack为41460的数据包时,一看到41460,正好是它的上个数据包的seq加上包的大小,就明白,上次发送的数据包已安全到达。于是它再发一个数据包给B。

26:B->A这个正在发送的数据包的seq也以它收到的数据包的ack填充,ack 就以它收到的数据包的seq(70000)加上包的size(54)填充,即ack=70000+54-54(全是头长,没数据项)。通过tcpdump发现确认包ack,确认传输过程中最后字节长度。

参考:TCP-IP详解卷-基础知识 IP TCP UPD 协议

减去54的原因 ,以太网封装格式(链路层使用的是Ethernet II 格式,这个格式有14字节以太网首部+4字节以太网尾部): 应用数据=size-14-20-20=size-54。(假设IP首部和TCP首部都没有可选选项)

为什么不减去以太网尾部的4字节呢? 因为在物理层上网卡要先去掉前导同步码和帧开始定界符,然后对帧进行CRC检验,如果帧校验和错,就丢弃此帧。如果校验和正确,就判断帧的目的硬件地址是否符合自己的接收条件(目的地址是自己的物理硬件地址、广播地址、可接收的多播硬件地址等),如果符合,就将帧交“设备驱动程>序”做进一步处 理。这时我们的抓包软件才能抓到数据,因此,抓包软件抓到的是去掉前导同步码、帧开始分界符、FCS之外的数据

1.6 四次挥手过程中的序列号

序号      方向      seq      ack             FIN      ACK
--------------------------------------------------------
1        A->B      80000    90000           1         1
2        A<-B      90000    80000+1=80001   0         1
3        A<-B      90000    80001           1         1
4        A->B      80001    90000+1=90001   0         1

1:(A) –> [FIN/ACK] –> (B)

客户端A没有要发送给服务端B的数据了,想要关闭链接,则发送一个FIN=1,ACK=1的包,告诉B可以关闭连接了,我没有什么数据要给你了。

2:(A) <– [ACK] <– (B)

然后B会发送ACK=1的包给A,告诉A我知道你没有什么想给我的了,但是我还有数据要给你,你先等下,我先不想FINISH呢。

3:(A) <– [FIN/ACK] <– (B)

等B把数据都发送给A之后,B会再次发送一个包,这次FIN=1,表示我这边也想关闭了,咱俩一起关把。在2和3之间,可能还会有很多B->A的传递,ack均为80001。

4:(A) –> [ACK] –> (B)

然后A回应一个ACK,表示我知道了,一起关吧。B收到这个ACK后,就会CLOSE。但是实际上A不会直接CLOSE,还会进入一个等待时间状态TIME_WAIT,持续2倍的MSL(Maximum Segment Lifetime,报文段在网络上能存活的最大时间)。过了这个状态,才会CLOSE。


为什么要由一个TIME_WAIT阶段呢?原因主要有两个:

1) 保证TCP的全双工连接能够可靠关闭

假如A发送的最后一次ACK丢包了,没有被B收到,那B超时之后,会再次发送一个FIN包,然后这个包被处于TIME_WAIT状态的A收到,A会再次发送一个ACK包,并重新开始计时,一直循环这个过程,直到A在TIME_WAIT的整个过程中都没有收到B发过来的FIN包,这说明B已经收到了A的ACK包并CLOSE了,因此A这时候才可以安心CLOSE。如果A没有TIME_WAIT状态而是直接close,那么当ACK丢包之后,B会再次发送一个FIN包,但是这个包不会被A回应,因此B最终会收到RST,误以为是连接错误,不符合可靠连接的要求。因此需要等待ACK报文到达B。

RST是TCP数据报中6个控制位之一,6个控制位的作用如下:

  • URG 紧急:当 URG=1 时,它告诉系统此报文中有紧急数据,应优先传送(比如紧急关闭),这要与紧急指针字段配合使用。

  • ACK 确认:仅当 ACK=1 时确认号字段才有效。建立 TCP 连接后,所有报文段都必须把 ACK 字段置为 1。

  • PSH 推送:若 TCP 连接的一端希望另一端立即响应,PSH 字段便可以“催促”对方,不再等到缓存区填满才发送。

  • RST复位:若 TCP 连接出现严重差错,RST 置为 1,断开 TCP 连接,再重新建立连接。

  • SYN 同步:用于建立和释放连接,稍后会详细介绍。

  • FIN 终止:用于释放连接,当 FIN=1,表明发送方已经发送完毕,要求释放 TCP 连接。

2)保证这次连接的重复数据段从网络中消失

如果A直接close了,然后向B发起了一个新的TCP连接,可能两个连接的端口号相同。一般不会有什么问题,但是如果旧的连接有一些数据堵塞了,没有达到B呢,新的握手连接就已经到B了,那么这时候,由于区分不同TCP连接是依据套接字,因此B会将这批迟到的数据认为是新的连接的数据,导致数据混乱(源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字,新旧连接的套接字很有可能相同)如果我们终止一个客户程序,并立即重新启动这个客户程序,则这个新客户程序将不能重用相同的本地端口。服务端处于被动关闭,不会出现该状态。

二.模拟字节流发送与协议包装过程

假设我们要发送一个包含“Hello, TCP!”消息的数据流,TCP如何处理这个数据流?

  1. 数据分割: 假设每个TCP段最多能携带5个字节的数据,消息“Hello, TCP!”将被分割为以下三个部分:

    • 第一段:Hello
    • 第二段:, TC
    • 第三段:P!
  2. 添加TCP头: 每个数据段将添加一个TCP头,包含源端口号、目的端口号、序列号、确认号、数据偏移、控制位、窗口大小、校验和和紧急指针等字段。

TCP协议头

TCP协议头的格式如下图所示,每个字段的长度和作用分别解释如下:

  0      4      8     12       16     20     24     28     32
  ----------------------------------------------------------
 |         Source Port         |      Destination Port       |
  ----------------------------------------------------------
 |                      Sequence Number                      |
  ----------------------------------------------------------
 |                    Acknowledgment Number                  |
  ----------------------------------------------------------
 | Data | Reser- |   Flags   |          Window Size          |
 |Offset|  ved   |           |                               |
  ----------------------------------------------------------
 |         Checksum           |        Urgent Pointer        |
  ----------------------------------------------------------
 |                    Options (if any)                       |
  ----------------------------------------------------------
 |                    Data (variable length)                 |
  ----------------------------------------------------------

序号(seq)用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则TCP用序号对每个字节进行计数。序号是32bit的无符号数,序号到达2^32-1后又从0开始。

当建立一个新的连接时,SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(InitialSequenceNumber)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号。既然每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号(ack)应当是上次已成功收到数据字节序号加1。只有ACK标志(下面介绍)为1时确认序号字段才有效。

发送ACK无需任何代价,因为32bit的确认序号字段和ACK标志一样,总是TCP首部的一部分。因此,我们看到一旦一个连接建立起来,这个字段总是被设置,ACK标志也总是被设置为1。

TCP为应用层提供全双工服务。这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。

 

字段解释
  1. 源端口号(Source Port,16位):发送方的端口号。
  2. 目的端口号(Destination Port,16位):接收方的端口号。
  3. 序列号(Sequence Number,32位):发送数据段的序列号,用于确保数据包按顺序到达。
  4. 确认号(Acknowledgment Number,32位):确认接收数据的序列号。
  5. 数据偏移(Data Offset,4位):表示TCP头部的长度(以32位字为单位),即TCP数据开始的偏移量。
  6. 保留位(Reserved,6位):保留位,当前未使用。
  7. 控制位(Flags,6位):包括多种控制标志位,如SYN、ACK、FIN等,分别表示连接建立、确认、结束等状态。
  8. 窗口大小(Window Size,16位):用于流量控制,表示接收方当前能够接收的数据量。
  9. 校验和(Checksum,16位):用于差错校验,确保数据完整性。
  10. 紧急指针(Urgent Pointer,16位):指出紧急数据的指针位置(如果设置了URG标志)。
  11. 选项(Options,可变长度):可选字段,用于扩展TCP协议的功能。

通过上述机制,TCP提供了一个可靠、顺序、面向连接的数据传输服务,确保数据能够在复杂的网络环境中正确传输。

序列号(Sequence Number)和确认号(Acknowledgment Number)的作用

序列号(Sequence Number)
  1. 定义: 序列号是一个32位的字段,用于标识TCP段中第一个字节的数据的编号。

  2. 作用

    • 数据分段:在数据传输过程中,发送方将数据分成多个段,每个段的第一个字节会分配一个序列号。这有助于接收方重新组装数据。
    • 数据重组:接收方根据序列号将数据包按顺序重新组装,即使数据包乱序到达也能正确排序。
    • 确认机制:序列号配合确认号实现数据的确认和重传。发送方在发送数据后,等待接收方的确认,如果未收到确认则认为数据可能丢失,进行重传。
确认号(Acknowledgment Number)
  1. 定义: 确认号也是一个32位的字段,用于告诉发送方接收方已经成功收到的数据。

  2. 作用

    • 确认接收:确认号表示接收方期望接收的下一个字节的序列号。例如,确认号为1001表示接收方已经成功接收到序列号为1到1000的所有数据。
    • 可靠传输:通过确认号,发送方可以知道哪些数据段已经成功到达接收方,不需要重传,从而确保数据的可靠传输。
    • 流量控制:通过确认号,接收方可以控制发送方的数据发送速率,防止数据包过多而导致网络拥塞。

序列号与确认号的工作流程

以下是一个示例来说明序列号和确认号在数据传输过程中的作用:

  1. 连接建立(握手)

    • 第一步:客户端发送一个SYN段,序列号为X。
    • 第二步:服务器接收到SYN段后,发送一个SYN-ACK段,序列号为Y,确认号为X+1。
    • 第三步:客户端接收到SYN-ACK段后,发送一个ACK段,确认号为Y+1,序列号为X+1,连接建立。
  2. 数据传输

    • 发送方发送数据:假设客户端发送一个数据段,序列号为1001,数据长度为500字节。
    • 接收方确认数据:服务器接收到这个数据段后,发送一个确认段,确认号为1501(表示1001到1500字节的数据已经成功接收)。
    • 发送方继续发送数据:客户端根据接收方的确认号,发送序列号为1501的下一个数据段。
  3. 重传机制

    • 数据丢失:如果某个数据段丢失或损坏,例如,序列号为2001的数据段没有被接收方确认。
    • 重传数据:客户端会在一定时间内未收到确认后,重新发送序列号为2001的数据段。

示例

假设我们有一个包含“Hello, TCP!”的消息,并假设每个TCP段最多能携带5个字节的数据,消息将被分割如下:

  • 第一段Hello,序列号1001
  • 第二段, TC,序列号1006
  • 第三段P!,序列号1011

传输过程

  1. 发送第一段:序列号1001,数据“Hello”
    • 接收确认:确认号1006(表示已经收到序列号1001到1005的数据)
  2. 发送第二段:序列号1006,数据“, TC”
    • 接收确认:确认号1011(表示已经收到序列号1006到1010的数据)
  3. 发送第三段:序列号1011,数据“P!”
    • 接收确认:确认号1013(表示已经收到序列号1011到1012的数据)

通过序列号和确认号的配合,TCP能够实现可靠的数据传输,确保数据按顺序且无误地到达接收方。

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

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

相关文章

怎么脚本ai创作?分享三个方法

怎么脚本ai创作&#xff1f;在数字化时代&#xff0c;AI技术正逐渐渗透到我们生活的方方面面&#xff0c;其中AI脚本创作软件的出现&#xff0c;极大地提高了创作效率&#xff0c;降低了创作门槛。今天&#xff0c;就为大家推荐三款备受好评的AI脚本创作软件&#xff0c;其中聪…

MyBatis 获取参数的两种方式

${paramName} 使用这种方式的结果是直接替换。 #{paramName} 使用这种方式的实现是占位符。&#xff08;?&#xff09;

昂辉科技EasySAR-BootLoader上位机产品

近年来&#xff0c;硬件标准化、同质化和软件差异化、复杂化成为了汽车产品研发的重要趋势。与此同时&#xff0c;大量的智能化功能和快速上车的节奏&#xff0c;对软件开发提出了更高的要求。在软硬件解耦的大背景下&#xff0c;建立统一的软件体系和开发工具以紧跟硬件更新迭…

UE4中性能优化工具合集

UE4中性能优化工具合集 简述CPUUnreal InsightUnreal ProfilerSimpleperfAndroid StudioPerfettoXCode TimeprofilerBest Practice GPUAdreno GPUMali GPUAndroid GPU Inspector (AGI) 内存堆内存分析Android StudioLoliProfilerUE5 Memory InsightsUnity Mono 内存MemreportRH…

JavaScript 基础 - 第2天【函数】

文章目录 前言一、声明和调用1、声明&#xff08;定义&#xff09;2、调用 二、参数三、返回值四、作用域1、全局作用域2、局部作用域 五、匿名函数1、函数表达式2、立即执行函数 前言 理解封装的意义&#xff0c;能够通过函数的声明实现逻辑的封装&#xff0c;知道对象数据类…

基于pytorch的LSTM模型训练与预测(附源码)

1.LSTM模型 关于这个模型的基本概念长短时记忆网络(LSTM)(超详细 |附训练代码)_lstm代码-CSDN博客可以查看这篇文章&#xff0c;写得很详细&#xff0c;我觉得能大概看明白反向传递各个部分的推导就行了。 2.pytorch的安装 我使用的是conda环境&#xff0c;在参照网上的安装过程…

EndNote 专业的文献管理软件下载,强大的引用和参考文献生成功能

EndNote&#xff0c;它以其强大的功能和便捷的操作赢得了广大学术工作者的青睐&#xff0c;成为了他们不可或缺的研究助手。 EndNote软件的出现&#xff0c;极大地简化了学术文献的管理和组织工作。用户只需将收集到的文献导入软件&#xff0c;便可轻松实现对文献的分类、排序和…

【APP移动端自动化测试】第四节.元素操作的API

文章目录 前言一、点击&输入&清空操作 1.1 点击元素 1.2 输入&清空元素二、获取文本内容&位置&大小操作 2.1 获取文本内容 2.2 获取位置&大小三、根据属性名获取属性值操作四、滑动和拖拽操作 4.1 _swipe 4.2 _scroll …

20240613确认飞凌的OK3588-C的核心板的HDMI IN功能

20240613确认飞凌的OK3588-C的核心板的HDMI IN功能 2024/6/13 16:09 v4l2-ctl --list-devices v4l2-ctl --list-formats-ext -d /dev/video8 【这里必须拿到HDMI OUT机芯的分辨率。如果拿不到&#xff0c;肯定硬件异常/线没有接好】 v4l2-ctl -V -d /dev/video8 gst-launch-1.…

宏电“灌区哨兵”助力灌区信息化建设,开启灌区“智水”时代

灌区是保障国家粮食安全的重要水利设施。“十四五”提出&#xff0c;要推进大中型灌区节水改造和精细化管理。灌区信息化是建设智慧水利、深化行业监管、提升灌区科学管理水平的基础支撑&#xff0c;也是“十四五”期间灌区现代化改造的重点内容之一。 宏电智慧灌区信息化解决方…

探秘扩散模型:训练算法与采样算法的双重解读

很早之前就新建了一个专栏从0开始弃坑扩散模型,但发了一篇文章就没有继续这一系列&#xff0c;在这个AIGC的时代&#xff0c;于是我准备重启这个专栏。 整个专栏的学习顺序可以见这篇汇总文章 很多时候我们看到一篇文章很长&#xff0c;难免会心生胆怯&#xff0c;所以我将这些…

docker的教程长亭

把我的常用docker写在这里 之前用 vul - hub 靶场经常用 现在docker不知道为什么挂了 开启 docker-compose up -d 关闭 docker-compose down docker ps 只是运行 docker ps -a 所有 包括停止 docker ps -q 只看id docker stop <container_name_or_id> docker 的容器…

Pinia的简要概述

Pinia是专为 Vue 应用程序开发的状态管理库&#xff0c;Pinia 支持 Vue2和 Vue3。在 Vue3项目中,既可以使用传统的 Vuex实现状态管理&#xff0c;又可以使用 Pinia 实现状态管理。本章将对 Pinia的基本使用方法进行讲解。 一.Pinia 概述 Pinia 是新一代的轻量级状态管理库&…

Echarts图表实现X轴自动滚动加载数据

1.用到了Echarts图表自带的dataZoom组件 2.使用定时器定时刷新数据 效果图 关键代码 dataZoom: [{xAxisData: 0,//这里是从X轴的0刻度开始show: false,//是否显示滑动条type: slider, // 这个 dataZoom 组件是 slider 型 dataZoom 组件startValue: 0, // 从头开始。endValue:…

小程序 UI 风格,构建美妙视觉

小程序 UI 风格&#xff0c;构建美妙视觉

数据结构——队列(Queue)详解

1.队列&#xff08;Queue&#xff09; 1.1概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out)的性质 入队列&#xff1a;进行插入操作的一端称为队尾(Tail/Rear) 出…

10054 远程主机强迫关闭了一个现有的连接

在连接数据库SQLserver&#xff0c;时可能会出现如下的故障&#xff0c;特别是当操作系统是Win11时&#xff0c;在连接比较旧的版本数据库时会出现这样的问题&#xff1a;10054 远程主机强迫关闭了一个现有的连接。报错如下图所示&#xff1a; 根据微软的官方网站&#xff0c;查…

U盘文件损坏且无法读取怎么修复?五个方法帮你搞定

在现代社会&#xff0c;U盘已经是我们日常生活和工作中不可缺少的工具之一。U盘的容量大、体积小、携带方便&#xff0c;很多人都喜欢使用U盘用于个人和工作数据的存储和传输。但是&#xff0c;U盘和其他的电子设备一样&#xff0c;在试用期间有时候会出现U盘打不开提示目录结构…

jadx+android studio+雷电模拟器 动态调试apk

# 环境准备 1.雷电模拟器&#xff0c;开启root 2.jadx&#xff1a; https://sourceforge.net/projects/jadx.mirror/files/v1.5.0/jadx-gui-1.5.0-with-jre-win.zip/download 3.java jdk 11 https://www.oracle.com/cn/java/technologies/javase/jdk11-archive-downloads.…

【CT】LeetCode手撕—21. 合并两个有序链表

目录 题目1-思路2- 实现⭐21. 合并两个有序链表——题解思路 3- ACM实现 题目 原题连接&#xff1a;21. 合并两个有序链表 1-思路 双指针&#xff1a;题目提供的 list1 和 list2 就是两个双指针 通过每次移动 list1 和 list2 并判断二者的值&#xff0c;判断完成后将其 插入…