UE4 C++联网RPC教程笔记(三)(第8~9集)完结
UE4 C++联网RPC教程笔记(三)(第8~9集)完结
- 8. exe 后缀实现监听服务器
- 9. C++ 实现监听服务器
8. exe 后缀实现监听服务器
前面我们通过蓝图节点实现了局域网连接的功能,实际上我们还可以给项目打包后生成的 .exe 文件创建一个快捷方式,然后修改这个快捷方式的属性中的目标就可以实现简易的联网功能。
下面内容截取自梁迪老师准备的 RPC 联网文档:
使用 .exe 后缀输入和 open IP 地址联网
注意:这里只讨论 NM_Standalone、NM_ListenServer 以及 NM_Client 的情况
- NM_Standalone:打包出 exe 后,不创建快捷方式直接运行 exe,执行
GetNetMode()
返回 NM_Standalone 类型,在这个状态下打印GetWorld()->IsServer()
以及HasAuthority()
都是 true,如果在存在监听服务器的情况下,运行命令行open 127.0.0.1
,该单独端就会链接上监听服务器,执行GetNetMode()
返回 NM_Client 类型,打印GetWorld()->IsServer()
以及HasAuthority()
都是 false,如果再次运行命令行open 127.0.0.1
,该端会先断开服务器,然后再链接一次 - NM_ListenServer:打包出来的 exe 生成快捷方式,并且在快捷方式的属性下在 exe 结尾添加
(空格)?listen
,如:RPCProject.exe ?listen
,运行该快捷方式就会运行监听服务器端,执行GetNetMode()
返回 NM_ListenServer 类型,打印GetWorld()->IsServer()
以及HasAuthority()
都是true,如果运行命令行open 127.0.0.1
,该监听服务器端就会变成单独端 NM_Standalone - NM_Client:打包出来的 exe 生成快捷方式,并且在快捷方式的属性下在 exe 结尾添加
(空格)127.0.0.1 -game
,如:RPCProject.exe 127.0.0.1 -game
,运行该快捷方式,如果存在监听端,就会链接上监听服务器,成为客户端,执行GetNetMode()
返回 NM_Client 类型,打印GetWorld()->IsServer()
以及HasAuthority()
都是 false
接下来我们在 GameMap 里用到的玩家控制器中添加一些打印当前端的逻辑。
RPCController.h
protected:.void EchoNetMode();
RPCController.cpp
// 引入头文件
#include "RPCHelper.h"void ARPCController::BeginPlay()
{Super::BeginPlay();// 限定打开的窗口尺寸。此处老师将变量名拼写错成 “Src”FString ScreenCommand = FString("r.setres 1280x720w");ConsoleCommand(ScreenCommand);bShowMouseCursor = false;FInputModeGameOnly InputMode;SetInputMode(InputMode);// 打印当前的端EchoNetMode();
}void ARPCController::EchoNetMode()
{ENetMode NetMode = GetNetMode();switch (NetMode){case NM_Standalone:DDH::Debug() << "NM_Standalone" << DDH::Endl();break;case NM_DedicatedServer:DDH::Debug() << "NM_DedicatedServer" << DDH::Endl();break;case NM_ListenServer:DDH::Debug() << "NM_ListenServer" << DDH::Endl();break;case NM_Client:DDH::Debug() << "NM_Client" << DDH::Endl();break;case NM_MAX:DDH::Debug() << "NM_MAX" << DDH::Endl();break;}
}
编译后,将默认关卡设置为 GameMap。随后开始打包。
打包成功后,运行 .exe 文件,可以看到左上角打印了当前的端名以及控制器名。
创建 .exe 文件的一个快捷方式,命名为 RPCCourseServer (保留 .exe 后缀),随后修改该文件的属性。
运行 RPCCourseServer.exe,可以看到左上角已经变成了聆听服务器。
再创建一个快捷方式,命名为 RPCCourseClient,这次给属性里的目标添加后缀 (空格)127.0.0.1 -game
。先运行 RPCCourseServer,然后再运行 RPCCourseClient.exe,可以看到后者的窗口里左上角显示是客户端。
此时在服务端按下 J 键,只有在客户端能看到角色处生成了红色的数字 1。说明联网方法和变量都是可以用的。
关掉客户端,服务端左上角会显示客户端的控制器登出了。
保持服务端开启,运行 RPCCourse.exe,按 ~
键(波浪符)呼出控制台,输入 open 127.0.0.1
。可以看到独立端加入了服务端,并且原独立端左上角输出了当前为客户端。
此时在服务端按下 J 键,也是只有原独立端可以看到自己角色位置生成了一个红色的数字 1。
在服务端呼出控制台然后输入 open 127.0.0.1
,服务器会关闭。
9. C++ 实现监听服务器
前面我们用蓝图和快捷方式实现聆听服务器联机,接下来我们尝试下用 C++ 来实现同样的效果。下面内容截取自梁迪老师准备的 RPC 联网文档:
创建寻找加入会话 C++ 模式(这里只实现局域网)
C++ 联网步骤和蓝图基本相同,但是中间多了一个 StartSession()
方法需要调用,主要可以参考
- UCreateSessionCallbackProxy
- UStartSessionCallbackProxy
- UFindSessionsCallbackProxy
- UJoinSessionCallbackProxy
- UDestroySessionCallbackProxy
这些类的实现,具体实现参考项目里的 URPCInstance 类。
UE4 官方推荐将联网的逻辑放在 GameInstance 下处理,GameInstance 在整个游戏所有关卡中都存在,用来传递关卡数据,在保存数据方面起作用,联网数据放在 GameInstance 下方便在任何关卡去操作联网
接下来开始实操。在默认路径下创建一个 C++ 的 Instance 类,命名为 RPCInstance。
将默认地图设置成 MenuMap。
来到主界面 UI 类,声明一个 URPCInstance 的指针和相应的注册方法,用来保存对 GameInstance 的引用,并且声明两个蓝图可调用的方法用于接入主界面的按钮点击事件。
MenuWidget.h
// 提前声明
class URPCInstance;UCLASS()
class RPCCOURSE_API UMenuWidget : public UUserWidget
{GENERATED_BODY()public:void AssignRPCInstance(URPCInstance* InInstance);UFUNCTION(BlueprintCallable)void LANServerEvent();UFUNCTION(BlueprintCallable)void LANClientEvent();public:URPCInstance* RPCInstance;
};
MenuWidget.cpp
// 引入头文件
#include "RPCInstance.h"void UMenuWidget::AssignRPCInstance(URPCInstance* InInstance)
{RPCInstance = InInstance;
}void UMenuWidget::LANServerEvent()
{RPCInstance->HostSession();
}void UMenuWidget::LANClientEvent()
{RPCInstance->ClientSession();
}
RPCInstance 里承载着联网相关的逻辑,主要都是调用网络模块的 API 和利用委托绑定回调函数。
RPCInstance.h
// 引入头文件
#include "Interfaces/OnlineSessionInterface.h" // 如果这个不行就用下面这句
//#include "../Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlineSessionInterface.h"
#include "Delegates/IDelegateInstance.h"#include "RPCInstance.generated.h"// 提前声明
class IOnlineSubsystem;
class APlayerController;UCLASS()
class RPCCOURSE_API URPCInstance : public UGameInstance
{GENERATED_BODY()public:URPCInstance();// 注册玩家控制器,并且获取联网系统和端 IDvoid AssignPlayerController(APlayerController* InController);// 创建会话void HostSession();// 寻找会话void ClientSession();// 销毁会话void DestroySession();protected:// 开启服务器回调函数void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);void OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful);// 加入服务器回调函数void OnFindSessionsComplete(bool bWasSuccessful);void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);// 销毁会话回调函数void OnDestroySessionComplete(FName SessionName, bool bWAsSuccessful);protected:APlayerController* PlayerController;// 开启服务器委托与句柄FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate;FOnStartSessionCompleteDelegate OnStartSessionCompleteDelegate;FDelegateHandle OnCreateSessionCompleteDelegateHandle;FDelegateHandle OnStartSessionCompleteDelegateHandle;// 加入服务器委托与句柄FOnFindSessionsCompleteDelegate OnFindSessionsCompleteDelegate;FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate;FDelegateHandle OnFindSessionsCompleteDelegateHandle;FDelegateHandle OnJoinSessionCompleteDelegateHandle;// 销毁会话委托与句柄FOnDestroySessionCompleteDelegate OnDestroySessionCompleteDelegate;FDelegateHandle OnDestroySessionCompleteDelegateHandle;// 联网系统IOnlineSubsystem* OnlineSub;// 端的 IDTSharedPtr<const FUniqueNetId> UserID;// 保存寻找到的 SessionsTSharedPtr<FOnlineSessionSearch> SearchObject;
};
RPCInstance.cpp
// 引入头文件
#include "../Plugins/Online/OnlineSubsystem/Source/Public/OnlineSubsystem.h"
#include "../Plugins/Online/OnlineSubsystem/Source/Public/OnlineSessionSettings.h"
#include "../Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlineSessionInterface.h"
#include "../Plugins/Online/OnlineSubsystemUtils/Source/OnlineSubsystemUtils/Public/OnlineSubsystemUtils.h"
#include "GameFramework/PlayerController.h"
#include "RPCHelper.h"
#include "Kismet/GameplayStatics.h"URPCInstance::URPCInstance()
{// 绑定回调函数OnCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &URPCInstance::OnCreateSessionComplete);OnStartSessionCompleteDelegate = FOnStartSessionCompleteDelegate::CreateUObject(this, &URPCInstance::OnStartOnlineGameComplete);OnFindSessionsCompleteDelegate = FOnFindSessionsCompleteDelegate::CreateUObject(this, &URPCInstance::OnFindSessionsComplete);OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &URPCInstance::OnJoinSessionComplete);OnDestroySessionCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &URPCInstance::OnDestroySessionComplete);
}void URPCInstance::AssignPlayerController(APlayerController* InController)
{PlayerController = InController;// 获取 OnlineSub// 获取方式一:Online::GetSubsystem(GetWorld(), NAME_None),推荐方式// 获取方式二:使用 IOnlineSubsystem::Get(),直接获取可以 CreateSession 但是 JoinSession 后客户端没有跳转场景OnlineSub = Online::GetSubsystem(PlayerController->GetWorld(), NAME_None);// 获取 UserID// 获取方式一:UGameplayStatics::GetGameInstance(GetWorld())->GetLocalPlayers()[0]->GetPreferredUniqueNetId()if (GetLocalPlayers().Num() == 0) {DDH::Debug() << "No LocalPlayer Exists, Can't Get UserID" << DDH::Endl();}else {UserID = (*GetLocalPlayers()[0]->GetPreferredUniqueNetId()).AsShared();}#if 0// 获取方式二:使用 PlayerState 获取,该方式在打包成 exe 运行无问题,但是在编辑器模式下运行多个窗口,就会找不到 PlayerStateif (PlayerController->PlayerState)UserID = PlayerController->PlayerState->UniqueId.GetUniqueNetId();elseDDH::Debug() << "No PlayerState Exists, Can't Get UserID" << DDH::Endl();
#endif// 如果在这里直接获取 Session 运行时会报错,生命周期的问题
}void URPCInstance::HostSession()
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 会话设置FOnlineSessionSettings Settings;// 连接数Settings.NumPublicConnections = 10;Settings.bShouldAdvertise = true;Settings.bAllowJoinInProgress = true;// 使用局域网Settings.bIsLANMatch = true;Settings.bUsesPresence = true;Settings.bAllowJoinViaPresence = true;// 绑定委托OnCreateSessionCompleteDelegateHandle = Session->AddOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegate);// 创建会话Session->CreateSession(*UserID, NAME_GameSession, Settings);}}
}void URPCInstance::ClientSession()
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 实例化搜索结果指针并且设定参数SearchObject = MakeShareable(new FOnlineSessionSearch);// 返回结果数SearchObject->MaxSearchResults = 10;// 是否是局域网,就是 IsLANSearchObject->bIsLanQuery = true;SearchObject->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);// 绑定寻找会话委托OnFindSessionsCompleteDelegateHandle = Session->AddOnFindSessionsCompleteDelegate_Handle(OnFindSessionsCompleteDelegate);// 进行会话寻找Session->FindSessions(*UserID, SearchObject.ToSharedRef());}}
}void URPCInstance::DestroySession()
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 绑定销毁会话委托OnDestroySessionCompleteDelegateHandle = Session->AddOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegate);// 执行销毁会话Session->DestroySession(NAME_GameSession);}}
}void URPCInstance::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 解绑创建会话完成回调函数Session->ClearOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegateHandle);// 判断创建会话是否成功if (bWasSuccessful) {DDH::Debug() << "CreateSession Succeed" << DDH::Endl();// 绑定开启会话委托OnStartSessionCompleteDelegateHandle = Session->AddOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegate);// 执行开启会话Session->StartSession(NAME_GameSession);}elseDDH::Debug() << "CreateSession Failed" << DDH::Endl();}}
}void URPCInstance::OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful)
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 注销开启会话委托绑定Session->ClearOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegateHandle);if (bWasSuccessful) {DDH::Debug() << "StartSession Succeed" << DDH::Endl();// 服务端跳转场景UGameplayStatics::OpenLevel(PlayerController->GetWorld(), FName("GameMap"), true, FString("listen"));}elseDDH::Debug() << "StartSession Failed" << DDH::Endl();}}
}void URPCInstance::OnFindSessionsComplete(bool bWasSuccessful)
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 取消寻找会话委托绑定Session->ClearOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegateHandle);if (bWasSuccessful) {// 如果收集的结果存在并且大于 1if (SearchObject.IsValid() && SearchObject->SearchResults.Num() > 0) {DDH::Debug() << "Find Sessions Succeed" << DDH::Endl();// 绑定加入 Session 委托OnJoinSessionCompleteDelegateHandle = Session->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate);// 执行加入 SessionSession->JoinSession(*UserID, NAME_GameSession, SearchObject->SearchResults[0]);}elseDDH::Debug() << "Find Sessions Succeed But Num == 0" << DDH::Endl();}elseDDH::Debug() << "Find Sessions Failed" << DDH::Endl();}}
}void URPCInstance::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 取消加入会话委托绑定Session->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);// 如果加入成功if (Result == EOnJoinSessionCompleteResult::Success) {// 传送玩家到新地图FString ConnectString;if (Session->GetResolvedConnectString(NAME_GameSession, ConnectString)) {DDH::Debug() << "Join Sessions Succeed" << DDH::Endl();// 客户端切换到服务器的关卡PlayerController->ClientTravel(ConnectString, TRAVEL_Absolute);}}else DDH::Debug() << "Join Sessions Failed" << DDH::Endl();}}
}void URPCInstance::OnDestroySessionComplete(FName SessionName, bool bWAsSuccessful)
{if (OnlineSub) {IOnlineSessionPtr Session = OnlineSub->GetSessionInterface();if (Session.IsValid()) {// 注销销毁会话委托Session->ClearOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegateHandle);// 其他逻辑...}}
}
读者可能会发现上面的代码中,绑定委托后直接就通过 Session 执行相应逻辑了,然后在接下来的逻辑里解绑委托。这里笔者倾向于将这一过程理解为 装弹 —> 发射 —> 退弹壳。UE4 已经将网络模块的细枝末节都为我们封装好了,我们只需要知道如何使用就够了,当然,喜欢探索的读者也可以查阅源码去深入理解。
在主界面控制器里注册自己到 RPCInstance,并且将 RPCInstance 注册到主界面 UI。
MenuController.cpp
// 引入头文件
#include "Kismet/GameplayStatics.h"
#include "RPCInstance.h"void AMenuController::BeginPlay()
{// 获取 GameInstanceURPCInstance* RPCInstance = Cast<URPCInstance>(UGameplayStatics::GetGameInstance(GetWorld()));RPCInstance->AssignPlayerController(this);UClass* MenuWidgetClass = LoadClass<UMenuWidget>(NULL, TEXT("WidgetBlueprint'/Game/Blueprint/MenuWidget_BP.MenuWidget_BP_C'"));UMenuWidget* MenuWidget = CreateWidget<UMenuWidget>(GetWorld(), MenuWidgetClass);MenuWidget->AddToViewport();MenuWidget->AssignRPCInstance(RPCInstance); // 注册 RPCInstance 到 MenuWidget
}
最后添加一些网络模块相关的依赖。
RPCCourse.Build.cs
public RPCCourse(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "Slate", "UMG", "HeadMountedDisplay", "OnlineSubsystem", "OnlineSubsystemUtils" }); // 添加这三个模块依赖// 添加动态加载模组DynamicallyLoadedModuleNames.AddRange(new string[] {"OnlineSubsystemNull",});}
编译后,在项目设置里将默认的 GameInstance 设置为 RPCInstance。
来到 MenuWidget_BP 的图表,重新调整两个按钮点击事件的连接节点如下:
此时运行玩家数应该是 3。运行后,在服务端创建服务器,创建成功;让另外两个客户端加入服务器,也能进入成功。并且在服务端按 J 键,另外两个客户端各自能看到自己角色处生成红色数字。
不过如果在客户端创建服务器,另外一个客户端可以加入,但是服务端加入会显示找到会话和加入成功,但不会跳转到 GameMap。所以必须要让服务端创建服务器才能正常运作。
至此,梁迪老师的 RPC 课程到这里就结束了,衷心感谢梁迪老师提供的优质课程 : )
相关文章:

UE4 C++联网RPC教程笔记(三)(第8~9集)完结
UE4 C联网RPC教程笔记(三)(第8~9集)完结 8. exe 后缀实现监听服务器9. C 实现监听服务器 8. exe 后缀实现监听服务器 前面我们通过蓝图节点实现了局域网连接的功能,实际上我们还可以给项目打包后生成的 .exe 文件创建…...

程序员一定要远离“钻研技术无用,搞钱才是正道”的言论
不知道大家有没有刷到过这样的言论: "程序员真的不要花大量时间研究底层代码,技术钻研的再高级再牛也逃不过被优化的下场。 前辈们开发一个功能用一天,我开发一个功能得用一个星期,只会显得我像一个技术菜鸟࿰…...

el-table同时固定左列和右列时,出现错误情况
最近遇到一个问题,就是需求是要求表格同时固定序号列和操作列,我们用的是饿了么组件库的el-table,如下图,出现了错误情况: 解决方法就是使用doLayout方法: 如果使用了keep-alive,可以在activated里执行doLayout方法: activated() {this.$nextTick(() => {this.$ref…...

django自定义后端过滤
DRF自带的过滤 第一个 DjangoFilterBackend 是需要安装三方库见[搜索:多字段筛选]两外两个是安装注册了rest_framework就有。 如上图,只要配置了三个箭头所指的方向,就能使用。 第一个单字段过滤 用户视图集中加上filterset_fields …...

计算机网络Day03--物理层
信道复用技术 频分复用 时分复用 统计时分复用 频分复用(FDM) 最基本 将整个宽带分为多份,用户在分配到一定的频带后,在通信过程中自始至终都使用这个频带 所有的用户在同一时间占用不同的带宽资源,以并行的方式工…...

RabbitMQ节点故障的容错方案
RabbitMQ节点故障的容错方案 1. broker启动加载逻辑1.1 日志文件1.2 broker启动流程1.2.1 整体流程1.2.2 数据恢复流程 2. 队列高可用2.1 选主逻辑2.1.1 从节点晋升策略2.1.2 主队列选择策略 2.2 HA切换 3. 疑问和思考3.1 如果一个broker宕机,运行在broker上的队列数…...

瑞_Redis_初识Redis(含安装教程)
文章目录 1 初识Redis1.1 认识NoSQL1.1.1 结构化与非结构化1.1.2 关联和非关联1.1.3 查询方式1.1.4 事务1.1.5 总结 1.2 认识Redis1.2.1 介绍1.2.2 特征1.2.3 优势 1.3 安装Redis ★★★1.3.1 Linux安装Redis1.3.1.1 安装Redis依赖 1.3.2 Windows安装Redis1.3.2.1 安装步骤1.3.…...

Android进阶(二十九) 走近 IntentFilter
文章目录 一、什么是IntentFilter ?二、IntentFilter 如何过滤隐式意图?2.1 动作测试2.2 类别测试2.3 数据测试 一、什么是IntentFilter ? 如果一个 Intent 请求在一片数据上执行一个动作, Android 如何知道哪个应用程序…...

vue+element下日期组件momentjs转换赋值问题
记录下使用momentjs转换日期字符串赋值给element的日期组件报错问题; <el-date-pickerv-model"form.serviceTime"type"date"class"fill-w mar-t-xs"value-format"yyyy-MM-dd HH:mm:ss"placeholder"请选择日期&quo…...

普源(RIGOL) DHO914S示波器 简单开箱评测
普源精电(RIGOL) DHO914S 12bit数字示波器 简单开箱评测。 旧的示波器感觉不好用,所以换个新的,看中了普源的这款,主要看中它便携支持PD供电,还有伯德图功能,以及12bit的垂直分辨率。如果你对我上面说的点没需求&…...

docker 安装Oracle19c
一、下载镜像 docker pull registry.cn-hangzhou.aliyuncs.com/zhuyijun/oracle:19c通过docker images 命令查看 如下图:已经有oracle 19c镜像。 二、创建挂载文件 # 创建文件 mkdir -p /home/data/oracle/oradata# 授权,不授权会导致后面安装失败 c…...

qt-OPENGL-星系仿真
qt-OPENGL-星系仿真 一、演示效果二、核心程序三、下载链接 一、演示效果 二、核心程序 #include "model.h"Model::Model(QOpenGLWidget *_glWidget) { glWidget _glWidget;glWidget->makeCurrent();initializeOpenGLFunctions(); }Model::~Model() {destroyV…...

Java实战:Spring Boot实现AOP记录操作日志
本文将详细介绍如何在Spring Boot应用程序中使用Aspect Oriented Programming(AOP)来实现记录操作日志的功能。我们将探讨Spring Boot集成AOP的基本概念,以及如何使用Spring Boot实现AOP记录操作日志。最后,我们将通过一个具体示例…...

C++ //练习 7.38 有些情况下我们希望提供cin作为接受istream参数的构造函数的默认实参,请声明这样的构造函数。
C Primer(第5版) 练习 7.38 练习 7.38 有些情况下我们希望提供cin作为接受istream&参数的构造函数的默认实参,请声明这样的构造函数。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 Sa…...

算法:两数之和
算法:两数之和 方法一:暴力法 function twoSum(nums, target) {for (let i 0; i < nums.length; i) {for (let j i 1; j < nums.length; j) {if (nums[i] nums[j] target) {return [i, j];}}}return null; }方法二:哈希表 func…...

pytorch: ground truth similarity matrix
按照真实标签排序pair-wise相似度矩阵的Pytorch代码 本文仅作留档,用于输出可视化 Inputs: Ground-truths Y ∈ R n 1 \mathbf{Y}\in\mathbb R^{n\times 1} Y∈Rn1, Similarity matrix A ∈ R n n \mathbf{A}\in\mathbb R^{n\times n} A∈RnnOutputs: Block dia…...

鸿蒙 gnss 开关使能流程
先WiFi,后 定位,再从蓝牙到NFC,这个就是我大致熟悉开源鸿蒙代码的一个顺序流程,WiFi 的年前差不多基本流程熟悉了,当然还有很多细节和内容没有写到,后续都会慢慢的丰富起来,这一篇将开启GNSS的篇…...

设计模式-创建型模式-抽象工厂模式
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。 由于工厂方法模式中的每个工厂只生产一类产品&…...

【Go】五、Grpc 的入门使用
grpc 与 protobuf grpc 使用的是 protobuf 协议,其是一个通用的 rpc 框架,基本支持主流的所有语言、其底层使用 http/2 进行网络通信,具有较高的效率 protobuf 是一种序列化格式,这种格式具有 序列化以及解码速度快(…...

PDF加粗内容重复读取解决方案
文章目录 前言发现问题解决方案问题分析大致逻辑 show my code 前言 在使用pdfplumber读取PDF的过程中,由于加黑的内容会被莫名其妙的读取两次,带来了很大的困扰。这篇文章将给出解决方案。 发现问题 在在使用pdfplumber读取PDF的过程中,读…...

Golang 并发 Channel的用法
目录 Golang 并发 Channel的用法1. channel 的创建2. nil channel读写阻塞示例close示例 3. channel 的读写4. channel 只读只写5. 关闭channelchannel关闭后,剩余的数据能否取到读取关闭的channel,将获取零值使用ok判断,是否关闭使用for-ran…...

cfa复习资料介绍之二:notes(SchweserNotes)
什么是CFA notes? CFA资料Study Notes都是外国一些出版机构针对CFA考试提供的复习资料,而其中Schweser在国内的名气最大,用的人也最多。内容详尽并且突出重点,并且CFA Notes的内容相比于官方curriculum教材更加符合中国CFA考生的心态&#x…...

FITC Palmitate Conjugate,FITC-棕榈酸酯缀合物,可以用标准 FITC 滤光片组进行成像
FITC Palmitate Conjugate,FITC-棕榈酸酯缀合物,可以用标准 FITC 滤光片组进行成像 您好,欢迎来到新研之家 文章关键词:FITC Palmitate Conjugate,FITC-棕榈酸酯缀合物,FITC 棕榈酸酯缀合物,F…...

本机防攻击简介
定义 在网络中,存在着大量针对CPU(Central Processing Unit)的恶意攻击报文以及需要正常上送CPU的各类报文。针对CPU的恶意攻击报文会导致CPU长时间繁忙的处理攻击报文,从而引发其他业务的中断甚至系统的中断;大量正常…...

Python 进阶语法:JSON
1 什么是 JSON? 1.1 JSON 的定义 JSON 是 JavaScript Object Notation 的简写,字面上的意思是 JavaScript 对象标记。本质上,JSON 是轻量级的文本数据交换格式。轻量级,是拿它与另一种数据交换格式XML进行比较,相当轻…...

mescroll 在uni-app 运行的下拉刷新和上拉加载的组件
官网传送门: https://www.mescroll.com/uni.html 最近使用到了mescroll 但是一直都是整个页面的滚动, 最近需求有需要局部滚动, 收藏了一个博主的文章觉得写的还挺好, 传送门: https://blog.csdn.net/Minions_Fatman/article/details/134754926?spm1001.2014.3001.5506 使用…...

netty的TCP服务端和客户端实现
第一步:引入依赖 <dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.90.Final</version></dependency></dependencies> 第二步:实…...

合纵连横 – 以 Flink 和 Amazon MSK 构建 Amazon DocumentDB 之间的实时数据同步
在大数据时代,实时数据同步已经有很多地方应用,包括从在线数据库构建实时数据仓库,跨区域数据复制。行业落地场景众多,例如,电商 GMV 数据实时统计,用户行为分析,广告投放效果实时追踪ÿ…...

HBase 进阶
参考来源: B站尚硅谷HBase2.x 目录 Master 架构RegionServer 架构写流程MemStore Flush读流程HFile 结构读流程合并读取数据优化 StoreFile CompactionRegion Split预分区(自定义分区)系统拆分 Master 架构 Master详细架构 1)Meta 表格介…...

一周学会Django5 Python Web开发-Django5路由命名与反向解析reverse与resolve
锋哥原创的Python Web开发 Django5视频教程: 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频,包括:2024版 Django5 Python we…...