当前位置: 首页 > news >正文

计算机网络 第三章:运输层(一)

        运输层位于应用层和网络层之间,是分层的网络体系结构的重要部分。该层为运行在不同主机上的应用进程提供直接的通信服务。通常特别关注因特网协议,即 TCP 和 UDP 运输层协议

        讨论运输层和网络层的关析,为研究运输层第一个关键功能:将网络层的在两个端系统之间的交付任务扩展到运行在两个不同端系统上的应用层进程之间的交付任务。 讨论因特网的无连接运输协议 UDP 时阐述这个功能。

        两个实体怎样才能在一种会丢失或损坏数据的媒体上可靠的通信。通过一系列复杂性不断增加的场景,建立起一套被运输协议用来解决的技术,体现在因特网面向连接的运输协议 TCP 中。

        讨论第二个基础性问题:控制运输层实体的传输速率以避免网络中的拥塞或从拥塞中恢复过来。将考虑拥塞的原因和后果,以及常用的拥塞技术。将研究 TCP 应对拥塞控制的方法。

3.1 概述运输层服务

        运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信 (logic communication) 功能。从应用程序的角度看,通过逻辑通信,运行不同进程的主机好像直接相连一样;实际上,这些主机也许位于地球的两侧,通过很多路由器及多种不同类型的链路相连。应用进程使用运输层提供的逻辑通信功能彼此发送报文,而无须考虑承载这些报文的物理基础设施的细节。图3-1 图示了逻辑通信的概念。

        如图 3-1 所示,运输层协议是在端系统中而不是在路由器中实现的。在发送端,运输层将从发送应用程序进程接收到的报文转换成运输层分组,用因特网术语来讲该分组称为运输层报文段(segment)。实现的方法(可能)是将应用报文划分为较小的块,并为每块加上一个运输层首部以生成运输层报文段。然后,在发送端系统中,运输层将这些报文段传递给网络层,网路层将其封装成网络层分组(即数据报)并向目的地发送。注意到下列事实是重要的:网络路由器仅作用于该数据报的网络层字段;即它们不检查封装在该数据报的运输层报文段的字段。在接收端网络层从数据报中提取运输层报文段,并将该报文段向上交给运输层。运输层则处理接收到的报文段,使该报文段中的数据为接收应用进程使用。

        网络应用程序可以使用多种的运输层协议。例如,因特网有两种协议,即 TCP 和 UDP。每种协议都能为调用的应用程序提供一组不同的运输层服务。

3.1.1 运输层和网络层的关系

        在协议栈中,运输层位于网络层之上。网络层提供了主机之间的逻辑通信,而传输层为运行在不同主机上的进程提供了逻辑通信

        考虑两个家庭,分别位于东海岸和西海岸,每家有12个孩子。这两家的孩子是堂兄弟姐妹,喜欢彼此通信,没人每星期要互相写一封信,用单独的信封通过传统的邮政服务传送。因此,每个家庭向另一家发送 144 封信。每个家庭有个孩子负责收发邮件,分别是Ann 和 Bill。通过邮政服务的邮车将信件传输。

        在这个例子中,邮政服务为两个家庭间提供逻辑通信,邮政服务将信件从一家送往另一家(而不是一个人送往另一个人)。另一方面,Ann 和 Bill 为堂兄弟姐妹之间提供了逻辑通信,Ann 和 Bill 从兄弟姐妹那里收取信件或到兄弟姐妹那里交付信件。从兄弟姐妹的角度,Ann 和 Bill 就是邮件服务,尽管它们只是端到端交付过程的一部分(即端系统),解释运输层和网络层之间的关系

         

        Ann 和 Bill 都是在各自家中进行工作的;例如,它们并没有参与任何一个中间邮件中心对邮件进行分拣,或者将邮件从一个邮件中心送到另一个邮件中心之类的工作。类似的,运输层协议只工作在端系统。在端系统中,运输层协议将来自应用进程的报文移动到网络边缘网络层),反过来一样,但对有关这些报文在网络核心如何移动并不做任何规定。事实上,如图3-1所示,中间路由器既不处理也不识别运输层加在应用层报文的任何信息

        此外,计算机网络中可以安排多种运输层协议,每种协议为应用程序提供不同的服务模型。并且, 运输协议能够提供的服务常常受制于底层网络层协议的服务模型。如果网络层协议无法为主机之间发送的运输层报文段提供时延或带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或带宽保证。

         然而,即使底层网络协议不能在网络层提供相应的服务,运输层协议也能提供某些服务。例如,即使底层网络协议是不可靠的,也就是说网络层协议会使分组丢失、篡改和冗余,运输协议也能为应用程序提供可靠的数据传输服务。另一个例子是,即使网络层不能保证运输层报文段的机密性,运输协议也能使用加密来确保应用程序报文不被入侵者读取

3.1.2 因特网运输层概述

        因特网为应用层提供了两种截然不同的可用运输层协议。一种是 UDP(用户数据报协议),为调用它的应用程序提供了一种不可靠、无连接的服务。另一种是 TCP(传输控制协议),为调用它的应用程序提供了一种可靠的、面向连接的服务

        统一将 TCP 和 UDP 的分组通常为报文段,而将数据报名称保留给网络层分组

因特网的网络层:

        因特网网络层协议有一个名字—IP,即网际协议IP 为主机之间提供了逻辑通信。IP 的服务模型是尽力而为交付服务。意味着 IP 尽最大努力在通信的主机之间交付报文段,但不做任何确保。它不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性。由于这些原因,IP 被称为 不可靠服务。还要指出,每台主机至少有一个网络层地址,即 IP 地址。

UDP 和 TCP 的服务模型:

        基本责任:将两个端系统间 IP 的交付服务扩展为运行在端系统上的两个进程之间的交付任务。将主机间交付扩展到进程间交付被称为运输层的多路复用多路分解。UDP 和 TCP 还可以通过在其报文段首部中包括差错检查字段而提供完整性检查。进程到进程的数据交付差错检查是两种最低限度的运输层服务,也是UDP 所能提供的仅有的两种服务。特别是,与 IP 一样, UDP 也是一种不可靠的服务,即不能保证一个进程所发送的数据能够完整无缺地到达目的进程。

         另一方面,TCP 为应用程序提供了几种附加服务。首先,提供可靠数据传输。通过使用流量控制、序号、确认和计时器,TCP 确保正确地、按序地将数据从发送进程交付给接收进程。这样,TCP 就将两个端系统间的不可靠 IP 服务转换成了一种进程间的可靠数据传输服务。TCP 还提供拥塞控制。防止任何一条 TCP 连接用过多流量来淹没通信主机之间的链路和交换设备。TCP 力求为每个通过一条拥塞网络链路的连接平等的共享网络链路带宽。这可以通过调节 TCP 连接的发送端发送进网络的流量速率做到。在另一方面,UDP 流量是不可调节的。使用 UDP 传输的应用程序可以根据其需要以其愿意的任何速率发送数据。

 3.2 多路复用与多路分解

        运输层的多路复用多路分解,将由网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的交付服务。将在因特网环境中讨论这种基本的运输层服务。

         在目的主机,运输层从紧邻其下的网络层接收报文段。运输层负责将这些报文段中的数据交付给在主机上运行的适当应用程序进程。来看一个例子,假定你正坐在计算机前下载 Web 页面,同时还在运行一个 FTP 会话和两个Telnet 会话。这样你就有 4 个网络应用进程在运行,即两个Telnet 进程,一个 FTP 进程和一个 HTTP 进程。当你的计算机中的运输层从底层的网络层接收数据时,它需要将所接收到的数据定向到这 4 个进程中的一个。现在我们来研究这是怎样完成的。

         首先回想 2.7 节的内容,一个进程(作为网络应用的一部分)有一个或多个套接字
