P2P中NAT穿越方案(UDP/TCP)(转)

转自:P2P中NAT穿越方案(UDP/TCP)_udp反向链接-CSDN博客

同:P2P中NAT穿越方案(UDP/TCP) - 知乎 (zhihu.com)

本文介绍了传统基于udp的打洞方式,更进一步阐述了tcp打洞的原理,是对于打洞原理最完善的讲解。

————————————
 

1、P2P简介

对等网络,即对等计算机网络,是一种在对等者(Peer)之间分配任务和工作负载的分布式应用架构,是对等计算模型在应用层形成的一种组网或网络形式。因此,从字面上,P2P可以理解为对等计算或对等网络。

在P2P网络环境中,彼此连接的多台计算机之间都处于对等的地位,各台计算机有相同的功能,无主从之分,一台计算机既可作为服务器,设定共享资源供网络中其他计算机所使用,又可以作为工作站,整个网络一般来说不依赖专用的集中服务器,也没有专用的工作站。网络中的每一台计算机既能充当网络服务的请求者,又对其它计算机的请求做出响应,提供资源、服务和内容。通常这些资源和服务包括:信息的共享和交换、计算资源(如CPU计算能力共享)、存储共享(如缓存和磁盘空间的使用)、网络共享、打印机共享等。P2P网络具有分散性、可扩展性、健壮性等特点,这使得P2P技术在信息共享、实时通信、协同工作、分布式计算、网络存储等领域都有广阔的应用。

2、NAT简介

NAT技术是一种把内部网络(简称为内网)私有IP地址转换为外部网络(简称为外网)公共IP地址的技术,它使得一定范围内的多台主机只利用一个公共IP地址连接到外网,可以在很大程度上缓解了公网IP地址紧缺的问题。

3、NAT对P2P通信的影响

NAT技术虽然在一定程度上解决了IPv4地址短缺的问题,在构建防火墙、保证网络安全方面都发挥了一定的作用,却破坏了端到端的网络通信。NAT阻碍主机进行P2P通信的主要原因是NAT不允许外网主机主动访问内网主机,因为NAT设备上没有相关转发表项,要在NAT网络环境中进行有效的P2P通信,就必须寻找相应的解决方案。本文就着重介绍几种常见的解决方案。

4、P2P穿越NAT的方案

4.1 反向链接技术

当通信的双方中只有一方位于NAT之后时,它们可以利用反向链接技术来进行P2P通信。图3中Client A(拥有内网IP地址10.0.0.1)位于NAT之后,它通过TCP端口1234连接到服务器(拥有外网IP地址)的TCP端口1235上,NAT设备(拥有外网IP地址155.99.25.11)为这个连接重新分配了TCP端口62000。Client B(拥有外网IP地址138.76.29.7)也通过TCP端口1234连接到服务器端口1235上。Client A和Client B从服务器处获知的对方的外网地址二元组{IP地址:端口号}分别为{138.76.29.7:1234}和{155.99.25.11:62000},它们在各自的本地端口上进行侦听。

由于Client B 拥有外网IP地址,所以Client A要发起与Client B的通信,那么它可以直接通过TCP连接到Client B。但如果Client B尝试通过TCP连接到Client A进行P2P通信,则会失败,原因是Client A位于NAT设备后,虽然Client B发出的TCP SYN请求能够到达NAT设备的端口62000,但NAT设备会拒绝这个连接请求。要想与Client A通信,Client B要通过服务器给Client A转发一个连接请求,反过来请求Client A连接到Client B(即进行反向链接),从而建立起它们之间的TCP连接。

4.2 UDP打洞技术

如果两个P2P客户端都位于NAT设备后面,想要进行P2P通信,那又该如何解决呢?UDP打洞技术就是为解决这个问题而应运而生的,它能够通过中间服务器实现P2P客户端互连。

4.2.1 集中服务器

打洞技术假定客户端A和客户端B都可以与公网内的已知集中服务器建立UDP连接,一个客户端在集中服务器上登陆的时候,服务器记录下该客户端的两对地址二元组信息{IP地址:UDP端口},一对是该客户端与集中服务器进行通信的自身的IP地址和端口号,另一对是集中服务器记录下的由服务器“观察”到的该客户端实际与自己通信所使用的IP地址和端口号。我们可以把前一对地址二元组看作是客户端的内网IP地址和端口号,把后一对地址二元组看作是客户端的内网IP地址和端口号经过NAT转换后的外网IP地址和端口号。集中服务器可以从客户端的登陆消息中得到该客户端的内网相关信息,还可以通过登陆消息的IP头和UDP头得到该客户端的外网相关信息。如果该客户端不是位于NAT设备后面,那么采用上述方法得到的两对地址二元组信息是完全相同的。

