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

SIP系列七:ICE框架(P2P通话)

        我的音视频/流媒体开源项目(github)

        SIP系列目录

目录

一、NAT

1、NAT介绍

2、NAT类型

2.1、 完全圆锥型NAT

2.2、受限圆锥型NAT

2.3、端口受限圆锥型NAT

2.4、对称NAT

3、NAT打洞

3.1、不同一NAT下

3.2、同一NAT下          

二、ICE

三、ICE中的SDP


        至此,SIP的基础内容都介绍完了,SIP建立连接之后两个SIP用户就可以进行媒体通话了,但是媒体通话是点对点(P2P)的,不经过SIP代理服务器,但是实际上用户的设备(PC,手机等)都没有自己的公网地址,所以两端是不可能直接通信的,除非两个用户都在同一个局域网内部。因此SIP建立通话的过程中要获取对端的公网地址和端口才能进行P2P媒体通话,这里涉及到两个重要的概念:NAT、ICE,下面就对这两个概念进行介绍。

一、NAT

        参考:WebRTC源码研究(25)NAT打洞原理_webrtc 打洞-CSDN博客

1、NAT介绍

        NAT(网络地址转换Network Address Translation),在计算机网络中是一种在IP数据包通过路由器或防火墙时重写来源IP地址或目的IP地址的技术,这种技术被普遍使用在有多台主机但只通过一个公有IP地址访问互联网的私有网络中。NAT解决了IPV4地址短缺和网络安全的问题,但也让主机之间的通信变得复杂,导致了通信效率的降低,因为大家都藏在NAT之后了,无法直接通信。

        以传统的邮件作为例子,给大家说明NAT是什么?比如A和B两个人要发信,那B告诉A它在某个楼的某层时,这个时候A 可以给B发消息或者信件吗?这肯定不行,因为它并不知道一个具体的地址是多少?你必须告诉它具体哪个省哪个市哪个区哪个小区哪号楼哪层时,只有这种公共的地址,也就是大家都认识的地址,邮局才能帮你把这封信送达。你说哪号楼哪层这个只有你小区内的人才知道。那这个就和我们的网络是相关的。对于网络上的主机,你必须要有个公网的地址,那相互之间才能进行通讯,如果告诉它一个私网(内网)的地址,那它根本找不到你。那对于我们现实中大部分主机都是在网关之后的,他们之间都是有自己的内网IP地址,并不知道自己的外网是多少。那怎么办呢?实际是有一个映射,在网关上有个NAT功能,它可以使你的内网地址变成外网地址。所以他就是一个资源组,映射之后就将你的内网IP端口映射成外网IP端口。那有了外网的IP端口之后,其他的主机就可以通过内网的IP地址与你通讯了。这就是NAT。

        如下图所示,表示了NAT的作用:

        左边的分别是内网的几台机子,通过内网的IP他们之间是可以相互通信的,但是与互联网之间是不通的,如何访问互联网的资源呢,就必须通过NAT,将我们的内网地址转换成外网地址。

        RFC1918规定了三个保留地址段落:10.0.0.0-10.255.255.255;172.16.0.0-172.31.255.255;192.168.0.0-192.168.255.255。这三个范围分别处于A,B,C类的地址段,不向特定的用户分配,被IANA作为私有地址保留。这些地址可以在任何组织或企业内部使用,和其他Internet地址的区别就是,仅能在内部使用,不能作为全球路由地址。这就是说,出了组织的管理范围这些地址就不再有意义,无论是作为源地址,还是目的地址。对于一个封闭的组织,如果其网络不连接到Internet,就可以使用这些地址而不用向IANA提出申请,而在内部的路由管理和报文传递方式与其他网络没有差异。

        对于有Internet访问需求而内部又使用私有地址的网络,就要在组织的出口位置部署NAT网关,在报文离开私网进入Internet时,将源IP替换为公网地址,通常是出口设备的接口地址。一个对外的访问请求在到达目标以后,表现为由本组织出口设备发起,因此被请求的服务端可将响应由Internet发回出口网关。出口网关再将目的地址替换为私网的源主机地址,发回内部。这样一次由私网主机向公网服务端的请求和响应就在通信两端均无感知的情况下完成了。依据这种模型,数量庞大的内网主机就不再需要公有IP地址了。

        NAT处理报文有几个关键特点:

        1)网络被分为私网和公网两个部分,NAT网关设置在私网到公网的路由出口位置,双向流量必须都要经过NAT网关;
        2)网络访问只能先由私网侧发起,公网无法主动访问私网主机;
        3)NAT网关在两个访问方向上完成两次地址的转换或翻译,出方向做源信息替换,入方向做目的信息替换;
        4)NAT网关的存在对通信双方是保持透明的;
        5)NAT网关为了实现双向翻译的功能,需要维护一张关联表,把会话的信息保存下来。

        NAT网络地址转换有两种方式:基本网络地址转换(Basic NAT)、网络地址端口转换(NAPT)。

        基本网络地址转换(Basic NAT):

        这一种也可称作NAT或“静态NAT”,在RFC 2663中提供了信息。它在技术上比较简单,仅支持地址转换,不支持端口映射。Basic NAT要求对每一个当前连接都要对应一个公网IP地址,因此要维护一个公网的地址池。宽带(broadband)路由器通常使用这种方式来允许一台指定的设备去管理所有的外部链接,甚至当路由器本身只有一个可用外部IP时也如此,这台路由器有时也被标记为DMZ主机。由于改变了IP源地址,在重新封装数据包时候必须重新计算校验和,网络层以上的只要涉及到IP地址的头部校验和都要重新计算。

        网络地址端口转换(NAPT):

        这种方式支持端口的映射,并允许多台主机共享一个公网IP地址。支持端口转换的NAT又可以分为两类:源地址转换和目的地址转换。前一种情形下发起连接的计算机的IP地址将会被重写,使得内网主机发出的数据包能够到达外网主机。后一种情况下被连接计算机的IP地址将被重写,使得外网主机发出的数据包能够到达内网主机。实际上,以上两种方式通常会一起被使用以支持双向通信。

        NAPT维护一个带有IP以及端口号的NAT表,结构如下:

        基本网络地址转换(Basic NAT)已不常用,我们通常提到的NAT就是NAPT。

        NAT 的副作用主要有下面几点:

  • 对等网络传输需穿透NAT:IP协议的定义中,在理论上,具有IP地址的每个站点在协议层面有相当的获取服务和提供服务的能力,不同的IP地址之间没有差异。但NAT工作原理破坏了这个特征,如需实现真正意义上的对等网络传输,则需要穿透NAT。这是本文重点。
  • 应用层需保持UDP会话连接:由于NAT资源有限,会根据一定规则回收转换出去的资源(即ip/port组合),UDP通信又是无连接的,所以基于UDP的应用层协议在无数据传输、但需要保持连接时需要发包以保持会话不过期,就是通常的heartbeat之类的。
  • 基于IP的访问限制策略复杂化。

        国内移动无线网络运营商在链路上一段时间内没有数据通讯后, 会淘汰NAT表中的对应项, 造成链路中断。

  • NAT带来的第一个副作用:NAT超时:

        国内的运营商一般NAT超时的时间为5分钟,所以通常我们TCP长连接的心跳设置的时间间隔为3-5分钟。

  • NAT带来的第二个副作用就是:NAT墙。

        NAT会有一个机制,所有外界对内网的请求,到达NAT的时候,都会被NAT所丢弃,这样如果我们处于一个NAT设备后面,我们将无法得到任何外界的数据(除非我们主动连接外部设备)。

        但是这种机制有一个解决方案:就是如果我们A主动往B发送一条信息,这样A就在自己的NAT上打了一个B的洞。这样A的这条消息到达B的NAT的时候,虽然被丢掉了,但是如果B这个时候在给A发信息,到达A的NAT的时候,就可以从A之前打的那个洞中,发送给到A手上了。

        简单来讲,就是如果A和B要进行通信,那么得事先A发一条信息给B,B发一条信息给A。这样提前在各自的NAT上打了对方的洞,这样下一次A和B之间就可以进行通信了。

