UnrealEngine - 网络同步之连接篇
1 连接过程 - 握手
传统的 C/S 架构下,Client 和 Server 通常会建立一条抽象的 Connection,用来进行两端的通信。
UE 的官方文档中提供了 Client 连接到 Server 的示例 ,简单来说分为如下几步:
- 打包构建好 Client 和 Server 进程
- 启动 Server 进程,启动参数为
./Binaries/Win64/<PROJECT_NAME>Server.exe -log - 启动 Client 进程,启动参数为
./Binaries/Win64/<PROJECT_NAME>Client.exe 127.0.0.1:7777 -WINDOWED -ResX=800 -ResY=450
默认情况下,专用服务器在 localhost Ip 地址(
127.0.0.1)的端口7777处监听。可以添加命令行参数-port=<PORT_NUMBER>,更改专用服务器的端口。如果要更改服务器正在使用的端口,则还需要更改将客户端连接到服务器时的端口。
1.1 启动 Server
Client 连接到 Server 的前提是 Server 启动完毕,监听完毕端口,准备好接收连接了。UE 中监听的核心接口如下:
bool UWorld::Listen( FURL& InURL ); |
其接口核心参数为一个 FURL ,UE 中会根据启动参数和配置等构建一个 FURL,其结构如下 (只展示部分变量):
//URL structure. | |
USTRUCT() | |
struct FURL | |
{ | |
// Optional hostname, i.e. "204.157.115.40" or "unreal.epicgames.com", blank if local. | |
UPROPERTY() | |
FString Host; | |
// Optional host port. | |
UPROPERTY() | |
int32 Port; | |
// Map name, i.e. "SkyCity", default is "Entry". | |
UPROPERTY() | |
FString Map; | |
// Options. | |
UPROPERTY() | |
TArray<FString> Op; | |
} |
可以看到里面有关键的 Host 和 Port 等信息。
Listen 接口具体做了什么呢?
- 通过
UEngine:: CreateNamedNetDriver创建 NetDriver,主要驱动网络同步 UNetDriver::InitListen解析 FURL,监听端口
网络相关的流程在这里开始就交付给了UNetDriver,显然它是一个比较重要的网络管理类,这里简单看下其结构

可以看到主要负责:
- Server 端初始化监听端口
- 初始化连接
- 管理 UNetConnection,UNetConnection 显然就是抽象出来的连接
- 这里有 ServerConnection 和 ClientConnections,当拥有 ServerConnection 时表示当前是 Client 端,拥有 ClientConnection 时表示当前时 Server 端
同时其派生了不同的类,如:
- UDemoNetDriver:用来支持游戏录像和回放(类似守望先锋的击杀回放)
- UWebSocketNetDriver:用于实现 WebSocket 协议的网络通信。WebSocket 是一种基于 TCP 的网络协议,允许在客户端和服务器之间进行双向通信,可以实现实时通信和数据传输。通过使用
UWebSocketNetDriver,可以在 UE4中使用 WebSocket 协议进行网络通信 - UIpNetDriver:用于实现基于 IP(Internet Protocol)的网络通信
Server 端完整的绑定端口监听的流程大致如下:

可以看到其实和普通的 C++ 创建 TCP C/S 连接类似,最终都是创建一个 Socket 并且 Bind 到指定端口。
1.2 Client 初始化
客户端启动之后,也是类似的流程,创建 NetDriver 驱动网络相关的流程,对比 Server,其多了一个 UPendingNetGame 的对象。UPendingNetGame 类是一个用于处理网络游戏连接过程的类。它在客户端尝试连接到服务器时创建,并在连接成功或失败后销毁。
关于 UPendingNetGame
用处:
UPendingNetGame 主要负责处理客户端与服务器之间的连接流程。主要功能包括:
a. 处理连接请求:客户端向服务器发起连接请求时,UPendingNetGame 负责处理这个请求,包括创建套接字连接、发送握手请求等。
b. 加载关卡:在连接过程中,若服务器需要客户端加载一个关卡,UPendingNetGame 负责处理这个请求,包括加载关卡资源、同步关卡状态等。
c. 状态同步:在连接过程中,UPendingNetGame 负责与服务器进行状态同步,包括玩家数据、游戏规则等。
d. 错误处理:若连接过程中出现错误,如超时、被拒绝等,UPendingNetGame 负责处理这些错误,通知用户并做出相应处理创建与销毁:
a. 创建:当客户端尝试连接到服务器时,会创建一个 UPendingNetGame 实例。
b. 销毁:当客户端成功连接到服务器并完成状态同步后,UPendingNetGame 完成其任务并被销毁。如果连接过程中出现错误,如超时、被拒绝等, UPendingNetGame 也会在处理完错误后被销毁
Client 的初始化流程大致如下:
- UEngine::Browse 解析 FURL
- UPendingNetGame::InitNetDriver 初始化网络驱动
- UIpNetDriver::InitConnect 初始化连接
- 创建 UIpNetConnection
- UIpNetConnection::InitLocalConnection 初始化连接信息
- 调用 Connection 的 Handler 的 BeginHandshaking 发握手包
其大致执行堆栈如下:

1.3 Server 收包
Server 端上 PacketHandler 处理的数据包的结构如下:
/** | |
* Represents a view of a received packet, which may be modified to update Data it points to and Data size, as a packet is processed. | |
* Should only be stored as a local variable within functions that handle received packets. | |
**/ | |
struct FReceivedPacketView | |
{ | |
/** View of packet data, with Num() representing BytesRead - can reassign to point elsewhere, but don't use to modify packet data */ | |
TArrayView<const uint8> Data; | |
/** Receive address for the packet */ | |
TSharedPtr<FInternetAddr> Address; | |
/** Error if receiving a packet failed */ | |
ESocketErrors Error; | |
}; |
1.3.1 收包流程
Server 监听完端口之后就要处理客户端发过来的连接请求,由于是 UDPSocket,所以只需要简单的 Bind + RecvFrom 就能接收数据了。其主流程主要由 NetDriver 的 TickDispatch 驱动,如下:
UIpNetDriver::TickDispatch- FPacketIterator (UIpNetDriver*) ++,UE 实现了一个 Iterator 遍历消费 Socket 的 Packet
UIpNetDriver::AdvanceCurrentPacketFPacketIterator::ReceiveSinglePacket迭代器收包- UIpNetDriver 中检查 SocketReceiveThreadRunnable 如果存在这个线程(默认情况下应该是没开的,这个时候就相当于这个线程的逻辑在 GameThread 跑了),从 SocketReceiveThreadRunnable->ReceiveQueue 这个 Packet 队列弹出,这里主要是区分用 GameThread 还是用 SocketReceiveThread 来取包。
FReceiveThreadRunnable::Run本身是生产者,可以将 ReceiveQueue 理解为一个数据中间件,IpNetDriver 的 TickDispatch 则是消费者,一直消费 ReceiveQueue 的数据- ReceiveQueue 在
SocketReceiveThreadRunnable线程中一直使用FSocket::RecvFrom(抽象接口,大部分情况下都是为FSocketBSD::RecvFrom)接收数据,其底层实现就是使用recvfrom这个操作系统接口
- UIpNetDriver 中检查 SocketReceiveThreadRunnable 如果存在这个线程(默认情况下应该是没开的,这个时候就相当于这个线程的逻辑在 GameThread 跑了),从 SocketReceiveThreadRunnable->ReceiveQueue 这个 Packet 队列弹出,这里主要是区分用 GameThread 还是用 SocketReceiveThread 来取包。