4.2.2 建立P2P的SESSION

假定客户端A要发起对客户端B的直接连接,具体的“打洞”过程如下:

(1)客户端A最初不知道如何向客户端B发起连接,于是客户端A向集中服务器发送消息,请求集中服务器帮助建立与客户端B的UDP连接。

(2)集中服务器将含有客户端B的外网和内网的地址二元组发给客户端A,同时,集中服务器将包含有客户端A的外网和内网的地址二元组信息的消息也发给客户端B。这样一来,客户端A与客户端B就都知道对方外网和内网的地址二元组信息了。

(3)当客户端A收到由集中服务器发来的包含客户端B的外网和内网的地址二元组信息后,客户端A开始向客户端B的地址二元组发送UDP数据包,并且客户端A会自动锁定第一个给出响应的客户端B的地址二元组。同理,当客户端B收到由集中服务器发来的客户端A的外网和内网地址二元组信息后,也会开始向客户端A的外网和内网的地址二元组发送UDP数据包,并且自动锁定第一个得到客户端A回应的地址二元组。由于客户端A与客户端B互相向对方发送UDP数据包的操作是异步的,所以客户端A和客户端B发送数据包的时间先后并没有时序要求。

下面来看下这三者之间是如何进行UDP打洞的。在这我们分三种具体情景来讨论:

第一种是最简单的一种情景,两个客户端都位于同一个NAT设备后面,即位于同一内网中;

第二种是最普遍的一种情景,两个客户端分别位于不同的NAT设备后面,分属不同的内网;

第三种是客户端位于两层NAT设备之后,通常最上层的NAT是由网络提供商提供的,第二层NAT是家用的NAT路由器之类的设备提供的。

4.2.3 P2P的两个客户端位于同一个NAT设备后面

首先假设两个客户端位于同一个NAT设备后面,并且位于内网,如图4所示。客户端A与集中服务器建立了UDP连接,经过NAT转换后,A的公网端口被映射为62000。客户端B同样与集中服务器建立了UDP连接,公网端口映射为62005。

假设客户端A想通过集中服务器,发起对客户端B的连接。客户端A向集中服务器发出消息请求与客户端B进行连接,集中服务器将客户端B的外网地址二元组以及内网地址二元组发给客户端A,同时把客户端A的外网以及内网的地址二元组信息发给客户端B。客户端A和客户端B发往对方公网地址二元组信息的UDP数据包不一定会被对方收到,这取决于当前的NAT设备是否支持不同端口之间的UDP数据包能否到达即Hairpin转换特性,无论如何客户端A与客户端B发往对方内网的地址二元组信息的UDP数据包是一定可以到达的,内网数据包不需要路由,且速度更快。客户端A与客户端B推荐采用内网的地址二元组信息进行常规的P2P通信。

假定NAT设备支持Hairpin转换,具体的Hairpin转换见4.2.5章节,应用程序也应忽略与内网地址二元组的连接,如果客户端A、客户端B采用外网的地址二元组做为P2P通信的连接,这势必会造成数据包无谓地经过NAT设备,这是一种对资源的浪费。就目前的网络情况而言,应用程序在“打洞”的时候,最好还是把外网和内网的地址二元组都尝试一下。如果都能成功,优先以内网地址进行连接。

4.2.4 P2P客户端位于不同的NAT设备后面

假定客户端A与客户端B在不同的NAT设备后面,分属不同的内网,如图5所示。客户端A与客户端B都经由各自的NAT设备与集中服务器建立了UDP连接,客户端A与客户端B的本地端口号均为4321,集中服务器的公网端口号为1234。在向外的会话中,客户端A的外网IP被映射为155.99.25.11,外网端口为62000,客户端B的外网IP被映射为138.76.29.7,外网端口为31000。

如下所示:

客户端A——>本地IP:10.0.0.1,本地端口:4321,外网IP:155.99.25.11,外网端口:62000