2、NAT类型

        如上图所示,基础型基本不用了,常用的NAPT有图示的四种类型。

2.1、 完全圆锥型NAT

        完全圆锥型NAT(Full cone NAT),即一对一(one-to-one)NAT。

        一旦一个内部地址(iAddr:port)映射到外部地址(eAddr:port),所有发自iAddr:port的包都经由eAddr:port向外发送。任意外部主机都能通过给eAddr:port发包到达iAddr:port(注:port不需要一样),是最好穿越的一种 NAT类型。但是安全性就差很多。

2.2、受限圆锥型NAT

        内部客户端必须首先发送数据包到对方(IP=X.X.X.X),然后才能接收来自X.X.X.X的数据包。在限制方面,唯一的要求是数据包是来自X.X.X.X。
        内部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有发自iAddr:port1的包都经由eAddr:port2向外发送。外部主机(hostAddr:any)能通过给eAddr:port2发包到达iAddr:port1。(注:any指外部主机源端口不受限制,但是目的端口必须是port2。只有外部主机数据包的目的IP 为内部客户端的所映射的外部ip,且目的端口为port2时数据包才被放行。)

        客户端向P发送了数据那么P机器上的所有端口都可以向客户端发送数据,但是别的机器是不可以的,也就是只要IP对就行,端口不管,入下图所示:

2.3、端口受限圆锥型NAT

        类似受限制锥形NAT(Restricted cone NAT),但是还有端口限制。

        需要满足两个条件:客户端主要向外界主机发送消息、外界主机返回的消息的IP和PORT必须和客户端发送的数据包中的相同。如下图所示:

2.4、对称NAT

        前几个类型的NAT,客户端向不同的主机发送消息,NAT都只有一组ip:port映射,在对称NAT中,客户端向不同的主机发送消息,会有不同的ip:port映射。而且外部主机返回消息的ip:port不能变。

        对称NAT特点:和不同主机通信都会有不同的映射,而且ip和端口都要限制。

        对称NAT是最难穿越的,如果两个通信的主机都在对称NAT之后,是不可能穿越的。

3、NAT打洞

        最常用的打洞技术使用的是STUN/TURN协议,是一种UDP的穿透规范。

        首先看一下UDP打洞技术。参考:P2P通信原理与实现 - 有价值炮灰

