【TCP】传输控制协议

图片

前言

TCP(Transmission Control Protocol)即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。它由IETF的RFC 793定义,为互联网中的数据通信提供了稳定的传输机制。TCP在不可靠的IP层之上实现了数据传输的可靠性,通过使用确认、重传和错误检测等技术来确保数据的正确到达。

TCP的特点:

  1. 面向连接:TCP在数据传输之前需要建立连接,通信结束后会断开连接。

  2. 可靠传输:TCP确保数据包正确无误地从源点传送到目的地,若数据包在传输过程中丢失或出错,会被重新发送。

  3. 全双工通信:TCP允许数据在两个方向上同时传输,提高了通信效率。

  4. 流量控制:TCP通过滑动窗口机制进行流量控制,避免快速发送方压倒慢速接收方。

  5. 拥塞控制:TCP实施拥塞控制策略来避免网络拥塞,如慢启动、拥塞避免、快速重传和快速恢复等。

  6. 有序传输:TCP保证数据按发送时的顺序到达接收端。

  7. 可变大小的滑动窗口:TCP使用可变大小的滑动窗口来动态调整数据传输速率。

TCP 面向连接

TCP的面向连接特性确保了数据传输的稳定性和可靠性。具体如下:

  • 三次握手建立连接:在数据传输开始之前,TCP通过所谓的“三次握手”机制来建立一个连接。这个过程包括一个SYN(同步)信号的交换,以及确认信号ACK(应答),从而确保双方都准备好进行通信。

  • 一对一通信:一旦建立了连接,TCP就会在这个连接上进行一对一的数据交换。这意味着发送方和接收方之间建立了一条虚拟的、直接的通道,数据包会按照发送顺序到达对方。

  • 四次挥手断开连接:当数据传输完成后,TCP使用“四次挥手”过程来有序地关闭连接。这个过程涉及到FIN(结束)信号的交换和相应的ACK信号,确保双方都同意关闭连接,并且所有数据都已经传输完毕。

  • 可靠性保障:面向连接的特性还意味着TCP能够保证数据的可靠传输。它通过序列号、确认应答、重传机制等来确保每个数据包都能够正确地到达目的地,即使在网络条件不理想的情况下也能保持数据的完整性。

  • 流量控制和拥塞控制:TCP的面向连接特性还包括了流量控制和拥塞控制机制。这些机制帮助TCP适应网络的变化,避免因为数据发送过快而导致的网络拥塞或者接收方处理不过来的情况。

TCP的面向连接特性为网络通信提供了一种稳定可靠的方式,它通过建立和维护一个持续的连接状态,确保了数据的按序到达和完整性,同时也支持了复杂的流量控制和拥塞控制算法,以适应不断变化的网络环境。

TCP 可靠传输

TCP的可靠传输机制是其核心特性之一,确保了数据在网络中的完整性和顺序性。以下是TCP实现可靠传输的几个关键方法:

  • 检验和:TCP在发送数据时会计算检验和,并将其附加到数据包中。接收方在收到数据包后会重新计算检验和并与数据包中的检验和进行比对,以此来检测数据在传输过程中是否出现了错误。

  • 序列号/确认应答:TCP为每个数据包分配一个序列号,接收方在收到数据包后会发送一个确认应答,其中包含了期望收到的下一个序列号。这样发送方就可以知道哪些数据包已经被成功接收。

  • 超时重传:如果在规定的时间内没有收到确认应答,发送方会认为数据包丢失并重新发送。这个超时时间是通过自适应算法来确定的,以适应不同的网络条件。

  • 滑动窗口控制:TCP使用滑动窗口机制来进行流量控制,避免发送方发送的数据量超过接收方的处理能力。窗口的大小可以根据网络状况动态调整。

  • 拥塞控制:当网络出现拥塞时,TCP会减少数据的发送速率,以防止网络拥塞的进一步恶化。拥塞控制算法会根据网络的反馈信息来调整发送速率。