客户端B——>本地IP:10.1.1.3,本地端口:4321,外网IP:138.76.29.7,外网端口:31000

在客户端A向服务器发送的登陆消息中,包含有客户端A的内网地址二元组信息,即10.0.0.1:4321;服务器会记录下客户端A的内网地址二元组信息,同时会把自己观察到的客户端A的外网地址二元组信息记录下来,即155.99.25.11:62000。同理,服务器也会记录下客户端B的内网地址二元组信息为10.1.1.3:4321和由服务器观察到的客户端B的外网地址二元组信息,138.76.29.7:31000。无论A与B二者中的任何一方向服务器发送P2P连接请求,服务器都会将其记录下来的上述的外网和内网地址二元组发送给A或B。

A、B分属不同的内网,它们彼此的内网地址在外网中是没有路由的,所以发往各自内网地址的UDP数据包会发送到错误的主机或者根本不存在的主机上。现在假定A的第一个消息将发往B的外网地址,如图5所示。该消息途经A的NAT设备,并在该设备上生成一个会话表项,该会话的源地址二元组信息是{10.0.0.1:4321},该地址二元组信息和客户端A与服务器建立连接的时候NAT生成的源地址二元组信息一样,但它的目的地址不同。如果A的NAT设备给出的响应是OK的,那么A的NAT设备将保留A的内网地址二元组信息,并且所有来自A的源地址二元组信息为{10.0.0.1:4321}的数据包都沿用A与集中服务器事先建立起来的会话,外网地址二元组信息均为{155.99.25.11:62000}。

A向B的外网地址发送消息的过程就是“打洞”的过程,从A的内网的角度来看应为从{10.0.0.1:4321}发往{138.76.29.7:31000},从A在其NAT设备上建立的会话来看,是从{155.99.25.11:62000}发到{138.76.29.7:31000}。

如果A发给B的外网地址二元组的消息包在B向A发送消息包之前到达B的NAT设备,B的NAT设备会认为A发过来的消息是未经授权的外网消息,会丢弃掉该数据包。B发往A的消息包与上述的过程一样,会在B的NAT设备上建立一个{10.1.1.3:4321,155.99.25.11:62000}的会话(通常也会沿用B与集中服务器连接时建立的会话,只是该会话现在不仅接受由服务器发给B的消息,还可以接受从A的NAT设备155.99.25.11:6200发来的消息),一旦A与B都向对方的NAT设备在外网上的地址二元组发送了数据包,就打开了A与B之间的“洞”,A与B向对方的外网地址发送数据,等效为向对方的客户端直接发送UDP数据包了。一旦应用程序确认已经可以通过往对方的外网地址发送数据包的方式让数据包到达NAT后面的目的应用程序,程序会自动停止继续发送用于“打洞”的数据包,转而开始真正的P2P数据传输。

4.2.5 P2P客户端位于多层NAT设备后面

有的网络拓扑结构包含了多个NAT设备,如果没有掌握该拓扑结构的详细信息,两个客户端之间是无法建立“最优化”的P2P路由的。现在我们来讨论最后一种情况,如图6所示。假定NAT C是由ISP(Internet Service Provider)提供的NAT设备,NAT C提供将多个用户节点映射到有限的几个公网IP的服务,NAT A和NAT B作为NAT C的内网节点将把用户的家庭网络或内部网络接入NAT C的内网,然后用户的内部网络就可以经由NAT C访问公网了。从这种拓扑结构上来看,只有服务器与NAT C是真正拥有公网可路由IP地址的设备,而NAT A和NAT B所使用的公网IP地址,实际上是由ISP服务提供商设定的(相对于NAT C而言)内网地址(本文的后续部分把这个由ISP提供的内网地址相对于NAT C称之为“伪”公网地址),同理隶属于NAT A与NAT B的客户端,相对与NAT A,NAT B而言,它们处于NAT A,NAT B的内网,以此类推,客户端可以放到到多层NAT设备后面。客户端A和客户端B发起对服务器S的连接的时候,就会依次在NAT A和NAT B上建立向外的Session,而NAT A、NAT B要联入公网的时候,会在NAT C上再建立向外的Session。