3.1、不同一NAT下

        假设客户端A和客户端B的地址都是内网地址,且在不同的NAT后面。A、B上运行的P2P应用程序和服务器S都使用了UDP端口1234,A和B分别初始化了 与Server的UDP通信,地址映射如图所示:

        现在假设客户端A打算与客户端B直接建立一个UDP通信会话, 如果A直接给B的公网地址138.76.29.7:31000发送UDP数据,NAT B将很可能会无视进入的 数据(除非是Full Cone NAT),因为源地址和端口与S不匹配,而最初只与S建立过会话.。B往A直接发信息也类似。

        假设A开始给B的公网地址发送UDP数据的同时,给服务器S发送一个中继请求,要求B开始给A的公网地址发送UDP信息。A往B的输出信息会导致NAT A打开 一个A的内网地址与与B的外网地址之间的新通讯会话(此时B向A发送的消息将不再被丢弃),B往A亦然。 一旦新的UDP会话在两个方向都打开之后,客户端A和客户端B就能直接通讯,,而无须再通过引导服务器S了。

3.2、同一NAT下          

        如果A和B在同一个NAT下经过上面的打洞过程,会有如下图所示的结果:

        首先A和B会得到由S观测到的对方的公网IP和端口号,然后给对方的地址发送信息。两个客户端只有在NAT允许内网主机对内网其他主机发起UDP会话的时候才能正常通信,我们把这种情况称之为"回环传输“(loopback transmission),因为从内部 到达NAT的数据会被“回送”到内网中而不是转发到外网。例如:当A发送一个UDP数据包给B的公网地址时,数据包最初有源IP地址和端口地址10.0.0.1:1234和 目的地址155.99.25.11:62001,NAT收到包后,将其转换为源155.99.25.11:62000(A的公网地址)和目的10.1.1.3:1234,然后再转发给B。即便NAT支持 回环传输,这种转换和转发在此情况下也是没必要的,且有可能会增加A与B的对话延时和加重NAT的负担。

        对于这个情况,优化方案是很直观的。 当A和B最初通过S交换地址信息时,他们应该包含自身的IP地址和端口号(从自己看),同时也包含从服务器看的自己的 地址和端口号。 然后客户端同时开始从对方已知的两个的地址中同时开始互相发送数据,并使用第一个成功通信的地址作为对方地址。如果两个客户端在同一个 NAT后,发送到对方内网地址的数据最有可能先到达。从而可以建立一条不经过NAT的通信链路;如果两个客户端在不同的NAT之后,发送给对方内网地址的数据包 根本就到达不了对方,但仍然可以通过公网地址来建立通路。

        总结就是,用户A给会用户B两组地址(A内网地址、通过S获取A的外网地址),同理B也会给A两组地址(B内网地址、通过S获取B的外网地址),用户A和用户B会互相发送消息,看哪个地址可以和对方进行通信(最终结果是通信最快的那对地址)。这也是ICE的工作原理,ICE将在后面介绍。

        UDP打洞技术有一个主要的条件:只有当两个NAT都是Cone NAT(或者非NAT的防火墙)时才能工作。 因为其维持了一个给定的(内网IP、内网UDP)二元组 和(公网IP、公网UDP)二元组固定的端口绑定,只要该UDP端口还在使用中,就不会变化。如果像对称NAT一样,给每个新会话分配一个新的公网端口。就会导致UDP应用程序无法使用跟外部端点已经打通了的通信链路。由于Cone NAT是当今最广泛使用的,尽管有一小部分的对称NAT是不支持打洞的,UDP打洞技术也还是被广泛采纳应用。

        如果打洞失败,那用户A和用户B只能通过S转发消息,即中继。

        STUN/TURN协议就是对上述的打洞流程进行了规范化、流程化,其中S就是STUN/TURN服务器,STUN是打洞协议,TURN是中继协议,TURN也可以是单独的一个服务。如下表所示,为不同类型NAT打洞成功的条件:

C1的NAT类型C2的NAT类型能否穿越防火墙
完全锥型完全锥型
完全锥型受限锥型
完全锥型端口受限锥型
完全锥型对称型
受限锥型受限锥型
受限锥型端口受限锥型
受限锥型对称型
端口受限锥型端口受限锥型
端口受限锥型对称型无法打通
对称型对称型无法打通

        目前主要有下面两种方式去尝试穿越对称NAT:

        同时开放TCP(SimultaneousTCP open)策略:
        如果 Client A和 Client B能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个面向对方的“外出”的TCP连接请求,并在对方的 SYN 包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就能成功地建立了。

        这种策略存在的问题: 时钟严格一致,很难做到。

        UDP端口猜测策略:
        通常,对称NAT分配端口有两种策略,一种是按顺序增加,一种是随机分配。如果这里对称NAT使用顺序增加策略,那么,ClientB将两次被分配的IP、端口二元组发送给Server后,Server就可以通知ClientA在这个端口范围内猜测刚才ClientB发送给它的socket中被NAT映射后的IP、端口二元组,ClientA很有可能在孔有效期内成功猜测到端口号,从而和ClientB成功通信。

        这种策略存在的问题:不能为随机分配端口的对称型NAT打洞。

二、ICE

        ICE(Interactive Connectivity Establishment)是一种用于 NAT(网络地址转换)穿越的框架,通常用于实时通信应用中,帮助两端设备或应用在 NAT 后建立连接。ICE 主要用于实现端到端的通信连接,尤其是在 P2P(Peer-to-Peer)协议中,通常与 STUN(Session Traversal Utilities for NAT)和 TURN(Traversal Using Relays around NAT)一起使用。

        ICE参考:交互式连接建立(ICE) - Rose - SDL中文论坛 - Powered by Discuz!

        具体介绍ICE框架之前先了解几个概念:

        Offer/Answer模型:两端的地址交换是通过交换SDP实现的,主叫端把自己的SDP发送给被叫端的过程叫Offer,被叫端把自己的SDP发送给主叫端的过程叫Answer。Offer/Answer就是SDP交换过程,对应SIP中 INVITE请求(带有SDP)就是Offer,对端回复200 OK(带有SDP)就是Answer。

        candidate:候选地址,由IP、端口及协议(TCP、UDP,一般为UDP),简单来说candidate就是通信地址。candidate有四种类型:

类型别名如何传给对端用法
主机候选host信令服务器从网卡中获取的本地传输地址,如果此地址位于NAT之后,则为内网地址
服务器反射候选srflx信令服务器从发送给Stun服务器的Binding检查中获取的传输地址。如果此地址位于NAT之后,则为最外层NAT的公网地址
对端反射候选prflxStun Binding请求从对端发送的Stun Binding请求获取的传输地址。这是一种在连接检查期间新发生的候选项
中继候选relay信令服务器媒体中继服务器的传输地址。通过使用TURN Allocate请求获取

        地址对:就是一对ip:port,例如<ip1:port>-<ip2-port2>,表示p2p两端用户的地址。

        接下来看一下ICE的交互框架,如下图所示:

        交互式连接建立是一种标准穿透协议,利用Stun和Turn服务器来帮助端点建立连接,收集到的候选地址通过SDP在两端交换。

         ICE 的工作流程
        候选地址收集:在通信开始时,每个端点(客户端)都会生成自己的候选地址。候选地址包括主机地址、NAT的映射地址(通过 STUN 服务器获取)、以及 中继地址(通过 TURN 服务器获取,用于无法P2P的时候进行媒体数据转发)。
        候选地址交换:通过信令协议(如 SIP、WebRTC 等),每个端点将其候选地址发送给对方,确保双方都能得到彼此的候选地址。
        连接性检查:双方使用 STUN 协议对所有候选地址进行连接性检查,以确定最适合的连接路径。
        选择最佳路径:ICE 会根据优先级选择最佳的连接候选,并最终建立P2P连接。

        如果加上信令服务器(SIP、WebRTC等,信令服务器实际就是转发信令消息的代理服务器),框架入下图所示:

       

        A、B收集并交换完地址之后,A和B都拿到如下地址:

别名类型
A$Cand1host192.168.1.105
A$Cand2srflx211.161.240.181(raddr: 192.168.1.105)
B$Cand1host192.168.0.204
B$Cand2srfix11.92.14.8(raddr: 192.168.0.204)

        接下来A、B的ICE代理都会产生如下候选地址对:

本地网卡地址对端地址状态
192.168.1.105:60001192.168.0.204:40001未进行过Stun检查
211.161.240.181:60003192.168.0.204:40001未进行过Stun检查
192.168.1.105:6000111.92.14.8:50002未进行过Stun检查
211.161.240.181:6000311.92.14.8:50002未进行过Stun检查

        表中有一条、或多条、或没有,能够把A的视频Rtp数据发向B的视频Rtp通道,到底怎么个可能性就要执行接下的Stun检查。A、B的ICE代理会设置上面地址对的优先级,ICE规定了优先级计算方法,A、B计算出来的优先级是一样的。 

        关于中继候选,中继服务器会给A、B都分配一个地址ip-a:port-a、ip-b:port-b。B向ip-a:port-a发送的数据会被转发到A,用户A发送ip-b:port-b的数据也会转发到B,如下图所示(B是公网环境,TURN Client就在ICE代理内包含的):

        STUN检查也就是地址对的连通性检测,Stun检查具体操作是向对端上发Stun Binding请求。由于要能支持STUN应答,每个ICE代理必须内置Stun服务器功能。联通性检测就是STUN的四次握手过程,如下图所示:

        注意:

        上面的过程可能产生(注意是可能,不是一定,要看NAT是什么类型的)一组新的映射地址(端口不一样),类型为prflx,这个地址将会被添加到A、B的候选地址对中参与连通性检测。

        还有一种可能,假如A和Stun服务器之间连接状态不好,在它收到B发来的srflx(11.92.14.8)之后还没得出自己的srflx(211.161.240.181)。虽然A没得到自己的srflx,但这不妨碍对B的srflx这个候选地址进行Stun检查,于是会向11.92.14.8发Stun请求。B收到这个请求,从请求解析出211.161.240.181。虽然这个地址在值上等于A的srflx,但不是从信令服务器得到,而是来自对端的Stun请求。此时B就会以这个prflx新建Connection(地址对)。A在之后终于向Stun服务器拿到了自己的srflx,并通过信令服务器发向B。B发现这个srflx地址对已存在,就不会再创建了。

        经过连通信检测过后A、B会选出一个合适的地址对,自此打洞完成,A、B开始P2P音视频通话。

        ICE交互过程还包括:A、B角色选择(谁负责发起STUN的四次握手请求)、地址对排序(优先级计算公式)、冻结候选、连通性检测的调度、认证机制、保活等。这些属于ICE的实现细节,这里不展开描述,不过好在有ICE开源库都实现了这些功能,我们直接用就是了。总计一下:ICE = STUN + TURN + 协商机制 +协商路径。

三、ICE中的SDP

        SDP中包含了用户的候选地址。示例如下:

v=0
o=- 4610227990434322888 2 IN IP4 192.168.1.100
s=-
c=IN IP4 192.168.1.100
t=0 0
m=audio 5004 RTP/AVP 0 8 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
m=video 5006 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=42e01f;packetization-mode=1a=ice-ufrag:username
a=ice-pwd:password
a=ice-options:tricklea=candidate:0 1 UDP 2122252543 192.168.1.100 5004 typ host
a=candidate:1 1 UDP 1686052607 10.0.0.1 5004 typ srflx raddr 192.168.1.100 rport 5004
a=candidate:2 1 UDP 3355443200 203.0.113.5 5004 typ relay raddr 192.168.1.100 rport 5004

        a=ice-ufrag:username-->ice-ufrag 是 ICE 用户标识符的一部分。它用于与 ice-pwd 共同构成身份验证机制。

        a=ice-pwd:password-->ice-pwd 是 ICE 密码的一部分。它与 ice-ufrag 一起用于验证连接的有效性和安全性。

        a=ice-options:trickle-->ice-options 表示支持的 ICE 选项。trickle 选项表示支持“逐步”候选地址的传输,即当候选地址被发现时,可以逐步发送它们,而不需要等待所有地址都发现后再发送。

         a=candidate:格式:

a=candidate:<foundation> <component> <transport> <priority> <connection-address> <port> typ <candidate-type> [raddr <rel-addr> rport <rel-port>] [generation <gen>] [network-id <id>] [network-cost <cost>]

        字段解释:
        foundation:每个候选地址的唯一标识符,用于标识候选地址的基础。通常是候选的一个哈希值。
        component:媒体流的组件类型,通常为 1(表示音频)或 2(表示视频)。
        transport:传输协议。常见的值有 UDP 和 TCP,这指定了候选地址所使用的协议。
        priority:候选地址的优先级,值越大优先级越高。优先级的计算基于 RFC 5245 中定义的算法,通常考虑了连接的可靠性和速度等因素。
        connection-address:候选地址的 IP 地址。可以是主机的私有地址、公共地址,或者 STUN/TURN 服务器返回的地址。
        port:与候选地址关联的端口号。
        typ:候选地址的类型。常见的类型有:
                host:本地地址。
                srflx(server reflexive):通过 STUN 服务器映射得到的地址。
                relay:通过 TURN 服务器中继得到的地址。
        raddr:(可选)在使用 srflx 或 relay 类型时,表示用于映射的远程地址(STUN/TURN 服务器的地址)。
        rport:(可选)在使用 srflx 或 relay 类型时,表示用于映射的远程端口。
        generation:(可选)候选的生成编号,用于多次候选的情况。通常为 1 或 2。
        network-id:(可选)网络标识符,用于区分不同网络接口上的候选地址。
        network-cost:(可选)网络代价,用于衡量候选地址的质量,数值越小代表代价越低,通常是基于带宽或延迟的评估。

        关于STUN/TURN协议不过多介绍,这里只是介绍整体框架,对于基于SIP的VOIP通话,PJSIP已经实现了ICE框架,可直接使用。

相关文章:

SIP系列七:ICE框架(P2P通话)

我的音视频/流媒体开源项目(github) SIP系列目录 目录 一、NAT 1、NAT介绍 2、NAT类型 2.1、 完全圆锥型NAT 2.2、受限圆锥型NAT 2.3、端口受限圆锥型NAT 2.4、对称NAT 3、NAT打洞 3.1、不同一NAT下 3.2、同一NAT下 二、ICE 三、ICE中的SDP 至此&#x…...

小程序-基于java+SpringBoot+Vue的智慧校园管理系统设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…...

Visual Studio 2022创建离线安装包

步骤1&#xff1a; 下载 Visual Studio 引导程序(最新版) 历史版本 步骤2 新建文件夹“E:\VS2022”&#xff0c;将下载的“vs_Professional.exe”拷贝到文件夹下在此文件夹窗口按住shift鼠标右键&#xff0c;选择“在此处打开powershell窗口” 步骤3 根据需要将代码复制到…...

Android 实现中英文切换

在开发海外项目的时候&#xff0c;需要实现app内部的中英文切换功能&#xff0c;所有的英文都是内置的&#xff0c;整体思路为&#xff1a; 创建一个sp对象&#xff0c;存储当前系统的语言类型&#xff0c;然后在BaseActivity中对语言进行判断&#xff1b; //公共Activitypubl…...

CmakeLists学习刨根问底

必要的两项内容 cmake_minimum_required(VERSION 2.5)project(mymuduo) 这行代码指定了构建项目所需的CMake最低版本为2.5。CMake是一个跨平台的自动化构建系统&#xff0c;它使用CMakeLists.txt文件来定义项目的构建过程。定义项目的名称为mymuduo。CMake将使用这个名称来生成…...

策略模式实战 - 猜拳游戏

**可以整体的替换一套算法&#xff0c;这就是策略模式。**这样对于同一个问题&#xff0c;可以有多种解决方案——算法实现的时候&#xff0c;可以通过策略模式来非常方便的进行算法的整体替换&#xff0c;而各种算法是独立封装好的&#xff0c;不用修改其内部逻辑。 具体的实…...

VoCo-LLaMA: Towards Vision Compression with Large Language Models

视觉语言模型在各种多模态任务上取得了显著的成功&#xff0c;但经常受到有限上下文窗口和处理高分辨率图像输入和视频的高计算成本的瓶颈。视觉压缩可以通过减少视觉令牌数量避免该问题。先前方法使用额外模块压缩视觉令牌并强制LLM理解压缩的令牌。然而&#xff0c;LLM对视觉…...