(socket),它相当于从网络向进程传递数据从进程向网络传递数据的门户。因此,如图3-2 示,在接收主机中的运输层实际上并没有直接将数据交付给进程, 而是将数据交给了一个中间的套接字。由于在任一时刻,在接收主机上可能有不止一个套接字 ,所以每个套接字都有唯一的标识符。标识符的格式取决于它是 UDP 还是 TCP 套接字。

         接收主机怎样将一个到达的运输层报文段定向到适当的套接字。为此目的,每个运输层报文段中具有几个字段。在接收端,运输层检查这些字段,标识出接收套接字,进而将报文段定向到该套接字。将运输层报文段中的数据交付到正确的套接字的工作称为 多路分解 (demulti plexing) 。在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(这将在以后用于分解)从而生成报文段,然后将报文段传递到网络层,所有这些工作称为多路复用 (multiplexing)。值得注意的是,图 3-2 中的中间那台主机的运输层必须将从其下的网络层收到的报文段分解后交给其上的 P1 或 P2 进程;这一过程是通过将到达的报文段数据定向到对应进程的套接字来完成的。中间主机中的运输层也必须收集从这些套接字输出的数据,形成运输层报文段, 然后将其向下传递给网络层。尽管在因特网运输层协议的环境下引入了多路复用和多路分解;它们在与某层(运输层或别处)的单一协议何时被位于接下来的高层的多个协议有关。

        运输层多路复用要求:① 套接字有唯一标识符;② 每个报文段有特殊字段来指示该报文段所要交付到的套接字。如图 3-3 所示,这些特殊字段是 源端口号字段 目的端口号字段。端口号是一个 16 比特的数,其大小在 0~65535 之间。0~1023 范围的端口号称为周知端口号,是受限制的,这是指它们保留给诸如 HTTP(端口号 80)和 FTP(端口号21)之类的周知应用层协议来使用。当开发一个新的应用程序时,必须为其分配一个端口号。

        运输层实现分解服务:在主机上的每个套接字能够分配一个端口号,当报文段到达主机时,运输层检查报文段中的目的端口号,并将其定向到相应的套接字。然后报文段中的数据通过套接字进入其所连接的进程。正如看到的那样,UDP 大体上是这样做的。

1. 无连接的多路复用与多路分解

        在主机上运行的 Python 程序使用下面一行代码创建了一个 UDP 套接字

                clientSocket = socket(AF_INET , SOCK_DGRAM) 

        当用这种方式创建一个 UDP 套接字时,运输层自动地为该套接字分配一个端口号。特别是,运输层从范围 1024~65535 内分配一个端口号,该端口号是当前未被该主机中任何其他 UDP 端口使用的号。另一种方法是,在创建一个套接字后,能够在 Python 中增加一行代码,通过套接字 bind() 方法为 UDP 套接字关联一个特定的端口号。 

                clientSocket.bind((' ',19157))

         如果应用程序开发者所编写的代码实现的是一个"周知协议"的服务器端,那么开发者就必须为其分配一个相应的周知端口号。通常,应用程序的客户端让运输层自动地(并且是透明地)分配端口号,而服务器端则分配一个特定的端口号。

        通过为 UDP 套接字分配端口号,来描述 UDP 的复用与分解。假定在主机 A 中的一个进程具有 UDP 端口 19157,它要发送一个应用程序数据块给位于主机 B 的另一进程,该进程具有 UDP 端口 46428。① 主机 A 中的运输层创建一个运输层报文段,其中包括应用程序数据、源端口号(19157),目的端口号(46428)和两个其他值。然后,运输层将得到的报文段传递到网络层。网络层将该报文段封装到一个 IP 数据报中,并尽力而为地将报文段交付给接收主机

       ② 如果该报文段到达接收主机B ,接收主机运输层检查该报文段中的目的端口号 (46428 ) 
并将该报文段交付给端口号 46428 所标识的套接字。值得注意的是,主机B 能够运行多个进程,每个进程有自己的套接字及相应的端口号。当 UDP 报文段从网络到达时, 主机B 通过检查该报文段中的目的端口号,将每个报文段定向(分解)到相应的套接字。 

         注意到下述事实是重要的:一个 UDP 套接字是由一个二元组全面标识的,该二元组包含一个目的 lP 地址和一个目的端口号。因此,如果两个 UDP 报文段有不同的源 IP 地址和/或源端口号,但具有相同的目的 IP 地址和目的端口号,那么这两个报文段将通过相同的目的套接字被定向到相同的目的进程

         源端口号的用途是什么呢?如图 3-4 所示,在 A 到 B 的报文段中,源端口号用作 “ 返回地址 ” 的一部分,即当 B 需要回发一个报文段给 A 时,B 到 A 的报文段中的目的端口号便从 A 到 B 的报文段中的源端口号中取值。(完整的返回地址是 A 的IP 地址和源端口号)。问想2.7节学习过的那个 UDP 服务器程序。UDPServer. py 中,服务器使用 recvfrom( )方法从其自客户接收到的报文段中提取出客户端 (源 )端口号,然后,它将所提取的源端口号作为目的端口号,向客户发送一个新的报文段。

 

2.面向连接的多路复用与多路分解

        为了理解 TCP 多路分解,必须更为仔细地研究 TCP 套接字和 TCP 连接创建。TCP 套接字和UDP套接字之间的一个细微差别是,TCP 套接字是由一个四元组(源 IP 地址、源端口号、目的 IP 地址、目的端口号)来表示的。因此,当一个 TCP 报文段从网络到达一台主机时,该主机使用全部 4 个值来将报文段定向(分解)到相应的套接字。特别与 UDP 不同的是,两个具有不同源 IP 地址 或 源端口号的到达 TCP 报文段将被定向到两个不同的套接字,除非 TCP 报文段携带了初始创建连接的请求。

        考虑 2.7.2 节中的 TCP客户-服务器编程的例子:

        • TCP 服务器应用程序有一个"欢迎套接字",它在 12000 端口上等待来自TCP客户(见图 2-27) 的连接建立请求 
        • TCP 客户使用下面的代码创建一个套接字并发送一个连接建立请求报文段
        clientSocket=socket (AF_INET, SOCK_ STREAM) 
        ciientSocket. connect({serverName, 12000)} 
        • 一条连接建立请求只不过是一个目的端口号为 12000,TCP 首部的特定"连接建立位"置位的 TCP 报文段。这个报文段也包含一个由客户选择的源端口号。

        • 当运行服务器进程的计算机的主机操作系统接收到具有目的端口 12000 的入连接请求报文段后,它就定位服务器进程,该进程正在端口号 12000 等待接受连接。该服务器进程则创建一个新的套接字:
        connectionSocket, addr = serverSocket.accept () 
       • 该服务器的运输层还注意到连接请求报文段中的下列 4 个值:① 该报文段中的源端口号;② 源主机 IP 地址;③ 该报文段中的目的端口号;④ 自身的 lP 地址。新创建的连接套接字通过这 4 个值来标识。所有后续到达的报文段,如果它们的源端口号、源主机 IP 地址、目的端口号和目的 IP 地址都与这 4 个值匹配,则被分解到这个套接字

        随着 TCP 连接完成,客户和服务器便可相互发送数据了。

         服务器主机可以支持很多并行的 TCP 套接字,每个套接字与一个进程相联系,并由其四元组来标识每个套接字。当一个 TCP 报文段到达主机时,所有 4 个字段(源 IP 地址,源端口号、目的 IP 地址、目的端口)被用来将报文段定向(分解)到相应的套接字

        图 3-5 图示了这种情况,图中主机 C 向服务器 B 发起了两个 HTTP 会话,主机 A 向服务器 B 发起了一个 HTTP 会话。主机 A 与 主机 C 及服务器 B  都有自己唯一的 IP 地址,它们分别是A、C、B。主机 C 为其两个 HTTP 连接分配了两个不同的源端口号(26145 和 7532)。因为主机 A 选择源端口号时与主机 C 互不相干,因此也可以将源端口号 26145 分配给其 HTTP 连接。但这不是问题,即服务器 B 仍然能够正确地分解这两个具有相同源端口号的连接,因为它们有着不同的源 IP 地址。