现在假定客户端A和B希望通过UDP“打洞”完成两个客户端的P2P直连。最优化的路由策略是客户端A向客户端B的“伪公网”IP上发送数据包,即ISP服务提供商指定的内网IP,NAT B的“伪”公网地址二元组,{10.0.1.2:55000}。由于从服务器的角度只能观察到真正的公网地址,也就是NAT A,NAT B在NAT C建立session的真正的公网地址{155.99.25.11:62000}以及{155.99.25.11:62005},非常不幸的是客户端A与客户端B是无法通过服务器知道这些

“伪”公网的地址,而且即使客户端A和B通过某种手段可以得到NAT A和NAT B的“伪”公网地址,我们仍然不建议采用上述的“最优化”的打洞方式,这是因为这些地址是由ISP服务提供商提供的或许会存在与客户端本身所在的内网地址重复的可能性(例如:NAT A的内网的IP地址域恰好与NAT A在NAT C的“伪”公网IP地址域重复,这样就会导致打洞数据包无法发出的问题)。

因此客户端别无选择,只能使用由公网服务器观察到的A,B的公网地址二元组进行“打洞”操作,用于“打洞”的数据包将由NAT C进行转发。

当客户端A向客户端B的公网地址二元组{155.99.25.11:62005}发送UDP数据包的时候,NAT A首先把数据包的源地址二元组由A的内网地址二元组{10.0.0.1:4321}转换为“伪”公网地址二元组{10.0.1.1:45000},现在数据包到了NAT C,NAT C应该可以识别出来该数据包是要发往自身转换过的公网地址二元组,如果NAT C可以给出“合理”响应的话,NAT C将把该数据包的源地址二元组改为{155.99.25.11:62000},目的地址二元组改为{10.0.1.2:55000},即NAT B的“伪”公网地址二元组,NAT B最后会将收到的数据包发往客户端B。同样,由B发往A的数据包也会经过类似的过程。目前也有很多NAT设备不支持类似这样的“Hairpin转换”,但是已经有越来越多的NAT设备商开始加入对该转换的支持中来。

4.2.6 UDP在空闲状态下的超时问题

由于UDP转换协议提供的“洞”不是绝对可靠的,多数NAT设备内部都有一个UDP转换的空闲状态计时器,如果在一段时间内没有UDP数据通信,NAT设备会关掉由“打洞”操作打出来的“洞”,作为应用程序来讲如果想要做到与设备无关,就最好在穿越NAT以后设定一个穿越的有效期。

很遗憾目前没有标准有效期,这个有效期与NAT设备内部的配置有关,某些设备上最短的只有20秒左右。在这个有效期内,即使没有P2P数据包需要传输,应用程序为了维持该“洞”可以正常工作,也必须向对方发送“打洞”心跳包。这个心跳包是需要双方应用程序都发送的,只有一方发送不会维持另一方的Session正常工作。除了频繁发送“打洞”心跳包以外,还有一个方法就是在当前的“洞”超时之前,P2P客户端双方重新“打洞”,丢弃原有的“洞”,这也不失为一个有效的方法。

4.3 TCP打洞问题

建立穿越NAT设备的P2P的TCP连接只比UDP复杂一点点,TCP协议的”“打洞”从协议层来看是与UDP的“打洞”过程非常相似的。尽管如此,基于TCP协议的打洞至今为止还没有被很好的理解,这也造成了的对其提供支持的NAT设备不是很多。在NAT设备支持的前提下,基于TCP的“打洞”技术实际上与基于UDP的“打洞”技术一样快捷、可靠。实际上,只要NAT设备支持的话,基于TCP的P2P技术的健壮性将比基于UDP技术的更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。

4.3.1 套接字和TCP端口的重用

实现基于TCP协议的P2P打洞过程中,最主要的问题不是来自于TCP协议,而是来自于应用程序的API接口。这是由于标准的伯克利(Berkeley)套接字的API是围绕着构建客户端/服务器程序而设计的,API允许TCP流套接字通过调用connect()函数来建立向外的连接,或者通过listen()和accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接,又能够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败。

为了让TCP“打洞”能够顺利工作,我们需要使用一个本地的TCP端口来监听来自外部的TCP连接,同时建立多个向外的TCP连接。幸运的是,所有的主流操作系统都能够支持特殊的TCP套接字参数,通常叫做“SO_REUSEADDR”,该参数允许应用程序将多个套接字绑定到本地的一个地址二元组(只要所有要绑定的套接字都设置了SO_REUSEADDR参数即可)。BSD系统引入了SO_REUSEPORT参数,该参数用于区分端口重用还是地址重用,在这样的系统里面,上述所有的参数必须都设置才行。