SocketReceiveThreadRunnable 默认是没有打开的,官方说明如下:
// If the cvar is set and the socket subsystem supports it, create the receive thread.CVarNetIpNetDriverUseReceiveThread.GetValueOnAnyThread() != 0 && SocketSubsystem->IsSocketWaitSupported()
1.3.2 处理客户端连接
首先 Server 需要检查这个 Packet 是否已经有连接了,这里引出一个问题,Server 端是如何管理和查询 Connection 的?主要是通过解析 Packet 的 Address,在 UNetDriver 中查询缓存地址映射关系。
// 声明 | |
class UNetDriver { | |
TMap<TSharedRef<const FInternetAddr>, UNetConnection*, FDefaultSetAllocator, FInternetAddrConstKeyMapFuncs<UNetConnection*>> MappedClientConnections; | |
} | |
// 使用 | |
const TSharedRef<const FInternetAddr> FromAddr = ReceivedPacket.Address.ToSharedRef(); | |
UNetConnection** Result = MappedClientConnections.Find(FromAddr); |
接下来是处理 Packet
- TickDispatch 正常消费到 Packet 之后,要确定 Packet 该丢给哪一层
- 由于未建立连接,下一层交由
UIpNetDriver::ProcessConnectionlessPacketPacketHandler::IncomingConnectionless校验 Packet 正确性PacketHandler::Incoming_Internal- 遍历
HandlerComponent对包进行处理 StatelessConnectHandlerComponent::IncomingConnectionless处理无连接的 PacketStatelessConnectHandlerComponent::ParseHandshakePacket检查是否为握手包,根据 Packet 时间戳确定是否是 bInitialConnect- 握手包回一个 Challenge 包
StatelessConnectHandlerComponent::SendConnectChallenge
- 遍历
StatelessConnectHandlerComponent::HasPassedChallenge校验- 检查是否是重连,处理重连逻辑
- 创建
UIpConnection UIpConnection::InitRemoteConnection- UNetConnection 的 ClientLoginState 初始化为
EClientLoginState::Type::LoggingIn
- UNetConnection 的 ClientLoginState 初始化为
FNetworkNotify::NotifyAcceptedConnection通知接收连接UNetDriver::AddClientConnection添加UIpConnection
关于 Challenge
Challenge 消息是 Unreal Engine 4(UE4)中的一种网络消息,用于在客户端和服务器之间进行身份验证。在 UE4 中,客户端和服务器之间的通信是通过一种称为 Unreal Network Protocol(简称 UNet)的协议实现的。UNet 通过在客户端和服务器之间发送各种类型的网络消息来管理通信。在 UE4 中,当客户端第一次连接到服务器时,服务器会向客户端发送一个 Challenge 消息,其中包含一个随机生成的 Challenge 令牌。客户端必须将这个 Challenge 令牌使用预共享密钥(PSK)进行签名,并将签名后的结果发送回服务器。服务器会验证签名是否正确,如果正确,则表示客户端是一个合法的用户,并将向客户端发送一个 ChallengeAck 消息,其中包含服务器的签名和一些其他的验证信息。客户端必须验证 ChallengeAck 消息是否正确,并将消息发送回服务器,以便进行最终的身份验证。
关于 NMT_Hello
可以看到收到客户端连接包之后,除了回复正常的 Ack 包之外,会主动给客户端发送一个 NMT_Hello 包,这里的 NMT_Hello 是一个枚举。UE4 中 NMT 开头的枚举是指 NetworkMessageTypes,是 Unreal Engine 4(UE4)中用于管理网络消息类型的一组枚举。在 UE4 中,网络消息是通过一种称为 Unreal Network Protocol(简称 UNet)的协议进行传输和管理的。UNet 通过在客户端和服务器之间发送各种类型的网络消息来管理通信。通过接收不同的 NMT 消息,从而在客户端服务器连接过程中,不同阶段执行不同的操作,比如当前收到这个消息应该加载地图或者创建 PlayerController。
1.4 握手小结
至此大致梳理完了 Client 和 Server 的握手流程:
- 创建网络驱动 UNetDriver
- Server 端 Listen
- Client 端先创建 UIpConnection 发起连接
- Server 端接收连接,回复 ConnectChallenge 包
- Client 收包,回复 ChallengeResponse 包
- Server 回复 ChallengeAck
- 握手完毕
其中重点内容主要有: - UNetDriver 是网络同步核心,用于驱动网络同步
- Client 会有一个
UPendingNetGame在正式连接前驱动握手过程 - Client 会先创建 Connection,Server 收到后才创建对应的 Connection,Connection 用于收发握手过程中的数据包
- Server 和 Client 收包底层使用 Connection 的 PacketHandler
- 握手过程主要利用
PacketHandler的 HandlerComponent 中的StatelessConnectHandlerComponent,其负责整个握手过程,此外 PacketHandler 的 HandlerComponent 可以挂载各种组件来支持对数据包的处理,比如 RSA,加密解密等
双方完整握手的流程如下:

1.5 QA
1.5.1 丢包处理
握手过程中显然有丢包的可能,在 CS 握手过程中,大致发送的 Packet 如下:

Client 主要发送两个包,Handshake 和 ChallengeResponse,当 Client 没有收到回应时,对应阶段在 StatelessConnectHandlerComponent::Tick 都会有一个重发机制。参考代码如下:
void StatelessConnectHandlerComponent::Tick(float DeltaTime) | |
{ | |
if (Handler->Mode == Handler::Mode::Client) | |
{ | |
// ... 省略一些代码 | |
if (LastSendTimeDiff > 1.0) | |
{ | |
if (State == Handler::Component::State::UnInitialized) | |
{ | |
NotifyHandshakeBegin(); | |
} | |
else if (State == Handler::Component::State::InitializedOnLocal && LastTimestamp != 0.0) | |
{ | |
SendChallengeResponse(LastSecretId, LastTimestamp, LastCookie); | |
} | |
} | |
} |
1.5.2 连接过程用到了哪些关键 Class
大致如下:

2 连接过程 - Enter Game
握手完毕后就要准备一些 Gameplay 层的相关操作,比如加载地图等,Packet 对于应用层还是太底层了,UE 为此引入了 Bunch 和 Channel 的概念
2.1 Bunch
2.1.1 Bunch 和 Packet 的区别
首先 Bunch 和 Packet 的关系如下:
- Bunch:Bunch是UE4中的一个基本网络数据单位。它可以被看作是一组数据的集合,这些数据代表了某个特定时刻的游戏状态变化。Bunch充当了一种中介,将游戏的状态信息打包成可以在网络上发送和接收的格式。它包含了一些关于对象、事件和属性的信息,以及一些控制网络通信的元数据。
- Packet:Packet是一个更大的网络数据单位,用于在网络上实际传输数据。一个Packet通常包含多个Bunch,以及其他一些网络层所需的信息,如包序号、时间戳等。Packet在网络上发送时,会被分割成更小的数据包,以适应各种网络环境和传输协议。
Bunch和Packet之间的关系是层次性的。Bunch负责打包游戏状态的变化,而Packet负责在网络上传输这些Bunch。在数据传输过程中,Bunch被组合成Packet,Packet在发送端被编码为可以在网络上传输的二进制数据,然后在接收端被解码还原为Bunch,以便在游戏中应用状态变化。

2.1.2 Bunch 的结构
Bunch 分为 FInBunch 和 FOutBunch,根据这个名字可以看出分别对应收到的 Bunch 结构和 发送的 Bunch 结构,其继承链如下:

FInBunch 的结构如下:
class ENGINE_API FInBunch : public FNetBitReader | |
{ | |
public: | |
// 省略一些字段 | |
int32 PacketId; // Note this must stay as first member variable in FInBunch for FInBunch(FInBunch, bool) to work | |
FInBunch * Next; | |
UNetConnection * Connection; // 属于哪个 Connection | |
int32 ChIndex; // channel 的下标 | |
int32 ChType; // channel 的类型 | |
FName ChName; // channel 的名称 | |
int32 ChSequence; // Channel 的 Seqid | |
uint8 bOpen:1; // 是否是 Channel 的首包 | |
uint8 bClose:1; // 是否是 Channel 的结束包 | |
uint8 bDormant:1; // 是否处于休眠 | |
uint8 bIsReplicationPaused:1; // 复制同步是否被暂停了 | |
uint8 bReliable:1; // 是否为可靠的 Bunch | |
uint8 bPartial:1; // 该 Bunch 是否被拆分 | |
uint8 bPartialInitial:1; // 是不是分片传输中的第一个 Bunch | |
uint8 bPartialFinal:1; // 是不是分片传输中的最后一个 Bunch | |
} |
FOutBunch 的结构如下:
class ENGINE_API FOutBunch : public FNetBitWriter | |
{ | |
public: | |
// 省略一些字段 | |
FOutBunch * Next; | |
UChannel * Channel; | |
double Time; | |
int32 ChIndex; | |
int32 ChType; | |
FName ChName; | |
int32 ChSequence; | |
int32 PacketId; | |
uint8 ReceivedAck:1; // 标记这个数据包是否已经被确认,以避免重复发送 | |
uint8 bOpen:1; | |
uint8 bClose:1; | |
uint8 bDormant:1; | |
uint8 bReliable:1; | |
uint8 bPartial:1; // Not a complete bunch | |
uint8 bPartialInitial:1; // The first bunch of a partial bunch | |
uint8 bPartialFinal:1; // The final bunch of a partial bunch | |
} |
Bunch 的信息中,除了一些分包相关的信息,最主要的便是 Channel 相关的信息了,比如这个 Bunch 属于哪个 Channel?Channel 的类型是什么?那么什么是 Channel ?其用处是什么?
2.2 Channel 定义
UE 中,Channel 主要分为三种类型:
- ActorChannel: 用于在服务器和客户端之间同步Actor状态的通道。它负责在网络上移动、旋转、缩放等操作,并确保所有客户端都具有相同的Actor状态。它还负责同步Actor的变量和属性。
- ControlChannel:一个特殊类型的网络通道,主要负责处理底层的网络连接和控制消息。与其他类型的通道(如UActorChannel)主要用于游戏数据传输不同,UControlChannel处理的消息与游戏逻辑关系较少,主要用于维护网络连接状态、通知连接事件以及传输核心控制信息。ControlChannel 的一些职责示例如下:
- 连接建立和断开:UControlChannel会处理网络连接建立和断开的消息。例如,当客户端与服务器建立连接时,UControlChannel会发送和接收连接请求和响应,以便双方建立通信。同样,当连接断开时,UControlChannel会负责发送断开通知,通知另一方连接已关闭。
- 心跳检测:为了确保连接保持活跃,UControlChannel会定期发送和接收心跳消息。这些消息用于检测双方是否仍在线,以便在一方掉线时及时处理连接断开事件。
- 通道管理:UControlChannel负责处理通道的打开和关闭。例如,当需要创建一个新的UActorChannel以传输游戏对象数据时,UControlChannel会发送相应的打开通道请求。同样,当某个通道不再需要时,UControlChannel会负责发送关闭通道请求。
- 控制消息:UControlChannel还可以处理其他一些控制消息,如暂停、恢复游戏等。这些消息通常对游戏逻辑产生一定影响,但主要用于维护游戏状态和连接。
- VoiceChannel:主要处理语音数据,比如常见的游戏中的队伍聊天
2.3 Channel 的创建
-
Client :Client 上 Channel 的创建接口为
UNetDriver::CreateInitialCilentChannels,其实就是在 InitNetDriver 的时候就创建好了 Channel
-
Server :Server 上 Channel 的创建时机如下:

基本上都是在握手过程中就创建好了 Channel。其关系如下:

2.3 Client 发送 NMT_Hello
Server 端在 InitRemoteConnection 之后,会执行 UNetConnection::SetExpectedClientLoginMsgType(NMT_Hello) ,表示等待 Client 端发送 NMT_Hello 的消息,而 Client 端发送该消息的时机就在握手完毕之后。
Client 端在调用 BeginHandshake 的时候,会传入一个 Delegates,Handshake 完毕之后会调用 Delegates. Broadcast,通知握手完毕,绑定了该 Delegate 的接口都会被执行,大致如下:
// 握手完毕的回调 | |
void UPendingNetGame::InitNetDriver() { | |
// 省略一些代码 | |
// 发起握手,传入握手完毕的回调 | |
ServerConn->Handler->BeginHandshaking( FPacketHandlerHandshakeComplete::CreateUObject(this, &UPendingNetGame::SendInitialJoin)); | |
} | |
// SendInit | |
void UPendingNetGame::SendInitialJoin() { | |
// 省略一些代码 | |
// 发送 NMT_Hello | |
FNetControlMessage<NMT_Hello>::Send(ServerConn, IsLittleEndian, LocalNetworkVersion, EncryptionToken); | |
} |
因此握手完毕后,Client 端就会调用 UPendingNetGame::SendInitialJoin ,发送 NMT_Hello 给 Server 端。
这里还有个问题,如何确定这个 Message 会发送给 ControlChannel ?实际上这里由 FNetControlMessage<>::Send 接口处理,其内部实现会直接发送一个 FControlChannelOutBunch,该 Bunch 会直接使用 Channel[0] 初始化,Channel[0] 默认情况下就是 ControlChannel。
2.5 ControlChannel 处理 ControlMessage
2.5.1 Server
Server 端处理 Bunch 的 CallStack 如下:

其大致流程如下:
- NetDriver 收到 Packet
- NetConnection 拆分 Packet 成多个 Bunch
- 根据 Bunch.ChIndex 找到对应的 Channel(Channel 缓存在 NetConnection)
- Channel 调用
ReceivedBunch(不同的 Channel 会各自重写该接口) - ControlChannel 收到 Message 后调用 NotifyControlMessage 进行广播,执行回调,其中 Server 登录流程相关的最主要的就是
UWorld::NotifyControlMessage接口
2.5.2 Client
Client 端登录过程中主要处理 ControlMessage 的接口为 UPendingNetGame::NotifyControlMessage
2.6 登录,加载地图,创建 PlayerController
- Server 端收到 NMT_Hello 后,会回复 NMT_Challenge
- Client 收到 NMT_Challenge 后,整合玩家数据 NickName,PlayerId 等,发送 NMT_Login
- Server 收到 NMT_Login:
- 设置 Connection 的 PlayerId
- 调用 GameMode::PreLogin,这里我们也可以定义自己的 PreLogin,来加一些 Token 校验之类的确定是否让玩家进入游戏。
- 返回 NMT_Welcome,同时会设置 LevelName,这样客户端就可以知道连接什么地图。
- Client 收到 NMT_Welcome:
- 设置地图路径,在 UPendingNetGame 的 URL 中,UEngine::TickWorldTravel 会一直轮询 UPendingNetGame 的地图 URL
- Travel 到目标地图
- 返回 NMT_NetSpeed 表示成功连接
- Server 收到 NMT_NetSpeed,没有什么特殊操作,只是简单设置下 NetSpeed
- Client 加载地图完毕,发送 NMT_Join。
UPendingNetGame::LoadMapCompleted->UPendingNetGame::SendJoin
- Server 收到 NMT_Join:
- 如果对应的 Connection 没有 PlayerController 则创建一个
- 触发
AGameModeBase::Login - 如果当前 World 的 Map 是 Transition 的或者在一个错误的 World,则也通知 Client 再次进行 Travel
总体流程图如下:
3. 总结
个人将 UE 中,Client 和 Server 建立连接到进入游戏中的过程分为了 2 步:
- 建立一个 UDP 连接(其实 UDP 没有连接的概念),并且在 Server 和 Client 都维护一个 UNetConnection
- 利用 Control Message 和 Control Channel 进行通信,进入游戏,执行 GameMode 的登录,加载地图,创建 PlayerController 等跟 Gameplay 密切相关的操作
相关文章:
UnrealEngine - 网络同步之连接篇
1 连接过程 - 握手 传统的 C/S 架构下,Client 和 Server 通常会建立一条抽象的 Connection,用来进行两端的通信。 UE 的官方文档中提供了 Client 连接到 Server 的示例 ,简单来说分为如下几步: 打包构建好 Client 和 Server 进程…...
【JDBC系列】- 扩展提升学习
扩展提升学习 😄生命不息,写作不止 🔥 继续踏上学习之路,学之分享笔记 👊 总有一天我也能像各位大佬一样 🏆 博客首页 怒放吧德德 To记录领地 🌝分享学习心得,欢迎指正࿰…...
阻塞和非阻塞,同步和异步
文章目录 典型的一次IO的两个阶段IO多路复用是同步还是异步? 典型的一次IO的两个阶段 数据就绪和数据读写 同步:需要应用程序自己操作 IO多路复用是同步还是异步? epoll也是同步的 具体数据读取还是通过应用程序自己完成的 只有使用了特…...
提速Rust编译器!
Nethercote是一位研究Rust编译器的软件工程师。最近,他正在探索如何提升Rust编译器的性能,在他的博客文章中介绍了Rust编译器是如何将代码分割成代码生成单元(CGU)的以及rustc的性能加速。 他解释了不同数量和大小的CGU之间的权衡…...
QT创建项目
可选择CMake或qmake...
基于vue3+webpack5+qiankun实现微前端
一 主应用改造(又称基座改造) 1 在主应用中安装qiankun(npm i qiankun -S) 2 在src下新建micro-app.js文件,用于存放所有子应用。 const microApps [// 当匹配到activeRule 的时候,请求获取entry资源,渲染到containe…...
华为OD真题--完美走位--带答案
2023华为OD统一考试(AB卷)题库清单-带答案(持续更新)or2023年华为OD真题机考题库大全-带答案(持续更新) 题目描述 输入一个长度为4的倍数的字符串Q,字符串中仅包含WASD四个字母。 将这个字符串中的连续子串…...
【AI】《动手学-深度学习-PyTorch版》笔记(十四):多层感知机
AI学习目录汇总 1、多层感知机网络结构 1.1 线性模型:softmax回归 在前面介绍过,使用softmax回归来处理分类问题时,每个输出通过都一个仿射函数计算,网络结构如下,输入和输出之间为全链接层: 1.2 多层感知机 多层感知机就是在输入和输出中间再添加一个或多个全链接…...
本地开发 npm 好用的http server、好用的web server、静态服务器
好用的web server总结 有时需要快速启动一个web 服务器(http服务器)来伺服静态网页,安装nginx又太繁琐,那么可以考虑使用npm serve、http-server、webpack-dev-server。 npm serve npm 的serve可以提供给http server功能&#…...
Gradio入门,并搭个鸡兔同笼问题小应用,附源码(MindOpt)
应用链接: https://979427749bc9ceec34.gradio.live 是公开访问链接,3天有效。 在modelscope中的创空间发布长期有效:https://modelscope.cn/studios/wuyoy520v01/MindOpt_Chicken-with-rabbit-cage/summary。 应用图如下,源代码见正文。 知…...
redis核心知识点简略笔记
value数据类型 string 二进制安全 list 有序、可重复 set 无序、不重复 hash field-value的map sorted set 不重复、通过double类型score分数排序 场景 string 计数器缓存分布式锁访问频率控制分布式session hash 购物车等对象属性灵活修改 list 定时排行榜 set 收藏 sorte…...
消息中间件 —— 初识Kafka
文章目录 1、Kafka简介1.1、消息队列1.1.1、为什么要有消息队列?1.1.2、消息队列1.1.3、消息队列的分类1.1.4、p2p 和 发布订阅MQ的比较1.1.5、消息系统的使用场景1.1.6、常见的消息系统 1.2、Kafka简介1.2.1、简介1.2.2、设计目标1.2.3、kafka核心的概念 2、Kafka的…...
Ceph集群安装部署
Ceph集群安装部署 目录 Ceph集群安装部署 1、环境准备 1.1 环境简介1.2 配置hosts解析(所有节点)1.3 配置时间同步2、安装docker(所有节点)3、配置镜像 3.1 下载ceph镜像(所有节点执行)3.2 搭建制作本地仓库(ceph-01节点执行)3.3 配置私有仓库(所有节点执行)3.4 为 Docker 镜像…...
PXC基于docker搭建mysql集群全过程
之前用mysql自带的bin-log复制,总是因为各种冲突,同步就阻塞掉了,一旦阻塞掉了,不主动发现,同步就终止了。还需要想办法手动去处理。所以考虑重新搭建集群。发现PXC方案不错,可以上两台,对服务器…...
项目知识点记录
1.使用druid连接池 使用properties配置文件: driverClassName com.mysql.cj.jdbc.Driver url jdbc:mysql://localhost:3306/book?useSSLtrue&setUnicodetrue&charsetEncodingUTF-8&serverTimezoneGMT%2B8 username root password 123456 #初始化链接数…...
【HDFS】ListenableFuture在HDFS中的应用
本文主要介绍以下内容: ListenableFuture提供的功能和基本使用方法;AsyncLogger、IPCLoggerChannel(它是AsyncLogger的子类)QuorumCall类一、ListenableFuture的基本使用 ListenableFuture 是 Guava 库中提供的一个接口,它扩展了 JDK 中的 Future 接口,并添加了异步任务…...
Databend 开源周报第 105 期
Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。 Databend 轻量级…...
ArcGISPro随机森林自动化调参分类预测模型展示
更改ArcGISPro的python环境变量请参考文章 ArcGISPro中如何使用机器学习脚本_Z_W_H_的博客-CSDN博客 脚本文件如下 点击运行 结果展示 负类预测概率 正类预测概率 二值化概率 文件夹(模型验证结果) 数据集数据库 ROC曲线 由于个人数据量太少所以…...
科技资讯|苹果手机版Vision Pro头显专利曝光,内嵌苹果手机使用
根据美国商标和专利局(USPTO)公示的清单,苹果公司近日获得了一项头显相关的技术专利,展示了一款亲民款 Vision Pro 头显,可以将 iPhone 放置在头显内部充当屏幕。 根据patentlyapple 媒体报道,这是苹果公司…...
Linux服务器映射到本地磁盘
内容来自网友博客。 把linux服务器上的文件夹映射到本地作为一个磁盘来访问,步骤如下 一. samba的安装: sudo apt-get install samba // (sudo get temp root auth) sudo apt-get install smbfs //旧版本 sudo apt-get install cifs-utils //新版本 上…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