3. Web 服务器与 TCP 

        考虑一台运行 Web 服务器的主机,例如在端口 80 上运行一个 Apache Web 服务器。当客户(如浏览器)向该服务器发送报文段时,所有报文段的目的端口都将为 80。特别是,初始连接建立报文段和承载 HTTP 请求的报文段都有 80 的目的端口。该服务器能够根据源 IP 地址和源端口号来区分来自不同客户的报文段。

        图 3-5 显示了一台 Web 服务器为每条连接生成一个新进程。如图 3-5,每个这样的进程都有自己的连接套接字,通过这些套接字可以收到 HTTP 请求 发送 HTTP 响应。 然而,连接套接字与进程之间并非总是有着一一对应的关系。事实上,当今的高性能 Web 服务器通常只使用一个进程,但是为每个新的客户连接创建一个具有新连接套接字的新线程。(线程被看作是一个轻量级的子进程)对于这样一台服务器,在任意给定的时间内都可能有(具有不同标识的)许多连接套接字连接到相同的进程

         如果客户与服务器使用持续 HTTP,则在整条连续持续期间,客户与服务器之间经由同一服务器套接字交换 HTTP 报文。然而,如果客户与服务器使用非持续 HTTP,则对每一对请求/响应都创建一个新的 TCP 连接并在随后关闭,因此对每一对请求/响应创建一个新的套接字并在随后关闭。这种套接字的频繁创建和关闭会严重影响一个繁忙的 Web 服务器的性能。

3.3 无连接运输:UDP

        如果应用程序开发人员选择 UDP 而不是 TCP,则该应用程序差不多就是直接与 IP 打交道。UDP 从应用进程得到数据附加上用于多路复用/分解服务的源和目的端口号字段,以及两个其他的小字段,然后将形成的报文段交给网络层。网络层将该运输层报文段封装到一个 IP 数据报中,然后尽力而为地尝试将此报文段交付给接收主机。如果报文段到达接收主机,UDP 使用目的端口号将报文段中的数据交付给正确的应用进程。值得注意的是,使用 UDP 时,在发送报文段之前,发送方和接收方的运输层实体之间没有握手,正因如此,UDP被称为是 无连接的

        DNS 是一个通常使用 UDP 的应用层协议的例子。当一台主机中的 DNS 应用程序想要进行一次查询时,它构造了一个 DNS 查询报文并将其交给 UDP。无须执行任何与运行在目的端系统中的 UDP 实体之间的握手,主机端的 UDP 为此报文添加首部字段,然后将形成的报文段交给网络层。网络层将此 UDP 报文段封装进一个 lP 数据报中,然后将其发送给一个名字服务器。在查询主机中的 DNS 应用程序则等待对该查询的响应。如果它没有收到响应(可能是由于底层网络丢失了查询或响应) ,则要么试图向另一个名字服务器发送该查询,要么通知调用的应用程序它不能获得响应

        许多应用情况下更适合用 UDP 而不是 TCP,原因有如下:

        ① 关于发送什么数据以及合适发送的应用层控制更为精细。采用 UDP 时,只要应用进程将数据传递给 UDP,UDP 就会将此数据打包进 UDP 报文段并立即传递给网络层。在另一方面,TCP 拥有一个阻塞控制机制,以便当源和目的主机间的一条或多条链路变得极度拥塞时来遏制运输层 TCP 发送方。TCP 仍然继续重新发送数据报文段直到目的主机收到此报文并加以确认,而不管可靠交付需要多长时间。因为实时应用通常要求最小的发送速率,不希望过分地延迟报文段的传送,且能容忍一些数据丢失,TCP 服务模型不是特别适合这些应用的需要。

        ② 无须连接建立。TCP 在开始传输数据之前都要经过三次握手。UDP 却不需要任何准备即可进行数据传输。因此,UDP 不会引入建立连接的时延。这可能是 DNS 运行在 UDP 之上而不是运行在 TCP 之上的原因(如果运行在 TCP 上,则 DNS 会慢很多)。HTTP 使用 TCP 而不是 UDP,因为对于具有文本数据的 Web 网页来说,可靠性是至关重要的。

        ③ 无连接状态。TCP 需要在端系统中维护连接状态。此连接状态包括接收和发送缓存、拥塞控制参数以及序号与确认号的参数。要实现 TCP 的可靠数据传输服务并提供拥塞控制,这些状态是必要的。另一方面,UDP 不维护连接状态,也不跟踪参数。因此,某些专门用于某种特定应用的服务器当应用程序运行在 UDP 之上而不是运行在TCP 上时,一般都能支持更多的活跃用户

        ④ 分组首部开销小。每个 TCP 报文段有 20 字节的首部开销,而 UDP 仅有 8 字节的开销。 

        图 3-6 列出了流行的因特网应用及其所使用的运输协议。如期望的那样,电子邮件、远程终端访问、Web 及文件传输都运行在 TCP 上。因为所有这些应用都需要 TCP 的可靠数据传输服务。无论如何,有很多重要的应用是运行在 UDP 上而不是 TCP 上。例如,UDP 用于承载网络管理数据(SNMP)。这种场合下,UDP 要优于 TCP,因为网络管理应用程序通常必须在该网络处于重压状态时运行,而正是这个时候可靠的、拥塞受控的数据传输难以实现。此外,DNS 运行在 UDP 之上,从而避免了 TCP 的连接创建时延。

        UDP 没有拥塞控制,而有时需要拥塞控制来预防网络进入一种拥塞状态,在拥塞状态中做的有用工作非常少。如果每个人都启用流式高比特率视频而不使用拥塞控制,就会使路由器中有大量分组溢出,以至于非常少的 UDP 分组能成功地通过源到目的的路径传输。另外,无控制的 UDP 发送方引入的高丢包率将引起 TCP 发送方大大地减少速率。UDP 中缺乏拥塞控制呢能够导致 UDP 发送方和接收方之间的高丢包率,并挤垮了 TCP 会话。