此外,TCP通过这些机制确保了即使在不可靠的IP网络上也能实现数据的可靠传输。它通过建立和维护一个持续的连接状态,确保了数据的按序到达和完整性,同时也支持了复杂的流量控制和拥塞控制算法,以适应不断变化的网络环境。

TCP 全双工通信

TCP的全双工通信指的是在同一连接中,两端设备都能够同时发送和接收数据。

首先,在全双工通信模式下,数据传输可以在两个方向上同时进行,而不需要等待对方回应。这意味着一个设备在发送数据的同时,也能够接收对方发送过来的数据。这种模式显著提高了通信的效率,因为它避免了半双工通信中必须等待一方完成发送后另一方才能开始发送的延迟。

其次,TCP协议通过其设计确保了这种全双工通信的实现。例如,TCP的报文段(Segment)包含了序列号和确认号等信息,使得设备可以连续发送数据并独立地确认收到的数据。此外,TCP的滑动窗口机制也支持全双工通信,允许每个方向上的流量控制独立于对方。

最后,TCP的这种双向通信能力是通过建立持久的连接来实现的,这个连接在数据交换完成之前一直保持活跃状态。即使在没有数据发送的时候,连接也不被关闭,从而允许任何一方在任何时间发送数据。

TCP的全双工通信为网络应用提供了高效、可靠的双向数据传输能力,它是TCP协议能够广泛应用的重要原因之一。

TCP 流量控制

TCP的流量控制是通过滑动窗口机制来实现的,它的目的是防止数据包丢失并确保网络资源的有效利用。

TCP流量控制的核心在于调节数据传输的速度,以确保发送方不会过快地发送数据以至于接收方无法及时处理。这种控制机制主要通过以下方式实现:

滑动窗口机制:TCP使用滑动窗口来控制数据的流动。窗口的大小表示了接收方能够接收的数据量。发送方会根据这个窗口的大小来调整自己的发送速率,确保不会超出接收方的处理能力。

窗口大小动态调整:接收方根据自身的接收缓存空间和处理能力,动态地调整窗口的大小,并通过TCP报文段中的窗口字段告知发送方。这样,发送方就可以根据最新的窗口大小来发送数据,避免过多的数据导致接收方缓冲区溢出。

避免分组丢失:流量控制的最终目的是为了避免分组丢失。如果发送方发送数据过快,超出了网络的传输能力或者接收方的处理速度,就可能导致数据包在网络中丢失。通过流量控制,可以确保数据包的顺利传输,减少重传的需要,提高网络的效率。

此外,在实际的网络环境中,由于各种因素的影响,如网络延迟、带宽变化等,TCP的流量控制机制需要不断地适应这些变化,以保持最佳的传输效率。例如,当网络拥塞时,TCP会减小窗口大小以降低发送速率,而当网络状况好转时,则会增大窗口大小以提高传输效率。

TCP的流量控制是一个复杂的过程,它涉及到多个参数和算法的相互作用。理解这些机制对于网络工程师来说非常重要,因为它们直接影响到网络应用的性能和用户体验。

TCP 拥塞控制

TCP拥塞控制是一个复杂的过程,它涉及到多个参数和算法的相互作用。

TCP拥塞控制是传输控制协议(TCP)中的一个重要机制,用于避免网络拥塞,确保数据能够有效、稳定地在互联网中传输。它通过监测网络状态并相应地调整数据的发送速率来实现这一目标。以下是TCP拥塞控制的关键点:

  • 拥塞窗口(CongWin):TCP连接的每一端都会维护一个拥塞窗口,这个窗口的大小表示在没有收到确认的情况下发送方可以发送的最大数据量。拥塞窗口的大小会根据网络的实时状态动态调整。

  • 慢启动和拥塞避免:当建立新的TCP连接时,TCP会经历一个慢启动阶段,初始时拥塞窗口设置为一个较小的值,然后逐渐增大。当拥塞窗口达到一定阈值后,进入拥塞避免阶段,增长速度会减慢。这样做的目的是为了避免一开始就发送大量数据导致网络拥塞。

  • 快速重传和快速恢复:如果发送方长时间未收到ACK而发生数据重传,这通常意味着网络可能出现了拥塞。此时,发送方会缩小拥塞窗口,降低发送速度。而当收到有效的ACK时,则会增大拥塞窗口,提高发送速度,因为这通常表示网络状态良好。

  • 拥塞控制策略:TCP拥塞控制的策略包括端到端拥塞控制和网络辅助的拥塞控制。端到端拥塞控制是由发送端自己来判断网络是否拥塞,并相应调整传输速率。而网络辅助的拥塞控制则是由网络中的路由器来告知发送方网络的拥塞情况。