每日小知识

Kafka是一个分布式流平台&#xff0c;具有高性能、高可靠性和可扩展性的特点。它主要用于处理实时的数据流&#xff0c;将数据以高吞吐量的方式进行发布和订阅。以下是关于Kafka的几个基本概念和优势的介绍&#xff1a; 概念&#xff1a; 生产者&#xff08;Producer&#xf…...

Linux其二设置端口号,静态ip以及命令

目录 1、VI编辑器 【linux版本的文本文件】 2&#xff09; 补充的vi编辑器的其他内容(了解) 2、ln 连接的意思 link的缩写 3、文件的查看 【重点】 4、压缩与解压&#xff08;重点&#xff09; 5、find 查找命令 6、which & whereis 作用是一样的&#xff0c;表示某…...

吉林大学23级数据结构上机实验(第7周)

A 去火车站 寒假到了&#xff0c;小明准备坐火车回老家&#xff0c;现在他从学校出发去火车站&#xff0c;CC市去火车站有两种方式&#xff1a;轻轨和公交车。小明为了省钱&#xff0c;准备主要以乘坐公交为主。CC市还有一项优惠政策&#xff0c;持学生证可以免费乘坐一站轻轨&…...

SpringBoot(整合MyBatis + MyBatis-Plus + MyBatisX插件使用)

文章目录 1.整合MyBatis 1.需求分析2.数据库表设计3.数据库环境配置 1.新建maven项目2.pom.xml 引入依赖3.application.yml 配置数据源4.Application.java 编写启动类5.测试6.配置类切换druid数据源7.测试数据源是否成功切换 4.Mybatis基础配置 1.编写映射表的bean2.MonsterMap…...

【2024最新】基于Springboot+Vue的网上图书商城平台Lw+PPT

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…...

JAVA-面向对象基础

文章目录 概要封装多态抽象类接口内部类为什么需要内部类 概要 面向对象是一种编程范式或设计哲学&#xff0c;它将软件系统设计为由多个对象组成&#xff0c;这些对象通过特定的方式相互作用 封装 将数据和操作数据的方法封装在一个类中&#xff0c;并通过访问修饰符控制对…...

Y3编辑器官方文档1:编辑器简介及菜单栏详解(文件、编辑、窗口、细节、调试)

文章目录 一、新建项目二、 编辑器主界面2.1 游戏场景2.2 导航栏/菜单栏2.3 功能栏三、菜单栏详细介绍3.1 文件3.1.1 版本管理3.1.2 项目管理(多关卡)3.1.2.1 多关卡功能说明3.1.2.2 关卡切换与关卡存档3.2 编辑3.2.1 通用设置3.2.2 键位设置3.3 窗口(日志)3.4 细节3.4.1 语言…...

力扣94题:二叉树的中序遍历

力扣94题&#xff1a;二叉树的中序遍历&#xff08;C语言实现详解&#xff09; 题目描述 给定一个二叉树的根节点 root &#xff0c;返回它的中序遍历&#xff08;Inorder Traversal&#xff09;。 中序遍历的规则是&#xff1a; 先访问左子树&#xff1b;再访问根节点&…...

【数据结构】二叉树的性质和存储结构

性质 在二叉树的第i层上至多有2^{i-1}个结点,至少有1个结点 深度为k的二叉树至多有2^{k-1}个结点&#xff08;k≥1&#xff09;&#xff0c;至少有k个结点 对任何一棵二叉树T&#xff0c;如果其叶子数为n0&#xff0c;度为2的结点数为n2&#xff0c;则n0n21 具有n个结点的完…...

【Spring项目】图书管理系统

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;项目实现准备 1&#xff1a;需求 &#xff08;1&#xff09;登录 2&#xff1a;准备…...

TCP编程案例

笔记&#xff1a;&#xff08;本题可能需要的&#xff09; TCP协议&#xff1a; TCP协议进行通信的两个应用进程&#xff1a;客户端、服务端。 使用TCP协议前&#xff0c;须先建立TCP连接&#xff0c;形成基于字节流的传输数据通道 传输前&#xff0c;采用“三次握手”方式…...

PyTorch 实现动态输入

使用 PyTorch 实现动态输入&#xff1a;支持训练和推理输入维度不一致的 CNN 和 LSTM/GRU 模型 在深度学习中&#xff0c;处理不同大小的输入数据是一个常见的挑战。许多实际应用需要模型能够灵活地处理可变长度的输入。本文将介绍如何使用 PyTorch 实现支持动态输入的 CNN 和…...

网络数据库

创建删除修改模式create schemadrop schema表create tabledrop tablealter table视图create view drop view索引create indexdrop index alter index 定义模式&#xff1a;create schema 《模式名》 authorization 《用户名》表定义语句 视图定义语句 授权定义语句//未指…...

【软考速通笔记】系统架构设计师⑲——专业英语

文章目录 一、前言二、常用名词三、架构风格四、非功能需求五、应用架构六、软件架构重用 一、前言 笔记目录大纲请查阅&#xff1a;【软考速通笔记】系统架构设计师——导读 二、常用名词 名词翻译architecture架构system系统design设计requirements需求components组件constr…...

Android APP自学笔记