3.3.1 UDP 报文结构

        UDP 报文段结构如图 3-7 所示。应用层数据占用 UDP 报文段的数据字段,例如,对于 DNS 应用,数据字段要么包含一个查询报文,要么包含一个响应报文。对于流式音频应用,音频抽样数据填充到数据字段。UDP 首部只有 4 个字段,每个字段由两个字节组成。通过端口号可以使目的主机将应用数据交给运行在目的端系统中的相应进程(执行分解功能)。长度字段指示了 UDP 报文段中的字节数(首部+数据)。数据字段的长度在一个 UDP 段中不同于在另一个段中,需要一个明确的长度。接收方使用检验和检查在该报文段中是否出现了差错。实际上,计算检验和时,除了 UDP 报文段以外还包括了 IP 首部一些字段。

3.3.2 UDP 检验和

        UDP 检验和提供了差错检测功能。这就是说,检验和用于确定当 UDP 报文段从源到达目的地移动时,其中的比特是否发生了改变(例如,由于链路中的噪声干扰或者存储在路由器中时引入问题)。发送方的 UDP 对报文段中的所有16 比特字的和进行反码运算求和时遇到的任何溢出都被回卷。得到的结果被放在 UDP 报文段中的检验和字段。下面给出一个计算检验和的简单例子。

举例来说,有下面 3 个 16 比特的字:

         

         注意到最后一次加法有溢出,它要被回卷(溢出为加到后面)。反码运算就是将所有的 0 换成 1,所有的 1转换成 0 。因此,该和 0100101011000010 的反码运算结果是 1011010100111101 ,这就变为了检验和。在接收方,全部的 16 比特字(包括检验和)加在一起,如果该分组中没有引入差错,则显然在接收方处该和将是 1111111111111111。如果这些比特之一是 0,那么我们就知道该分组中已经出现了差错

         UDP 提供了检验和,原因是不能保证源和目的之间的所有链路都提供差错检测。此外,即使报文段经链路正确地传输,当报文段存储在某台路由器的内存中时,也可能引入比特差错。在既无法确保证链路的可靠性,又无法确保内存中的差错检测的情况下,如果端到端数据传输服务要提供差错检测,UDP 就必须在端到端基础上再运输层提供差错检测。这是一个在系统设计中被称为的端到端原则的例子。

         因为假定 IP 是可以运行在任何第二层协议之上的,运输层提供差错检测作为保险措施非常有效。虽然 UDP 提供差错检测,但对差错恢复无能为力。UDP 的某种实现只是丢弃受损的报文段;其他实现是将受损的报文段交给应用程序并给出警告