TCP拥塞控制的目标是在保证数据传输效率的同时,避免因网络拥塞导致的数据传输问题。理解这些机制对于网络工程师来说非常重要,因为它们直接影响到网络应用的性能和用户体验。

TCP 有序传输

TCP的有序传输确保了数据包按照发送顺序到达接收方,从而保证了数据传输的可靠性和完整性。

  • 首先,在TCP协议中,每个数据包都被分配了一个序列号,这个序列号是连续的,确保了即使在网络中数据包经过了不同的路径,也能被正确地重新排序。这一点对于保证数据的有序性至关重要,因为网络层的协议如IP并不保证数据包的顺序。

  • 其次,TCP使用确认应答机制来确认数据包的接收。当接收方收到数据包时,它会发送一个包含期望收到的下一个序列号的ACK(确认应答信号),这允许发送方知道哪些数据已经被成功接收,并且可以按序处理后续的数据包。

  • 最后,如果数据包在传输过程中丢失,TCP会使用重传机制来重新发送这些数据包。这个机制确保了即使数据包丢失,也能够被重新发送并最终按顺序到达接收方。

总的来说,TCP的有序传输是通过序列号、确认应答和重传机制共同作用的结果,这些机制确保了数据包不仅能够到达目的地,而且能够按照发送时的顺序到达,从而为应用层提供了可靠的数据传输服务。理解这些机制对于网络工程师来说非常重要,因为它们直接影响到网络应用的性能和用户体验。

TCP 超时重传

TCP的超时重传机制是一种确保数据可靠性的关键特性,它通过在发送方设定一个定时器来监测数据的传输状态。

当发送方发送数据包后,会等待接收方返回确认报文(ACK)。如果在一定时间内未收到确认,发送方会认为数据包可能已经丢失或损坏,此时就会触发重传机制。这个时间阈值被称为重传超时(RTO),它会动态地根据网络状况进行调整。

RTO的计算通常基于往返时间(RTT)的估计,这是一个数据包从发送方到接收方再返回发送方所需的时间。为了更准确地计算RTO,TCP采用了如Karn算法等方法来避免不必要的重传并优化性能。此外,TCP还实现了快速重传机制,当发送方连续收到三个重复的ACK时,它会立即重传相应的数据包,而不是等待超时。

总的来说,TCP的超时重传机制是一个复杂的过程,它涉及到多个参数和算法的相互作用。理解这些机制对于网络工程师来说非常重要,因为它们直接影响到网络应用的性能和用户体验。

TCP 报文

TCP报文是TCP协议在传输数据时所使用的数据单元。

图片