摘抄于大学期间记录在QQ空间的一篇自学笔记&#xff0c;当前清理空间&#xff0c;本来想直接删除掉的&#xff0c;但是感觉有些舍不得&#xff0c;因此先搬移过来。 Android导入已有外部数据库 2015.06.26在QQ空间记录&#xff1a;在Android中不能直接打开res aw目录中的数据…...

Spring 事务和事务传播机制

Spring 事务和事务传播机制 一、Spring 事务的基本概念 事务是一组操作&#xff0c;被视为一个不可分割的工作单元&#xff0c;要么全部完成&#xff0c;要么全部失败回滚&#xff0c;以此来确保数据的一致性和完整性。Spring事务管理允许我们在应用程序中声明式地或编程式地…...

VSCode(四)CMake调试

1. 工具准备 1.1 C环境插件 1.2 CMake插件 2. Cmake工程 2.1 创建项目文件夹 ex:CMAKE_TEST 2.2 创建CMake工程 &#xff08;shift ctl P), 选择"CMAKE: Quick Start": 2.3 填写project name: (ex: test_cmake) 2.4 选择”Executable“ 项目文件内会自动…...

创建存储过程

在student数据库中&#xff0c;创建一个名为ST_CHAXUN_01的存储过程&#xff0c;该存储过程返回计算机系学生的“姓名”、“性别”、“出生日期”信息。 操作步骤如下&#xff1a; ① 在“对象资源管理器”窗格中&#xff0c;展开“数据库”结点。 ② 单击相应的数据库&#…...

macOS 15.1.1 (24B2091) 系统中快捷键符号及其代表的按键的对照表

以下是 macOS 15.1.1 (24B2091) 系统中快捷键符号及其代表的按键的对照表&#xff1a; 符号按键名称描述⌘Command (Cmd)常用的功能键&#xff0c;用于执行大多数快捷操作。⌥Option (Alt)Option 键&#xff0c;常用于辅助操作和特殊字符输入。⇧ShiftShift 键&#xff0c;常用…...

ftp服务器搭建-安装、配置及验证

ftp服务器搭建-安装、配置及验证 #安装 sudo apt-get install vsftpd #配置文件 cat > /etc/vsftpd.conf << "EOF" listenNO listen_ipv6YES anonymous_enableNO local_enableYES write_enableYES dirmessage_enableYES use_localtimeYES xferlog_enable…...

windows ping 执行过程分析

控制台 PS C:\Users\Admin> ping mcs.zijieapi.com正在 Ping mcs.zijieapi.com [223.109.117.226] 具有 32 字节的数据: 来自 223.109.117.226 的回复: 字节32 时间31ms TTL49 来自 223.109.117.226 的回复: 字节32 时间30ms TTL49 来自 223.109.117.226 的回复: 字节32 时…...

强大的远程同步工具-rsync

rsync 强大的远程同步工具&#xff0c;也可用于本地目录和本地目录的同步&#xff0c;它最大的特点就是会自动判断进行增量同步&#xff1b; # “演示文档”后加了/&#xff0c;代表传输的是文件&#xff0c;建议destination也指定一个目录&#xff0c;该目录可以不存在&…...

蝉联第一 | IDC云系统软件市场报告2024H1,云轴科技ZStack独立云厂商第一

2024年12月&#xff0c;根据全球知名的IT市场研究和咨询公司IDC最新发布的《中国云系统软件市场跟踪报告2024H1》显示&#xff0c;2024年上半年中国云系统软件市场规模达到27.93亿元人民币&#xff0c;同比增长11.6%。在这一增长趋势中&#xff0c;云轴科技ZStack作为云系统软件…...

【IPV6改造解读】

座右铭&#xff1a;我们每一个心怀希望的人&#xff0c;就像是一颗种子一样&#xff01; 文章目录 前言一、IPV6改造基本情况分析二、IPV6针对不同场景改造方案1.网站/APP场景-IPV6云网关升级改造方案2.局域网专网(互联网侧)场景-安全大脑IPV6改造方案 总结 前言 提示&#xf…...

常用的Kali命令Top10

Kali Linux是一种基于Debian的Linux发行版&#xff0c;被广泛用于渗透测试和网络安全。以下是一些常用的Kali Linux命令的详细解释&#xff1a; ifconfig&#xff1a;显示和配置网络接口的信息。可以用于查看当前系统的IP地址、网关和子网掩码等。 apt-get&#xff1a;用于安装…...

阿里巴巴正式发布了全新大型语言模型Marco-o1

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

【AI日记】24.12.07 kaggle 比赛 Titanic-10

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 内容&#xff1a; 学习 kaggle 入门比赛 Titanic - Machine Learning from Disaster学习机器学习&#xff08;pandas&#xff0c;numpy&#xff0c;sklearn&#xff0c;seaborn&#xff0c;matplotlib&a…...

《QT 示例宝库:探索丰富的编程世界》

《QT 示例宝库&#xff1a;探索丰富的编程世界》 一、QT 基础示例&#xff08;一&#xff09;QRadioButton 示例&#xff08;二&#xff09;拦截关闭事件示例 二、QT 常用代码示例&#xff08;一&#xff09;QObject 相关操作&#xff08;二&#xff09;Qt 基本容器遍历&#x…...

子类有多个父类的情况下Super不支持指定父类来调用方法

1、Super使用方法 super()函数在Python中用于调用父类的方法。它返回一个代理对象&#xff0c;可以通过该对象调用父类的方法。 要使用super()方法&#xff0c;需要在子类的方法中调用super()&#xff0c;并指定子类本身以及方法的名称。这样就可以在子类中调用父类的方法。 …...