3.4 可靠数据传输原理

        可靠数据传输的实现问题不仅在运输层出现,也会在链路层和应用层出现。

        图3-8 图示说明了可靠数据传输的框架。为上层实体提供的服务抽象是:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特就不会受到损坏(由 0 变为 1,或者相反)或丢失,而且所有数据都是按照其发送顺序进行交付。这恰好就是 TCP 向调用它的因特网应用所提供的服务模型。

         实现这种服务抽象是可靠数据传输协议的责任。由于可靠数据传输协议的下层协议也许是不可靠的,因此这是一项困难的任务。例如,TCP不可靠的(IP)端到端网络层之上实现的可靠数据传输协议。更一般的情况,两个可靠通信端点的下层可能是由一条物理链路(如在链路级数据传输协议的场合下)组成或是由一个全球互联网络(如在运输级协议的场合下)组成。然而,我们可将较低层直接视为不可靠的点对点信道

         本节中,底层信道模型越来越复杂,不断地开发一个可靠数据传输协议发送方一侧和接收方一侧。例如,将考虑当底层信道能够损坏比特或丢失整个分组时,需要什么样的协议机制。这里始终的一个假设是分组将以它们发送的次序进行交付某些分组可能会丢失;底层信道将不会对分组重排序。图3-8b 图示说明了用于数据传输协议的接口。通过调用 rdt_send()函数,上层可以调用数据传输协议的发送方。将要发送的数据交付给位于接受方的较高层。(rdt 表示可靠数据传输协议,_send 指示 rdt 的发送端正在被调用。)在接收端,当分组从信道的接收端到达时,将调用 rdt_rcv()。当 rdt 协议想要向较高层交付数据时,通过调用 deliver_data() 来完成。后面,使用“分组”而不是运输层的“ 报文段 ”。

         本节中,仅考虑 单向数据传输的情况,即数据传输从发送端到接收端的。还有可靠的双向数据传输(即全双工数据传输)。协议需要在发送端和接收端两个方向上传输分组,如图 3-8 所示,除了交换含有待传送的分组之外,rdt 的发送端和接收端还需往返交换控制分组。rdt 的发送端和接收端都要调用 udt_send()发送分组给对方(udt 指不可靠数据传输

3.4.1 构造可靠数据传输协议

         1. 经完全可靠信道的可靠数据传输:rdt1.0

        首先考虑最简单的情况,即底层信道是完全可靠的。称该协议为 rdt1.0,该协议本身是简单的。图 3-9 显示了 rdt1.0 发送方和接收方的有限状态机( Finite- State Machine , FSM)的定义。图3-9a 中的 FSM 定义了发送方的操作, 图 3-9b 中的 FSM 定义了接收方的操作。注意到下列问题是重要的,发送方和接收方有各自的 FSM。图 3-9中发送方和接收方的 FSM 每个都只有一个状态。FSM 描述图中的箭头指示了协议从一个状态变迁到另一个状态。(因为图 3-9 中的每个FSM 都只有一个状态,因此变迁必定是从一个状态返回到自身;很快将看到更复杂的状态图。)引起变迁的事件显示在表示变迁的横线上方事件发生时所采取的动作显式在横线下方。如果对一个事件没有动作或没有事件而发生了一个动作,将在横线上方或下方使用符号 ∧,以明确表示缺少动作或事件。FSM 的初始状态用虚线表示。尽管 图 3-9 只有一个初始状态,标识每个 FSM 的初始状态是非常重要的。

         rdt 的发送端只通过 rdt_send(data)事件接受来自较高层的数据,产生一个包含该数据的分组(经由 make_pkt(data)动作),并将分组发送到信道中。实际上,rdt_send(data)事件是由较高层应用的过程调用产生的(rdt_send())。

        在接收端,rdt 通过 rdt_rcv(packet) 事件从底层信道接收一个分组,从分组中取出数据(经由 extract(packet,data) 动作),将数据上传给较高层(通过 deliver_data(data) 动作)。实际上,rdt_rcv(packet) 事件是由较低层协议的过程调用产生的(例如,rdt_rcv())

         在这个简单的协议中,一个单元数据与一个分组没差别。而且,所有分组是从发送方流向接收方;有了完全可靠的信道,接收端就不需要提供任何反馈信息给发送方,因为不必担心出现差错!注意到我们也已经假定了接收方接收数据的速率能够与发送方发送数据的速率一样快。因此,接收方没有必要请求发送方慢一点!

 2. 经具有比特差错信道的可靠数据传输:rdt2.0

        底层信道更为实际的模型是分组中的比特可能受损的模型。在分组的传输、传播或缓存的过程中,这种比特差错通常会出现在网络的物理部件中。我们还将继续假定所有发送的分组(虽然有些比特可能受损)将按其发送的顺序被接收

         研发经这种信道进行可靠通信的协议前,首先考虑人们如何处理这类情形。考虑怎样通过电话口述一条长报文的。通常情况下,报文接收者在听到、理解并记下每句话后可能会说“ OK ”。如果报文接收者听到一句含糊不清的话时,可能要求你重复那句容易误解的话。这种口述报文协议使用了肯定确认(OK)与 否定确认(“请重复一遍”)。这些控制报文使得接收方可以让发送方知道哪些内容被正确接收或有误需要重复。在计算机网络中,基于这种重传机制的可靠传输协议称为自动重传请求(ARQ)协议。    

        ARQ 协议还需要另外三种协议功能来处理比特差错的情况:

        ① 差错检测。首先,需要一种机制以使接收方检测到何时出现了比特差错。前一节讲到, UDP 使用因特网检验和字段正是为了这个目的。在第 5 章中,我们将更详细地学习差错检测和纠错技术。这些技术使接收方可以检测并可能纠正分组中的比特差错。此刻,只需知道这些技术要求有额外的比特(除了待发送的初始数据比特之外的比特)从发送方发送到接收方;这些比特将被汇集在 rdt2 .0 数据分组的分组检验和字段中。

        ② 接收方反馈。因为发送方和接收方通常在不同端系统上执行,可能相隔数千英里,发送方要了解接收方情况(此时为分组是否被正确接收)的唯一途径就是让接收方提供明确的反馈信息给发送方。在口述报文情况下回答的"肯定确认" (ACK) 和"否定确认" (NAK) 就是这种反馈的例子。类似地,rdt2 .0 协议将从接收方向发送方回送 ACK 和 NAK 分组。理论上,这些分组只需要一个比特长:如用 0 表示 NAK ,用 1 表示 ACK。

        ③ 重传。接收方收到有差错的分组时,发送方将重传该分组文。 

        图 3-10 说明了表示 rdt2.0 的 FSM,该数据传输协议采用了差错检测、肯定确认与否定确认

 

        rdt2.0 的发送端有两个状态。在最左边的状态中,发送端协议正等待来自上传传下来的数据。当 rdt_send(data) 事件出现时,发送方将一个包含待发送数据的分组,带有检验和,然后经由 udt_send(sndpkt) 操作发送该分组。在最右边的状态,发送方协议等待来自接收方的 ACK 或 NAK 分组。如果收到一个 ACK 分组(图3-10 中符号 rdt_rcv(rcvpkt) && isACK(rcvpkt) 对应该事件),则发送方知道最近发送的分组已被正确接收,因此协议返回到等待来自上层的数据的状态。如果收到一个 NAK 分组,该协议重传一个分组并等待接收方为响应重传分组而回送的 ACK 和 NAK。下列事实很重要:当发送方处于等待 ACK 或 NAK 的状态时,它不能从上层获得更多的数据;就是说,rdt_send() 事件不可能出现仅当接收到 ACK 并离开该状态时才能发生这样的事件。因此,发送方将不会发送一块新数据,除非发送方确信接收方已正确接收当前分组。由于这种行为,rdt2.0 这样的协议被称为 停等协议

        rdt2.0 接收方的 FSM 仍然只有单一状态。当分组到达时,接收方要么回答一个 ACK,要么回答一个 NAK,这取决于收到的分组是否受损。在图 3-10 中,符号 rdt_rcv(rcpkt) && corrupt(rcvpkt) 对应于收到一个分组并发现有错的事件。 

        rdt2.0 协议看似可行了。但遗憾的是,它存在一个致命的缺陷。尤其是没有考虑到 ACK 和 NAK 分组受损的可能性。至少,需要在 ACK/NAK 分组中添加检验和比特以检测这样的差错。更难的问题是协议应该怎样纠正 ACK 或 NAK 分组中的差错。难点在于,如果一个 ACK 或 NAK 分组受损,发送方无法知道接收方是否接受了上一块发送的数据

         考虑处理受损 ACK 和 NAK 时的三种可能性:

        ① 考虑在口述报文情况下人可能的做法。如果说话者不理解来自接收方回答的 “ OK ”或 “ 请重复一遍 ”,说话者可能问 “ 你说什么?” (因此在协议中引入了一种新型发送方到接收方的分组)接收方则将复述其回答。但如果说话者的 “ 你说什么 ”产生了差错,情况又会怎样呢?接收者不明白那句混淆的话是口述内容的一部分,还是一个要求重复上次回答的请求,很可能回一句 “你说什么?”。于是回答可能含糊不清了,走上一条苦难重重之路。

        ② 增加足够的检验和比特,使发送方不仅可以检测差错,还可以恢复差错。对于会产生差错但不丢失分组的信道,可以直接解决问题。

        ③ 当发送方收到含糊不清的 ACK 或 NAK 分组时,只需重传当前数据分组即可。然而,这种方法在发送方到接收方的信道中引入了冗余分组。冗余分组的根本困难在于接收方不知道它上次所发送的 ACK 或 NAK 是否被发送方正确地收到。因此,它无法事先知道接收到的分组是新的还是重传。 

         解决这个新问题的一个简单方法(几乎所有现有的数据传输协议中,包括 TCP ,都采用了这种方法)是在数据分组中添加一新字段,让发送方对其数据分组编号, 即将发送数据分组的序号 (sequence number)放在该字段。于是,接收方只需要检查序号即可确定收到的分组是否一次重传。对于停等协议这种简单情况,1 比特序号就足够了,因为它可让接收方知道发送方是否正在重传前一个发送分组(接收到的分组序号与最近收到的分组序号相同),或是一个新分组(序号变化了,用模2 运算"前向"移动) 。因为目前我们假定信道不丢分组, ACK NAK 分组本身不需要指明它们要确认的分组序号。发送方知道所接收到的 ACK NAK 分组(无论是否是含糊不清的)是为响应其最近发送的数据分组而生成的。

        图 3-11 和图3-12 给出了对 rdt2.1 的 FSM 描述,是 rdt2.0 的修订版。rdt2.1 的发送方和接收方 FSM 的状态数都是以前的两倍。这是因为协议状态此时必须反映出目前(由发送方)正发送的分组(在接收方)希望接收的分组的序号是 0 还是 1。发送或期望接收 0 号分组的状态中的动作与发送或期望接收 1号分组的状态中的动作是相似的;唯一的不同是序号处理的方法不同

         协议 rdt2.1 使用了从接收方到发送方的肯定确认和否定确认。当接收到失序的分组时,接收方对所接收的分组发送一个肯定确认。如果收到受损的分组,则接收方将发送一个否定确认。如果不发送 NAK ,而是对上次正确接收的分组发送一个 ACK ,我们也能实现与 NAK 一样的效果。发送方接收到对同一个分组的两个 ACK(即接收冗余 ACK(duplicate ACK) )后,就知道接收方没有正确接收到跟在被确认两次的分组后面的分组。rdt2.2 是在有比特差错信道上实现的一个无 NAK 的可靠数据传输协议,如图 3- 13、3.14 所示。rt2.1和 rdt2.2 之间的细微变化在于,接收方此时必须包括由一个 ACK 报文所确认的分组序号(这可以通过在接收方 FSM 中,在 make_pkt () 包括参数 ACK 0 或ACK 1 来实现),发送方此时必须检验接收到的 ACK 报文中被确认的分组序号(这可通过在发送方 FSM ,在 isACK() 中包括参数 0 或 1 来实现)。

 

 

3. 经具有比特差错的丢包信道的可靠数据传输:rdt3.0

         现在假定除了比特受损外 ,底层信道还会丢包,这在今天的计算机网络(包括因特网)中并不罕见。协议现在必须处理另外两个关注的问题: 怎样检测丢包以及发生丢包后该做些什么,rdt2.2 中已经研发的技术,如使用检验和、序号、ACK 分组和重传等。为解决第一个关注的问题,还需增加一种新的协议机制。

         有很多可能的方法用于解决丢包问题,这里,让发送方负责检测和恢复丢包工作。假定发送方传输一个数据分组,该分组或者接收方对该分组ACK 发生了丢失。这两种情况下,发送方都收不到应当到来的接收方响应。如果发送方愿意等待足够长的时间以便确定分组已经丢失,则它只需重传该数据分组即可。

        但是发送方需要等待多久才能确定已丢失了某些东西呢?发送方至少需要等待这样长的时间:即发送方与接收方之间的一个往返时延(可能会包括在中间路由器的缓冲时延)加上接收方处理一个分组所需的时间。在很多网络中,最坏情况下的最大时延很难估算的,确定的因素非常少。此外,理想的协议应尽可能快地从丢包中恢复出来;等待一个最坏情况的时延可能意味着要等待一段较长的时间,直到启动差错恢复为止。因此实践中采取的方法是发送方明智地选择一个时间值,以判定可能发生了丢包(尽管不能确保)。如果在这个时间内没有收到 ACK,则重传该分组.注意到如果一个分组经历了一个特别大的时延,发送方可能会重传该分组,即使该数据分组及其 ACK 都没有丢失。这就在发送方到接收方的信道中引入了冗余数据分组的可能性。幸运的是,rdt2.2 协议已经有足够的功能(序号)处理冗余分组的情况。

        从发送方的观点来看,重传是一种万能灵药。发送方不知道是一个数据分组丢失,还是一个 ACK 丢失,或者只是该分组或 ACK 过度延时。在所有这些情况下,动作是同样的:重传。为了实现基于时间的重传机制,需要一个倒计数定时器 (countdown timer) ,在一个给定的时间量过期后,可中断发送方。因此,发送方需要能做到:①每次发送一个分组(包括第一次分组和重传分组)时,便启动一个定时器②响应定时器中断(采取适当的动作)。③终止定时器

        图 3-15 给出了 rdt3.0 的发送方 FSM,这是一个在可能出错和丢包的信道上可靠传输数据的协议。

        图 3-16 显示了在没有丢包和延迟分组情况下协作运作的情况,以及如何处理数据分组丢失的。在图 3-16 中,时间从图的顶部朝底部移动;注意到一个分组的接收时间必定迟于一个分组的发送时间,这是因为发送时延与传播时延之故。在图 3-16b~d 中,发送方括号部分表明了定时器的设置时刻以及随后的超时。因为分组序号在 0 和 1 之间交替,因此时rdt3.0 有时被称为比特交替协议( alternating- bìt protocol)。

 

 

相关文章:

计算机网络 第三章:运输层(一)

运输层位于应用层和网络层之间,是分层的网络体系结构的重要部分。该层为运行在不同主机上的应用进程提供直接的通信服务。通常特别关注因特网协议,即 TCP 和 UDP 运输层协议。 讨论运输层和网络层的关析,为研究运输层第一个关键功能&#xff…...

机器学习(14)——模型调参

文章目录 一、动态调参方法论1. 调参策略选择2. 千万数据优化原则 二、模型调参策略对比1. LightGBM调参路线2. XGBoost调参路线3. 随机森林调参策略 三、代码实现示例通用数据准备(适用于所有模型)1. LightGBM调参示例2. XGBoost调参示例3. 随机森林调参…...

基于PetaLinux的Zynq PS应用自启动全攻略

一、嵌入式Linux启动管理的艺术 在工业机器人、智能摄像头、边缘计算网关等场景中,开机自启动管理是系统可靠性的第一道关卡。本文将深入讲解Zynq PS端在PetaLinux环境下实现: Systemd服务深度定制启动时序精准控制启动速度优化技巧动态服务管理创新方案二、环境搭建与工程配…...

贪心算法:多处最优服务次序、删数问题

多处最优服务次序问题 问题描述:设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti(1≤i≤n),共有s处可以提供此项服务。应如何安排n个顾客的服务次序,才能使平均等待时间达到最小?平均等待时间是n个顾客等待服务时间的总和除以n。 算法设计:对于给定的n个顾…...

使用 Flask 框架实现FTP,允许用户通过 Web 界面浏览和下载文件夹中的所有文件

Flask 文件和文件夹下载服务实现 以下是一个基于 Flask 框架的简单 Web 服务,用于开放指定文件夹(./shared_files),允许用户通过浏览器浏览和下载文件夹中的所有文件和子文件夹。ZIP 和 TAR 文件将直接下载,而文件夹将…...

【Go】从0开始学习Go

文章目录 从0开始学习Go0 与C对比1 代码框架1.1 helloworld式代码示例1.2 主体代码元素(核心三部分)1.3 其他 2 与C/C区别3 有用的小工具4 注意事项 从0开始学习Go 0 与C对比 特性CGo编译型语言需要编译为机器码直接编译为二进制可执行文件静态类型类型…...

软件设计师SQL考点分析——求三连

一、考点分值占比与趋势分析 综合知识分值统计表(75分制) 年份考题数量分值分值占比考察重点2018334%关系代数、权限控制2019222.67%SQL注入、授权语句2020445.33%投影操作、权限回收2021334%视图操作、权限传递2022222.67%数据库安全、WITH GRANT OPT…...

使用tcs34725传感器和51单片机识别颜色

使用TCS34725颜色传感器和51单片机来识别颜色是一个非常有趣的项目。TCS34725是一种常用的RGB颜色传感器,能够测量红、绿、蓝光的强度,从而实现颜色识别。 1. 硬件连接 TCS34725传感器通过IC接口与51单片机连接。以下是连接方式: SDA&…...

数据库-oracle-包-视图传参

并发下可能不准确 -- 修改包规范 CREATE OR REPLACE PACKAGE sczz.p_view_param IS function set_n(n varchar2) return varchar2; function get_n return varchar2; function set_ny(ny varchar2) return varchar2; function get_ny return varchar2; …...

深入探讨Java中的上下文传递与ThreadLocal的局限性及Scoped Values的兴起

在Java开发中,特别是在依赖框架的应用程序中,上下文数据的管理是一个常见但具有挑战性的问题。上下文数据可能包括元数据、配置信息或其他需要在代码不同部分之间共享的信息。传统的做法是通过方法参数显式传递这些上下文,但这种方法会导致代码复杂、难以维护,尤其是在大型…...

Spring boot 学习笔记2

Maven 项目管理工具:Maven 通过 pom.xml(Project Object Model)文件描述项目配置,包括依赖、构建流程、插件等,实现项目标准化管理 依赖管理:自动下载并管理项目所需的第三方库(如 Spring、MyB…...

“保证医疗器械信息来源合法 真实、安全的保障措施、情况说明及相关证明”模板

保证医疗器械信息来源合法真实、安全的保障措施、情况说明及相关证明 一、医疗器械信息来源合法、真实、安全的管理措施 目前我公司网站所展示的医疗器械是企业代理品种,是取得合法注册资格的产品,拥有合法证明文件的产品。本网站仅展示本公司行政许可…...

Feature Toggle 不再乱:如何设计一个干净、安全、可控的特性开关系统?

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…...

不锈钢保温容器行业2025数据分析报告

不锈钢保温容器市场概况 2024年全球不锈钢保温容器市场规模约为453.3亿元,预计到2031年将增长至608.3亿元,年均复合增长率(CAGR)为4.3%。这一增长主要得益于全球范围内对保温容器需求的持续增加,尤其是在户外活动、餐…...

leetcode239 滑动窗口最大值deque方式

这段文字描述的是使用单调队列&#xff08;Monotonic Queue&#xff09; 解决滑动窗口最大值问题的优化算法。我来简单解释一下&#xff1a; 核心思路 问题分析&#xff1a;在滑动窗口中&#xff0c;若存在两个下标 i < j 且 nums[i] ≤ nums[j]&#xff0c;则 nums[i] 永远…...

腾讯云怎么在游戏云中助力

腾讯云游戏云&#xff1a;依托深厚游戏基因&#xff0c;打造高质量全方位生态平台 在竞争激烈的云计算市场中&#xff0c;腾讯云凭借其得天独厚的游戏生态资源和深耕多年的技术沉淀&#xff0c;正成为游戏行业不可忽视的重要力量。腾讯不仅是全球领先的游戏开发和发行商&#…...

深入理解pip:Python包管理的核心工具与实战指南

# 深入理解pip&#xff1a;Python包管理的核心工具与实战指南 在Python开发中&#xff0c;第三方库是提升效率的关键。而pip作为Python官方的包管理工具&#xff0c;承担着安装、卸载、升级和管理库的重要职责。本文将全面解析pip的核心命令&#xff0c;结合实例演示用法&#…...

【python】windows修改 pip 默认安装路径

在 Windows 系统 下&#xff0c;希望修改 pip 默认安装路径&#xff0c;结合你前面贴的图片和信息&#xff0c;一个 推荐做法&#xff08;不修改 site.py&#xff09;的完整教程。 目标&#xff1a;让 pip 安装包默认装到你指定的路径&#xff08;如 D:\MyPythonLibs&#xff…...

Python函数——万字详解

—— 小 峰 编 程 导 语&#xff1a; 从今天开始&#xff0c;我们将进入第二模块的学习——函数。第一模块主要是学习python基础知识&#xff0c;从第二模块开始就可以通过程序去解决工作中实际的问题。从今天开始&#xff0c;我们将进入第二模块的学习&#xff0c;此模块…...

es在已有历史数据的文档新增加字段操作

新增字段设置默认值 场景 在已经有大量数据的索引文档上&#xff0c;增加新字段 技术实现 一.更新索引映射 通过PUT请求显式定义新字段类型&#xff0c;确保后续写入的文档能被正确解析 PUT /文档名/_mapping {"properties": {"字段名1": {"type…...

LeetCode 35 搜索插入位置题解

LeetCode 35 搜索插入位置题解 题目描述 题目链接 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置&#xff08;需保证数组仍然有序&#xff09;。要求时间复杂度为 O(log n)。…...

RabbitMQ通信模式(Simplest)Python示例

RabbitMQ通信模式-Python示例 0.RabbitMQ官网通信模式1.Simplest(简单)模式1.1 发送端1.2 接收端 0.RabbitMQ官网通信模式 1.Simplest(简单)模式 1.1 发送端 # -*- coding: utf-8 -*- """ Author: xxx date: 2025/5/19 11:30 Description: Simaple简单模…...

游戏开发实战(一):Python复刻「崩坏星穹铁道」嗷呜嗷呜事务所---源码级解析该小游戏背后的算法与设计模式【纯原创】

文章目录 奇美拉项目游戏规则奇美拉(Chimeras)档案领队成员 结果展示&#xff1a; 奇美拉项目 由于项目工程较大&#xff0c;并且我打算把我的思考过程和实现过程中踩过的坑都分享一下&#xff0c;因此会分3-4篇博文详细讲解本项目。本文首先介绍下游戏规则并给出奇美拉档案。…...

力扣热题100之删除链表的倒数第N个节点

题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 代码 方法一 将链表中的值放入列表中&#xff0c;然后删除倒数第n个值&#xff0c;再将剩下的数依次转化为链表 # Definition for singly-linked list. # class ListNode: # …...

OCframework编译Swift

建一个OC的framework&#xff1a; 需要对外暴露的OC文件&#xff0c;需要放到OC的.h文件中 framework中&#xff0c;OC类&#xff0c;调用framework中的Swift类&#xff1a; #import "WowAudioFocus/WowAudioFocus-Swift.h" //02 #import "{工程名}/{工程…...

【AI News | 20250519】每日AI进展

AI Repos 1、deepdrone DeepDrone是一款基于smolagents框架的无人机聊天代理&#xff0c;集成DroneKit实现无人机分析与操作。用户可通过自然语言聊天与无人机助手交互&#xff0c;实现飞行路径和传感器数据可视化、基于飞行时长的维护建议、任务规划以及真实的无人机控制&…...

分布式ID生成系统

代码地址: github mid 简介 分布式 ID 生成系统是一个高性能、可靠的 ID 生成服务,支持两种模式:Snowflake(基于时间戳的内存生成)和 Segment(基于 MySQL 的号段分配)。系统采用双 Buffer 策略优化性能,集成 Prometheus 监控和 Zap 结构化日志,确保高可用性和可观测性…...

MAC常用操作整理

音量方法&#xff1a; 电脑键盘的右上角就有静音和不静音的按钮&#xff0c;还有调节音量的按钮&#xff0c;调节屏幕亮度的按钮 切换输入法方法&#xff1a; 1.大写按键&#xff0c;2.function按键(fn), 3.control 空格键, 选择上一个输入法&#xff0c;4.controloption空格…...

【Canvas与图标】圆角方块蓝星CSS图标

【成图】 120*120的png图标 大小图&#xff1a; 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>圆角方块蓝星CSS Draft1</…...

易境通散货拼柜系统:提高货代企业货物配载效率

在国际物流代理运输领域&#xff0c;货物配载是整个供应链的核心环节&#xff0c;其优化对于提升整个供应链的效率至关重要。传统的配载管理方式往往依赖人工操作&#xff0c;不仅效率低下&#xff0c;还容易出现错误。面对多订单、多货主、多目的地的复杂场景&#xff0c;传统…...

[Spring Boot]整合Java Mail实现Outlook发送邮件

日常开发过程中,我们经常需要使用到邮件发送任务,比方说验证码的发送、日常信息的通知等。日常比较常用的邮件发送方包括:163、QQ等,本文主要讲解Outlook SMTP的开启方式、OutLook STARTTTL的配置、如何通过JavaMail来实现电子邮件的发送等。 Outlook作为微软提供的企业电子…...

【盈达科技】GEO优化实战策略

提升内容在生成式引擎中的可见性&#xff1a;实战策略 随着生成式引擎&#xff08;Generative Engines, GEs&#xff09;的兴起&#xff0c;内容创作者面临着新的挑战和机遇。这些引擎通过整合和总结多源信息来提供精准且个性化的回答&#xff0c;正在迅速取代传统搜索引擎。为…...

HTTP 协议基础

本篇文章会从如下角度介绍 HTTP 协议&#xff1a; 原理与工作机制请求方法与状态码Header 与 Body 1、原理与工作机制 1.1 HTTP 是什么 HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff0c;"超"表示扩展而非超级&#xff0c;即可以链接到其他文本…...

ros运行包,Ubuntu20.04成功运行LIO-SAM

zz:~/lio_sam_ws$ source devel/setup.bash zz:~/lio_sam_ws$ roslaunch lio_sam run.launch 创建包链接&#xff1a; 链接1&#xff1a;Ubuntu20.04成功运行LIO-SAM_ubuntu20.04运行liosam-CSDN博客 链接2&#xff1a;ubuntu 20.04 ROS 编译和运行 lio-sam,并且导出PCD文件…...

Linux《自主Shell命令行解释器》

在上一篇的进程控制当中我们已经了解了进程退出、进程等待以及进程替换的相关概念&#xff0c;那么在了解了这些的概念之后接下来在本篇当中我们就可以结合之前我们学习的知识来实现一个自主的Shell命令行解释器&#xff0c;通过Shell的实现能让我们进一步的理解操作系统当中的…...

设置IDEA打开新项目使用JDK17

由于最近在学习Spring-AI&#xff0c;所以JDK8已经不适用了&#xff0c;但是每次创建新项目都还是JDK8&#xff0c;每次调来调去很麻烦 把Projects和SDKs都调整为JDK17即可 同时&#xff0c;Maven也要做些更改&#xff0c;主要是添加build标签 <build><plugins>&…...

Vue百日学习计划Day36-42天详细计划-Gemini版

总目标: 在 Day 36-42 理解组件化开发的思想&#xff0c;熟练掌握 Vue 组件的注册、Props、Events、v-model、Slots、Provide/Inject 等核心概念和实践&#xff0c;能够构建可复用和易于维护的组件结构。 所需资源: Vue 3 官方文档 (组件基础): https://cn.vuejs.org/guide/es…...

Python对JSON数据操作

在Python中&#xff0c;对JSON数据进行增删改查及加载保存操作&#xff0c;主要通过内置的json模块实现。 一、基础操作 1. 加载JSON数据 • 从文件加载 使用json.load()读取JSON文件并转换为Python对象&#xff08;字典/列表&#xff09;&#xff1a; import json with open…...

upload靶场1-5关

网上的解析有一些题目对应不上&#xff0c;比如第五关说是 空格 点 空格绕过 &#xff0c;我这里就无法成功解析&#xff0c;但大小写绕过就成功了&#xff0c;慢慢会把后面的关卡也写出来 这里建议开一台win7的虚拟机&#xff0c;在上面搭建靶场&#xff0c;可以省很多麻烦 …...

网络传输(ping命令,wget命令,curl命令),端口

网络传输&#xff1a; ping命令&#xff1a;检查指定的网络服务器是否是可联通状态 语法&#xff1a;ping 【-c num】IP或主机名 -c&#xff1a;是检查的次数&#xff0c;不使用-c&#xff0c;将无限次持续检查 wget命令&#xff1a;wget是非交互式的文件下载器&#xff0…...

upload-labs靶场通关详解:第10关

一、分析源代码 $is_upload false; $msg null; if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {$deny_ext array(".php",".php5",".php4",".php3",".php2",".html",".htm",".ph…...

深入解析`lsof`命令:查看系统中打开文件与进程信息

1、lsof的基本概念 lsof &#xff08;List Open Files&#xff09; 提供了一种方式来查看系统上哪些进程正在访问哪些文件&#xff0c;能够显示文件类型、文件名、文件描述符、所属进程等详细信息。 在类Unix系统中&#xff0c;几乎所有的操作都与文件相关联&#xff0c;文件不…...

C++ 与 Python 内存分配策略对比

内存管理是编程中的一个核心概念&#xff0c;它直接影响程序的性能、稳定性和资源利用率。C 和 Python 作为两种广泛使用的编程语言&#xff0c;在内存分配和管理方面采用了截然不同的策略。 C 内存分配策略 C 赋予程序员对内存的精细控制能力&#xff0c;同时也带来了更大的…...

TB开拓者策略交易信号闪烁根因及解决方法

TB开拓者策略信号闪烁分析 TB开拓者策略交易信号闪烁根因 TB开拓者策略交易信号闪烁根因分析 信号闪烁是交易策略开发中常见的问题&#xff0c;特别是在TB(TradeBlazer)开拓者等平台上。以下是信号闪烁的主要根因分析&#xff1a; 主要根因 未来函数问题 使用了包含未来信息…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(24):受身形

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(24):受身形 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)うけみけい 受身形(2)復習(ふくしゅう):3、单词(1)日语(2)日语片假名单词4、相近词练习5、单词辨析记录6、总结1、前言 (1)情况说明 自己在今…...

牛客网NC209794:使徒袭来

牛客网NC209794:使徒袭来 题目背景 问题分析 数学建模 设三位驾驶员的战斗力分别为 a, b, c已知条件&#xff1a;a b c n (n为输入的正整数)目标&#xff1a;求 a b c 的最小值 解题思路 根据算术-几何平均值不等式(AM-GM不等式)&#xff0c;对于任意正实数a, b, c&a…...

命令行登录 MySQL 报 Segmentation fault 故障解决

问题描述&#xff1a;对 mysql8.0.35 源码进行 make&#xff0c;由于一开始因为yum源问题少安装依赖库 库&#xff0c;在链接时遇到错误 undefined reference to&#xff0c;后来安装了相关依赖库&#xff0c;再次 make 成功。于是将 mysqld 启动&#xff0c;再用 mysql -u roo…...

2025ICPC邀请赛南昌游记

滚榜时候队伍照片放的人家的闹麻了&#xff0c;手机举了半天 。 最后银牌700小几十罚时&#xff0c;rank60多点。 参赛体验还行&#xff0c;队长是福建人&#xff0c;说感觉这个热度是主场作战哈哈哈哈。空调制冷确实不太行吧。 9s过A是啥&#xff0c;没见过&#xff0c;虽然…...

kotlin flow的写法

以下是 Android 开发中 Kotlin Flow 的常见使用模式和操作符的完整中文总结&#xff1a; 1. 基本 Flow 创建方式 // 从多个值创建 val flow1 flowOf(1, 2, 3)// 使用 flow 构建器 val flow2 flow {emit(1)delay(100)emit(2) }// 从集合创建 val flow3 listOf(1, 2, 3).asFl…...

springboot+mybatis或mybatisplus在进行%name%的前后模糊查询时如何放防止sql注入

在使用 Spring Boot 配合 MyBatis 或 MyBatis-Plus 进行数据库操作时&#xff0c;确保防止 SQL 注入是非常重要的。对于 %name% 样式的前后模糊查询&#xff0c;以下是几种有效的方法来防止 SQL 注入&#xff1a; 1. 使用 MyBatis 的 <if> 标签和 #{} 占位符 MyBatis 默…...