首先,TCP报文包括一个TCP头部和紧随其后的数据部分。TCP头部包含了多个字段,每个字段都有特定的作用,共同确保了TCP的可靠传输和流量控制。以下是TCP报文的主要组成部分:

  • 源端口(Source Port):源计算机上的应用程序的端口号;

  • 目的端口(Destination Port):目标计算机的应用程序端口号;

  • 序列号(Sequence Number):它表示本报文段所发送数据的第一个字节的编号。在 TCP 连接中,所传送的字节流的每一个字节都会按顺序编号。当SYN标记不为1时,这是当前数据分段第一个字母的序列号;如果SYN的值是1时,这个字段的值就是初始序列值(ISN),用于对序列号进行同步。这时,第一个字节的序列号比这个字段的值大1,也就是ISN加1;

  • 确认号(Acknowledgment Number,ACK Number):它表示接收方期望收到发送方下一个报文段的第一个字节数据的编号(下一个序列号),等于已经接收确认的数据序列号加1,也就是接收方收到的序列号+报文大小+1;

  • 数据偏移(Header Length):数据偏移是指数据段中的“数据”部分起始处距离 TCP 数据段起始处的字节偏移量,占 4 位。其实这里的“数据偏移”也是在确定 TCP 数据段头部分的长度,告诉接收端的应用程序,数据从何处开始;

  • 保留位(Reserved):占 4 位。为 TCP 将来的发展预留空间,目前必须全部为 0;

  • 标志位字段:

    • CWR(Congestion Window Reduce):拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率。

    • ECE(ECN Echo):用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵。

    • URG(Urgent):表示本报文段中发送的数据是否包含紧急数据。URG=1 时表示有紧急数据。当 URG=1 时,后面的紧急指针字段才有效。

    • ACK:表示前面的确认号字段是否有效。ACK=1 时表示有效。只有当 ACK=1 时,前面的确认号字段才有效。TCP 规定,连接建立后,ACK 必须为 1。

    • PSH(Push):告诉对方收到该报文段后是否立即把数据推送给上层。如果值为 1,表示应当立即把数据提交给上层,而不是缓存起来。

    • RST:表示是否重置连接。如果 RST=1,说明 TCP 连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。

    • SYN:在建立连接时使用,用来同步序号。当 SYN=1,ACK=0 时,表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时,表示对方同意建立连接。SYN=1 时,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才为 1。

    • FIN:标记数据是否发送完毕。如果 FIN=1,表示数据已经发送完成,可以释放连接。

  • 滑动窗口(Window Size):占 16 位。它表示从 Ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间。该字段可以用于 TCP 的流量控制;

  • 校验和(TCP Checksum):它用于确认传输的数据是否有损坏。发送端基于数据内容校验生成一个数值,接收端根据接收的数据校验生成一个值。两个值必须相同,才能证明数据是有效的。如果两个值不同,则丢掉这个数据包。Checksum 是根据伪头 + TCP 头 + TCP 数据三部分进行计算的;

  • 紧急指针(Urgent Pointer):仅当前面的 URG 控制位为 1 时才有意义。它指出本数据段中为紧急数据的字节数,占 16 位。当所有紧急数据处理完后,TCP 就会告诉应用程序恢复到正常操作。即使当前窗口大小为 0,也是可以发送紧急数据的,因为紧急数据无须缓存;

  • 选项(Option):长度不定,但长度必须是 32bits 的整数倍。

TCP报文可能还包含选项字段,用于提供额外的功能,如窗口规模、最大报文段长度等。这些选项可以增强TCP的性能和灵活性。

总的来说,TCP报文的设计体现了TCP协议的核心特性,即面向连接、可靠的字节流服务。通过这些精心设计的字段,TCP能够实现数据的可靠传输,适应不同的网络条件和应用需求。

TCP 协议的例子

TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。在Java中,可以使用java.net包中的Socket和ServerSocket类来实现TCP协议的客户端和服务端通信。 以下是一个简单的Java TCP客户端和服务端的示例代码:

服务端代码:

import java.io.*;
import java.net.*;public class TCPServer {public static void main(String[] args) throws IOException {// 创建一个ServerSocket对象,监听8080端口ServerSocket serverSocket = new ServerSocket(8080);System.out.println("服务器启动,等待客户端连接...");// 调用accept()方法等待客户端连接Socket socket = serverSocket.accept();System.out.println("客户端已连接,IP地址:" + socket.getInetAddress().getHostAddress());// 获取输入流,读取客户端发送的数据BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));String msg = in.readLine();System.out.println("收到客户端消息:" + msg);// 获取输出流,向客户端发送数据PrintWriter out = new PrintWriter(socket.getOutputStream(), true);out.println("你好,客户端!");// 关闭资源out.close();in.close();socket.close();serverSocket.close();}
}

客户端代码:

import java.io.*;
import java.net.*;public class TCPClient {public static void main(String[] args) throws IOException {// 创建一个Socket对象,连接到服务器Socket socket = new Socket("localhost", 8080);System.out.println("客户端已启动,连接到服务器...");// 获取输出流,向服务器发送数据PrintWriter out = new PrintWriter(socket.getOutputStream(), true);out.println("你好,服务器!");// 获取输入流,读取服务器发送的数据BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));String msg = in.readLine();System.out.println("收到服务器消息:" + msg);// 关闭资源out.close();in.close();socket.close();}
}

在这个示例中,服务端监听8080端口,等待客户端连接。客户端连接到服务端后,双方可以通过输入输出流进行数据的发送和接收。

结语

TCP作为互联网协议族的核心组成部分之一,其设计精妙且功能强大,为全球范围内的数据通信提供了稳定可靠的基础。尽管随着时间的推移,新的技术和协议不断涌现,但TCP依然是许多网络应用的首选传输协议。了解和掌握TCP的原理及其工作机制对于网络工程师、开发者乃至任何对计算机网络感兴趣的人都是至关重要的。随着网络环境的不断变化,对TCP的优化和改进也将持续进行,以满足日益增长的网络通信需求。

图片

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

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

相关文章

常见电源电路(LDO、非隔离拓扑和隔离拓扑结构)

一、常见电路元件和符号 二、DC-DC转换器 DC-DC转换器:即直流-直流转换器,分为三类:①线性调节器;②电容性开关解调器;③电感性开关调节器; 2.1线性稳压器(LDO) 2.1.1 NMOS LDO…

【深度学习】sdxl中的 tokenizer tokenizer_2 区别

代码仓库: https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/tree/main 截图: 为什么有两个分词器 tokenizer 和 tokenizer_2? 在仔细阅读这些代码后,我们了解到 tokenizer_2 主要是用于 refiner 模型的。 #…

内存管理(mmu)/内存分配原理/多级页表

1.为什么要做内存管理? 随着进程对内存需求的扩大,和同时调度的进程增加,内存是比较瓶颈的资源,如何更好的高效的利于存储资源是一个重要问题。 这个内存管理的需求也是慢慢发展而来,早期总线上的master是直接使用物…

2.依附弹窗(AttachListPopup)

愿你出走半生,归来仍是少年! 环境:.NET 7 基于基础的Popup对象实现的依附于某个控件的弹窗,弹窗可呈现数组对象,达到较好的选择交互效果。 1.布局 通过Border实现圆角边框轮廓,然后通过内部的ListView实现列表展示。…

sql 行转列 日周月 图表统计

目录 目录 需求 准备 月 分析 按月分组 行转列 错误版本 正确版本 日 分析 行转列 周 分析 按周分组 行转列 本年 需求 页面有三个按钮 日周月,统计一周中每天(日),一月中每周(周),一年中每月(月),设备台数 点…

【C++11并发】mutex 笔记

简介 在多线程中往往需要访问临界资源,C11为我们提供了mutex等相关类来保护临界资源,保证某一时刻只有一个线程可以访问临界资源。主要包括各种mutex,他们的命名大都是xx_mutex。以及RAII风格的wrapper类,RAII就是一般在构造的时…

webug存在的越权漏洞-水平越权以及垂直越权的漏洞复现(超详解)

越权漏洞-webug、 1.登录 账号:admin 密码:admin 2.进入逻辑漏洞 3.进入越权修改密码靶场 (1)输入账号密码 进入进去会发现没有权限进入 方法一: 这里我们只需要将 127.0.0.1:8080/control/a/auth_cross/cross_a…

Higress 开源一周年:新版本,新标准,新工具,新征程

作者:Higress 团队 历程回顾 Higress 开源一年时间,一共发布了 18 个 release 版本,收获了 40 多位社区贡献者和 1800 star,上图是这一年过来达成的一些关键的里程碑。 前面半年通过集成开源生态,打磨开源版本稳定性…

Redis为什么速度快:数据结构、存储及IO网络原理总结

Redis,作为内存数据结构存储的佼佼者,其高性能表现一直备受赞誉。那么,Redis究竟是如何实现这一点的呢?接下来,我们将更深入地探讨其背后的关键技术,并提供进一步的优化策略。 一、内存存储与数据结构设计…

Neo4j 国内镜像下载与安装

Neo4j 5.x 简体中文版指南 社区版:https://neo4j.com/download-center/#community 链接地址(Linux版):https://neo4j.com/artifact.php?nameneo4j-community-3.5.13-unix.tar.gz 链接地址(Windows)&#x…

【Vue2 + ElementUI】分页el-pagination 封装成公用组件

效果图 实现 &#xff08;1&#xff09;公共组件 <template><nav class"pagination-nav"><el-pagination class"page-area" size-change"handleSizeChange" current-change"handleCurrentChange":current-page"c…

java抽象工厂实战与总结

文章目录 一、工厂模式&#xff08;三种&#xff09;1.简单工厂模式1.1 概念&#xff1a;1.2 使用场景&#xff1a;1.3 模型图解&#xff1a;1.4 伪代码&#xff1a; 2.工厂方法模式2.1 概念&#xff1a;2.2 使用场景&#xff1a;2.3 模型图解&#xff1a;2.4 伪代码 3.抽象工厂…

架构师之路(十四)计算机网络(网络层)

前置知识&#xff08;了解&#xff09;&#xff1a;计算机基础。 作为架构师&#xff0c;我们所设计的系统很少为单机系统&#xff0c;因此有必要了解计算机和计算机之间是怎么联系的。局域网的集群和混合云的网络有啥区别。系统交互的时候网络会存在什么瓶颈。 网络层提供主机…

uniapp设置隐藏原生导航栏(3)

1、单个页面隐藏 在pages.json里配置 (第一种方式) {"path": "pages/home/index","style": {"navigationBarTitleText": "首页","navigationStyle": "custom" // 使用自定义导航栏&#xff0c;系统会关…

MongoDB日期存储与查询、@Query、嵌套字段查询实战总结

缘由 MongoDB数据库如下&#xff1a; 如上截图&#xff0c;使用MongoDB客户端工具DataGrip&#xff0c;在filter过滤框输入{ profiles.alias: 逆天子, profiles.channel: }&#xff0c;即可实现昵称和渠道多个嵌套字段过滤查询。 现有业务需求&#xff1a;用Java代码来查询…

在Idea中使用git查看历史版本

idea查git历史 背景查看步骤总结 背景 有好几次同事到我电脑用idea查看git管理的历史记录&#xff0c;每次都说我的idea看不了历史版本&#xff0c;叫我到他电脑上去看&#xff0c;很晕&#xff0c;为什么,原来是我自己把显示历史文件的视图覆盖了&#xff0c;下面我们来一起学…

qt学习:http+访问百度智能云api实现车牌识别

登录到百度智能云,找到文字识别 完成操作指引 免费尝鲜---服务类型选择交通---接口选择全部----0元领取创建应用---填写应用名称---个人----应用描述开通 查看车牌识别的api文档 查看自己应用的api key 查看回应的数据格式 编程步骤 ui界面编辑 添加模块,头文件和定义变量…

【复现】Laykefu客服系统后台漏洞合集_29

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 2. 漏洞二&#xff1a; 3. 漏洞三&#xff1a; 4. 漏洞四&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 Laykefu客服系统是thinkphp5Gatewayworker搭建的web客服…

【C++修行之道】STL(初识list、stack)

目录 一、list 1.1list的定义和结构 以下是一个示例&#xff0c;展示如何使用list容器: 1.2list的常用函数 1.3list代码示例 二、stack 2.1stack的定义和结构 stack的常用定义 2.2常用函数 2.3stack代码示例 一、list 1.1list的定义和结构 list的使用频率不高&#…

小黑艰难的前端啃bug之路:内联元素之间的间隙问题

今天开始学习前端项目&#xff0c;遇到了一个Bug调了好久&#xff0c;即使margin为0&#xff0c;但还是有空格。 小黑整理&#xff0c;用四种方法解决了空白问题 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></tit…