鸿蒙UI开发——渐变色效果

1、概 述 ArkTs可以通过颜色渐变接口&#xff0c;设置组件的背景颜色渐变效果&#xff0c;实现在两个或多个指定的颜色之间进行平稳的过渡。 目前提供三种渐变类型&#xff1a;线性渐变、角度渐变、径向渐变。 我们在鸿蒙UI布局实战 —— 个人中心页面开发中&#xff0c;默认…...

java基础概念47-ArrayList、LinkList和迭代器

一、ArrayList集合 1-1、ArrayList的两种添加信息的方式 1-2、ArrayList集合底层逻辑 1、利用空参创建的集合&#xff0c;在底层创建一个默认长度为0的数组 2、添加第一个元素时&#xff0c;底层会创建一个新的长度为10的数组 3、存满时&#xff0c;会扩容1.5倍。 4、如果…...

java编程1.5.4 - 失而复得

前言 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往复以至无穷&a…...

R语言使用“纽约市数据集中的优步皮卡”数据创建不同年度时间范围的可视化

一、项目背景 为了分析纽约市优步&#xff08;https://baike.baidu.com/item/Uber/14900884&#xff09;皮卡在不同年度的使用情况&#xff0c;需要利用R语言进行数据可视化。通过对比不同年度的数据&#xff0c;可以揭示出优步皮卡使用的趋势和变化。 二、数据准备 数据集&a…...

韩企研学团造访图为科技:共探人工智能创新前沿

今日&#xff0c;一支由韩国知名企业研学专家组成的代表团莅临图为科技深圳总部&#xff0c;展开了一场深度技术交流与研讨活动。 此次访问旨在通过实地探访中国领先的科技企业&#xff0c;促进中韩两国在科技创新领域的深入合作与交流。 韩国游学团合影 图为科技作为一家在人…...

Windows设备go环境安装配置

一、下载go安装包 官网链接&#xff1a;All releases - The Go Programming Language (google.cn) 安装过程比较简单&#xff0c;这里不再赘述&#xff0c;可参考这位博主的文章。本文重点在环境配置。golang环境详细安装、配置_golang安装-CSDN博客 二、环境变量配置 1.添…...

THREE.js 入门(一)xyz坐标系

一、坐标系概念 在 three.js 中&#xff0c;相机的默认朝向是沿着 Z 轴的负方向。也就是说&#xff0c;默认情况下&#xff0c;相机会沿着 Z 轴的负方向“看”到场景中的对象&#xff0c;而 X 轴和 Y 轴分别对应水平方向和垂直方向。换句话说&#xff0c;相机的默认位置是 (0,…...

Android仿美团左右联动购物列表

Android仿美团左右联动购物列表 左右联动购物列表&#xff0c;不难。 一、思路&#xff1a; 两个RecycleView 二、效果图&#xff1a; 三、关键代码&#xff1a; public class MainActivity extends AppCompatActivity {private RecyclerView rl_left;private RecyclerVie…...

HTML 标签语义化指南:让网页更易读

HTML 语义化标签是指在 HTML 中使用具有明确含义的标签来标记网页内容的结构和意义。这些标签可以提供更多的语义信息&#xff0c;有助于搜索引擎理解网页内容&#xff0c;并为使用辅助技术的用户提供更好的访问体验。 以下是一些常见的HTML语义化标签及其含义和用途&#xff…...

Java日志记录几种实现方案

在平时使用到一些软件中&#xff0c;比如某宝或者某书&#xff0c;通过记录用户的行为来构建和分析用户的行为数据&#xff0c;同时也能更好优化产品设计和提升用户体验。比如在一个订单系统中&#xff0c;需要确定追踪用户的行为&#xff0c;比如&#xff1a; 登录/登出浏览商…...

【C++算法】32.前缀和_矩阵区域和

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a; 题目链接&#xff1a; 1314. 矩阵区域和 题目描述&#xff1a; 解法 防止有人看不明白题目&#xff0c;先解释一下题目 二维前缀和思想&#xff1a; 使用前缀和矩阵 ret [x1,y1]~[x2,y2] D …...

30天学会Go--第7天 GO语言 Redis 学习与实践

30天学会Go–第7天 GO语言 Redis 学习与实践 文章目录 30天学会Go--第7天 GO语言 Redis 学习与实践前言一、Redis 基础知识1.1 Redis 的核心特性1.2 Redis 常见使用场景 二、安装 Redis2.1 在 Linux 上安装2.2 在 Windows 上安装2.3 使用 Docker 安装 Redis 三、Redis 常用命令…...

面经自测——死锁/死锁的必要条件/死锁的预防/进程通信的方式

前言 本文是作者专门用来自测Java后端相关面试题的&#xff0c;所有问题都是在牛客、知识星球或网上找到的最近最新的面试题&#xff0c;全文回答都是作者按自己的真实水平仿照真实环境的回答&#xff0c;所以答案不一定真实&#xff08;但回答一定真诚&#x1f923;&#xff0…...

Mysql索引,聚簇索引,非聚簇索引,回表查询

什么是索引 数据库索引是为了实现高效数据查询的一种有序的数据数据结构&#xff0c;类似于书的目录&#xff0c;通过目录可以快速的定位到想要的数据&#xff0c;因为一张表中的数据会有很多&#xff0c;如果直接去表中检索数据效率会很低&#xff0c;所以需要为表中的数据建立…...