4.3.2 打开P2P的TCP流

假定客户端A希望建立与B的TCP连接。我们像通常一样假定A和B已经与公网上的已知服务器建立了TCP连接。服务器记录下来每个接入的客户端的公网和内网的地址二元组,如同为UDP服务的时候一样。从协议层来看,TCP“打洞”与UDP“打洞”是几乎完全相同的过程。

  • 客户端A使用其与服务器的连接向服务器发送请求,要求服务器协助其连接客户端B;
  • 服务器将B的公网和内网的TCP地址的二元组信息返回给A,同时,服务器将A的公网和内网的地址二元组也发送给B;
  • 客户端A和B使用连接服务器的端口异步地发起向对方的公网、内网地址二元组的TCP连接,同时监听各自的本地TCP端口是否有外部的连接联入;
  • A和B开始等待向外的连接是否成功,检查是否有新连接联入。如果向外的连接由于某种网络错误而失败,如:“连接被重置”或者“节点无法访问”,客户端只需要延迟一小段时间(例如延迟一秒钟),然后重新发起连接即可,延迟的时间和重复连接的次数可以由应用程序编写者来确定;
  • TCP连接建立起来以后,客户端之间应该开始鉴权操作,确保目前联入的连接就是所希望的连接。如果鉴权失败,客户端将关闭连接,并且继续等待新的连接联入。客户端通常采用“先入为主”的策略,只接受第一个通过鉴权操作的客户端,然后将进入P2P通信过程不再继续等待是否有新的连接联入。

与UDP不同的是,因为使用UDP协议的每个客户端只需要一个套接字即可完成与服务器的通信,而TCP客户端必须处理多个套接字绑定到同一个本地TCP端口的问题,如图7所示。现在来看实际中常见的一种情景,A与B分别位于不同的NAT设备后面,如图5所示,并且假定图中的端口号是TCP协议的端口号,而不是UDP的端口号。图中向外的连接代表A和B向对方的内网地址二元组发起的连接,这些连接或许会失败或者无法连接到对方。如同使用UDP协议进行“打洞”操作遇到的问题一样,TCP的“打洞”操作也会遇到内网的IP与“伪”公网IP重复造成连接失败或者错误连接之类的问题。

客户端向彼此公网地址二元组发起连接的操作,会使得各自的NAT设备打开新的“洞”允许A与B的TCP数据通过。如果NAT设备支持TCP“打洞”操作的话,一个在客户端之间的基于TCP协议的流通道就会自动建立起来。如果A向B发送的第一个SYN包发到了B的NAT设备,而B在此前没有向A发送SYN包,B的NAT设备会丢弃这个包,这会引起A的“连接失败”或“无法连接”问题。而此时,由于A已经向B发送过SYN包,B发往A的SYN包将被看作是由A发往B的包的回应的一部分,所以B发往A的SYN包会顺利地通过A的NAT设备,到达A,从而建立起A与B的P2P连接。

4.3.3 从应用程序的角度来看TCP打洞

从应用程序的角度来看,在进行TCP“打洞”的时候都发生了什么呢?假定A首先向B发出SYN包,该包发往B的公网地址二元组,并且被B的NAT设备丢弃,但是B发往A的公网地址二元组的SYN包则通过A的NAT到达了A,然后,会发生以下的两种结果中的一种,具体是哪一种取决于操作系统对TCP协议的实现:

(1)A的TCP实现会发现收到的SYN包就是其发起连接并希望联入的B的SYN包,通俗一点来说就是“说曹操,曹操到”的意思,本来A要去找B,结果B自己找上门来了。A的TCP协议栈因此会把B作为A向B发起连接connect的一部分,并认为连接已经成功。程序A调用的异步connect()函数将成功返回,A的listen()等待从外部联入的函数将没有任何反映。此时,B联入A的操作在A程序的内部被理解为A联入B连接成功,并且A开始使用这个连接与B开始P2P通信。

由于收到的SYN包中不包含A需要的ACK数据,因此,A的TCP将用SYN-ACK包回应B的公网地址二元组,并且将使用先前A发向B的SYN包一样的序列号。一旦B的TCP收到由A发来的SYN-ACK包,则把自己的ACK包发给A,然后两端建立起TCP连接。简单的说,第一种,就是即使A发往B的SYN包被B的NAT丢弃了,但是由于B发往A的包到达了A。结果是,A认为自己连接成功了,B也认为自己连接成功了,不管是谁成功了,总之连接是已经建立起来了。

(2)另外一种结果是,A的TCP实现没有像(1)中所讲的那么“智能”,它没有发现现在联入的B就是自己希望联入的。就好比在机场接人,明明遇到了自己想要接的人却不认识,误认为是其他的人,安排别人给接走了,后来才知道是自己错过了机会,但是无论如何,人已经接到了任务已经完成了。然后,A通过常规的listen()函数和accept()函数得到与B的连接,而由A发起的向B的公网地址二元组的连接会以失败告终。尽管A向B的连接失败,A仍然得到了B发起的向A的连接,等效于A与B之间已经联通,不管中间过程如何,A与B已经连接起来了,结果是A和B的基于TCP协议的P2P连接已经建立起来了。

第一种结果适用于基于BSD的操作系统对于TCP的实现,而第二种结果更加普遍一些,多数Linux和Windows系统都会按照第二种结果来处理。

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

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

相关文章

算法 之 树形dp 树的中心、重心

文章目录 重心实践题目小红的陡峭值 在树的算法中,求解树的中心和重心是一类十分重要的算法 求解树的重心 树的重心的定义:重心是树中的一个节点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点…

游戏引擎学习第146天

音高变化使得对齐读取变得不可能,我们可以支持循环声音了。 我们今天的目标是完成之前一段时间所做的音频代码。这个项目并不依赖任何引擎或库,而是一个教育项目,目的是展示从头到尾运行一个游戏所需要的全部代码。无论你对什么方面感兴趣&a…

深入理解MySQL主从原理

导读 高鹏(网名八怪),《深入理解MySQL主从原理》系列文的作者。 本系列通过GTID、Event、主库、从库、案例分析,五大块来详细讲解主从原理。 这篇文章重在学习笔记整理! 在学习《深入理解MySQL主从原理》一书时&…

前端数据模拟利器 Mock.js 深度解析

前端数据模拟利器 Mock.js 深度解析 一、Mock.js 核心价值 1.1 为何需要数据模拟 前后端并行开发加速接口文档驱动开发异常场景模拟测试演示环境数据构造 1.2 Mock.js 核心能力 // 典型数据生成示例 Mock.mock(/api/user, {"users|5-10": [{"id|1": 1…

Phi-4-multimodal:图、文、音频统一的多模态大模型架构、训练方法、数据细节

Phi-4-Multimodal 是一种参数高效的多模态模型,通过 LoRA 适配器和模式特定路由器实现文本、视觉和语音/音频的无缝集成。训练过程包括多阶段优化,确保在不同模式和任务上的性能,数据来源多样,覆盖高质量网络和合成数据。它的设计…

前后端数据加密传输【最佳方案】

AES和RSA区别 算法类型安全性密钥长度/输出长度速度应用场景AES对称加密高128位、192位、256位快适用于大规模数据加密,入HTTPS协议的数据传输RSA非对称加密高1024位、2048位、4096位较慢适用于数据安全传输、数字签名和身份验证 综上:兼顾安全性和性能…

Unity--Cubism Live2D模型使用

了解LIVE2D在unity的使用--前提记录 了解各个组件的作用 Live2D Manuals & Tutorials 这些文件都是重要的控制动画参数的 Cubism Editor是编辑Live2D的工具,而导出的数据的类型,需要满足以上的条件 SDK中包含的Cubism的Importer会自动生成一个Pref…

Linux | Vim 鼠标不能右键粘贴、跨系统复制粘贴

注:本文为 “ Vim 中鼠标右键粘贴、跨系统复制粘贴问题解决方案” 相关文章合辑。 未整理去重。 Linux 入门:vim 鼠标不能右键粘贴、跨系统复制粘贴 foryouslgme 发布时间 2016 - 09 - 28 10:24:16 Vim基础 命令模式(command-mode)插入模式(insert-m…

Flink-DataStreamAPI-执行模式

一、概览 DataStream API支持不同的运行时执行模式,我们可以根据用例的要求和作业的特征进行选择。 STREAMING执行模式:被称为“经典”执行模式为,主要用于需要持续增量处理并且预计无限期保持在线的无界作业BATCH执行模式:类似…

解决VScode 连接不上问题

问题 :VScode 连接不上 解决方案: 1、手动杀死VS Code服务器进程,然后重新尝试登录 打开xshell ,远程连接服务器 ,查看vscode的进程 ,然后全部杀掉 [cxqiZwz9fjj2ssnshikw14avaZ ~]$ ps ajx | grep vsc…

C#类型转换基本概念

一、基本定义‌ C# 类型转换是将数据从一种类型转换为另一种类型的过程,分为 ‌隐式转换‌ 和 ‌显式转换‌ 两类‌。 强类型语言特性‌:C# 要求变量类型在编译时确定,类型转换需满足兼容性或显式规则‌。目的‌:处理不同数据类…

使用阿里云操作系统控制台排查内存溢出

引言 操作系统控制台是阿里云最新推出的一款智能运维工具,专为提升运维效率、优化服务器管理而设计。它集成了多种运维管理功能,包括操作系统助手、插件管理器以及其他实用工具,为用户提供一站式的运维解决方案。无论是个人开发者还是企业运…

(C/S)架构、(B/S)架构

客户机/服务器(C/S)架构 理论描述: 客户机/服务器架构是一种网络架构风格,其中任务被分配给网络中的不同计算机,以提高效率和灵活性。这种架构由两部分组成:客户端(Client)和服务器&…

混合存储HDD+SSD机型磁盘阵列,配上SSD缓存功能,性能提升300%

企业日常运行各种文件无处不在,文档、报告、视频、应用数据......面对成千上万的文件,团队之间需要做到无障碍协作,员工能够即时快速访问、共享处理文件。随着业务增长,数字化办公不仅需要大容量,快速高效的文件访问越…

C 语言异常处理方式全面解析

引言​ 在 C 语言编程领域,稳健的错误处理机制对于保障程序的可靠性、稳定性以及安全性至关重要。异常处理作为错误处理的进阶形式,虽然并非 C 语言标准库原生支持的特性,但通过巧妙运用语言特性和编程技巧,开发者能够实现有效的…

【每日学点HarmonyOS Next知识】状态栏控制、片段按钮点击回调、绘制组件、取消按钮与输入框对齐、父调子组件方法

1、HarmonyOS 状态栏怎么控制显示于隐藏,设置状态栏颜色,子颜色等控制? 显示与隐藏 可以设置沉浸式,隐藏的话可以退出沉静式,在子窗口打开的页面 aboutToAppear 方法中设置沉浸式 aboutToAppear(): void {// 设置沉浸…

二级Python通关秘籍:字符串操作符/函数/方法全解析与实战演练

第一章 字符串基础概念与运算符速通 1.1 字符串的不可变性特性 在Python中,字符串被设计为immutable类型,任何修改操作都会生成新对象。这一特性直接影响字符串拼接的性能表现,建议使用join()方法代替多次操作。 1.2 基础操作符全掌握 pyt…

GStreamer —— 2.6、Windows下Qt加载GStreamer库后运行 - “教程6:媒体格式和Pad功能“(附:完整源码)

运行效果 简介 上一个教程演示了GUI 工具包集成(gtk)。本教程介绍媒体格式和Pad功能。Pad Capabilities 是 GStreamer 的一个基本元素,尽管大多数它们不可见,因为框架会处理它们 自然而然。这个有点理论性的教程展示了: • 什么是 Pad 功能。…

【前缀和与差分 C/C++】洛谷 P8218 求区间和

2025 - 03 - 09 - 第 72 篇 Author: 郑龙浩 / 仟濹 【前缀和与差分 C/C】 文章目录 洛谷 P8218 求区间和题目描述输入格式输出格式输入输出样例 #1输入 #1输出 #1 说明/提示思路代码 洛谷 P8218 求区间和 题目描述 给定 n n n 个正整数组成的数列 a 1 , a 2 , ⋯ , a n a_…

初识Bert

在学习Bert之前我们先了解“递归神经网络(RNN Recurrent neural network)” 和 “长短期记忆(LSTM Long short-term memory)” 我们如果仅仅识别每个字的含义,那么在一句话中没有相同的字还是可以的但是如果一句话中有相同的字,那…