UE4 C++联网RPC教程笔记(一)(第1~4集)

UE4 C++联网RPC教程笔记(一)(第1~4集)

  • 前言
  • 1. 教程介绍与资源
  • 2. 自定义 Debug 功能
  • 3. Actor 的复制
  • 4. 联网状态判断

前言

本系列笔记将会对梁迪老师的《UE4C++联网RPC框架开发吃鸡》教程进行个人的知识点梳理与总结,此课程也像全反射零耦合框架的课程那样,已经超过报名截止时间了,无法通过正常方法观看。

笔者依旧是采取神奇的方法,通过手机浏览器(不同浏览器的效果有差别,有的会直接要求你登录,遇到这样的就换一个;还有可能点开网页会发现没有播放按钮,遇到这样的就换一个网页)搜索该课程后可以在课程预览界面观看,也可以在目录进行跳转,不过没有字幕。建议是在 PC 端的手机模拟器观看。

本课程集数不多,可以通过目录跳转看完,就不需要复制一串数字到 URL 来切换集数了。

笔者用的引擎版本是 4.26.2,老师推荐的引擎版本是 4.20,不同的版本可能在代码上有所区别,笔者会通过注释标明。

本系列文章不允许转载。

本系列笔记可供读者学习后用于复习回顾或参考代码来解决一些敲错了代码导致的 Bug。并且笔者只会贴出对应集数修改的代码内容,已经有了的部分代码基本都不会贴出来,以免笔记篇幅过长。

1. 教程介绍与资源

此处列出本课程需要翻阅的网址:虚幻文档关于 RPC 的讲解 >>【】

RPC 的全称是 Remote Procedure Calls 远程过程调用。

本课程篇幅较短,分两步走:1. RPC 基础 2. 分别用蓝图和 C++ 实现监听服务器。

2. 自定义 Debug 功能

打开 UE4,创建一个新的 C++ 第三人称游戏项目,需带有初学者内容包,命名为 RPCCourse

如果学过梁迪老师另一个课程《UE4全反射零耦合框架开发坦克游戏》的读者可能会有印象,因为这个自定义 Debug 功能也在那个课程里面实现了,学过的读者可自行决定是否再看一遍。

创建以下 C++ 类:

创建一个 Object,命名为 RPCHelper,路径为默认。

要实现自定义 Debug 功能,我们需要用到单例模式

RPCHelper.h

#include "CoreMinimal.h"
// 引入头文件
#include "Engine/GameEngine.h"class RPCCOURSE_API DDRecord
{
private:// 自身单例static TSharedPtr<DDRecord> RecordInst;// 最终输出的字符串FString RecordInfo;// 显示时长float ShowTime;// 显示的颜色FColor ShowColor;public:// 构造和析构函数不写内容,并且由于可能会被大量调用所以写成内联函数inline DDRecord() {}~DDRecord() {}static TSharedPtr<DDRecord> Get();// 初始化显示时长和颜色inline void InitParam(float InTime, FColor InColor){ShowTime = InTime;ShowColor = InColor;}// 实际依赖引擎自带输出逻辑inline void Output(){if (GEngine)GEngine->AddOnScreenDebugMessage(-1, ShowTime, ShowColor, RecordInfo);// 清空最终输出字符串RecordInfo.Empty();}// 移位操作符重写,将传入的各种类型数据都转换成 FString 然后加入最终输出字符串inline DDRecord &operator<<(FName Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FText Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(const char* Info) { RecordInfo += Info; return *this; }inline DDRecord &operator<<(const char Info) { RecordInfo.AppendChar(Info); return *this; }inline DDRecord &operator<<(int32 Info) { RecordInfo.Append(FString::FromInt(Info)); return *this; }inline DDRecord &operator<<(float Info) { RecordInfo.Append(FString::SanitizeFloat(Info)); return *this; }inline DDRecord &operator<<(double Info) { RecordInfo.Append(FString::SanitizeFloat(Info)); return *this; }inline DDRecord &operator<<(bool Info) { RecordInfo.Append(Info ? FString("true") : FString("false")); return *this; }inline DDRecord &operator<<(FVector2D Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FVector Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FRotator Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FQuat Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FTransform Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FMatrix Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FColor Info) { RecordInfo.Append(Info.ToString()); return *this; }inline DDRecord &operator<<(FLinearColor Info) { RecordInfo.Append(Info.ToString()); return *this; }// 在遇到 DDRecord 对象时(即下文的 Endl)输出,即调用 Output()inline void operator<<(DDRecord& Record) { Record.Output(); }
};namespace DDH
{FORCEINLINE DDRecord& Debug(float InTime = 3000.f, FColor InColor = FColor::Yellow){DDRecord::Get()->InitParam(InTime, InColor);return *DDRecord::Get();}FORCEINLINE DDRecord& Endl(){return *DDRecord::Get();}
}

RPCHelper.cpp

TSharedPtr<DDRecord> DDRecord::RecordInst = NULL;TSharedPtr<DDRecord> DDRecord::Get()
{if (!RecordInst.IsValid())RecordInst = MakeShareable(new DDRecord());return RecordInst;
}

接下来到第三人称项目自带的这个 RPCCourseCharacter,重写它的 BeginPlay() 方法来测试一下我们的自定义 Debug 功能。

RPCCourseCharacter.h

protected:void BeginPlay() override;

RPCCourseCharacter.cpp

// 引入头文件
#include "RPCHelper.h"void ARPCCourseCharacter::BeginPlay()
{Super::BeginPlay();// 输出 DebugDDH::Debug(20.f, FColor::Red) << "Hello UE4 " << 123 << 0.888 << FVector(30, 40, 50) << FColor::Red << DDH::Endl();
}

编译后运行游戏,左上角输出红色的 Debug 语句,20 秒后消失。

在这里插入图片描述
继续改进下,让 Debug 支持更多输出方式,比如输出到 Output Log 控制台里,并且有日志记录、警告、报错模式。

RPCHelper.h

class RPCCOURSE_API DDRecord
{
public:// 状态模式,0:Debug,1:Log,2:Warning,3:Erroruint8 PatternID;public:inline void Output(){switch (PatternID) {case 0:{if (GEngine)GEngine->AddOnScreenDebugMessage(-1, ShowTime, ShowColor, RecordInfo);}break;case 1:{UE_LOG(LogTemp, Log, TEXT("%s"), *RecordInfo);}break;case 2:{UE_LOG(LogTemp, Warning, TEXT("%s"), *RecordInfo);}break;case 3:{UE_LOG(LogTemp, Error, TEXT("%s"), *RecordInfo);}break;}RecordInfo.Empty();}
};namespace DDH
{FORCEINLINE DDRecord& Debug(float InTime = 3000.f, FColor InColor = FColor::Yellow){DDRecord::Get()->PatternID = 0;		// 初始化DDRecord::Get()->InitParam(InTime, InColor);return *DDRecord::Get();}// 只改变输出颜色,不管显示时间FORCEINLINE DDRecord& Debug(FColor InColor){return Debug(3000.f, InColor);}FORCEINLINE DDRecord& Log(){DDRecord::Get()->PatternID = 1;return *DDRecord::Get();}FORCEINLINE DDRecord& Warning(){DDRecord::Get()->PatternID = 2;return *DDRecord::Get();}FORCEINLINE DDRecord& Error(){DDRecord::Get()->PatternID = 3;return *DDRecord::Get();}
}

最后测试一下日志记录模式。

RPCCourseCharacter.cpp

void ARPCCourseCharacter::BeginPlay()
{Super::BeginPlay();DDH::Log() << "Hello UE4 " << 123 << 0.888 << FVector(30, 40, 50) << FColor::Red << DDH::Endl();
}

编译后,打开 Window -> Develop Tools -> Output Log,运行游戏,可以看到日志输出了 Debug 语句。

在这里插入图片描述

最后将 BeginPlay() 里的 Debug 语句删除掉。

3. Actor 的复制

以下知识点内容截取自梁迪老师准备的 RPC 联网文档:

(1)bool 变量 bNetLoadOnClient
这个变量是给一开始就放置在场景中的对象使用的。
如果bNetLoadOnClient 设置为 true,当客户端连接上服务端时,客户端也会存在这个对象。
如果 bNetLoadOnClient 设置为 false,当客户端连接上服务端时,客户端不会存在这个对象。
SetReplicates 无论是否为 true 都不会影响这个变量的作用。(关于 SetReplicates 下面会讲解)

在默认路径下新建 3 个 C++ 的 Actor 类,分别命名为 RPCActorCubeReplicateCubeNoReplicate

我们先用 RPCActor 来测试 bNetLoadOnClient

RPCActor.cpp

ARPCActor::ARPCActor()
{bNetLoadOnClient = false;	// 设置为 不网络同步到客户端
}

来到角色类,在 BeginPlay() 里输出场上 RPCActor 实例的数量。

RPCCourseCharacter.cpp

// 引入头文件
#include "Kismet/GameplayStatics.h"
#include "RPCActor.h"void ARPCCourseCharacter::BeginPlay()
{Super::BeginPlay();// 寻找场景中的 RPCActorTArray<AActor*> ActArray;UGameplayStatics::GetAllActorsOfClass(GetWorld(), ARPCActor::StaticClass(), ActArray);DDH::Debug() << "RPCActor Num --> " << ActArray.Num() << DDH::Endl();
}

编译后,将 C++ 类的 RPCActor 拖进场景,随后作以下设置(对于 4.26 版本会多出第 2 步)。运行后结果如图所示。

在这里插入图片描述
输出场景内 RPCActor 的数量为 1 的语句是服务端发出来的,输出数量为 0 的语句则是客户端发出来的。至于为何会分别输出了两遍,是因为在编辑器中,调用引擎自带的屏幕输出方法,会使语句在每个端都输出一次。

同时因为 RPCActor 设置了 bNetLoadOnClientfalse,所以 RPCActor 只存在于服务端。

RPCActor.cpp

ARPCActor::ARPCActor()
{// 重新设置为 网络同步到客户端bNetLoadOnClient = true;
}

编译后,将原本场景里的 RPCActor 删除,重新放置一个 RPCActor。运行游戏,结果如图所示:

在这里插入图片描述
这时服务端和客户端的场景里都存在这个 RPCActor 的实例。

(2)SetReplicates(bool)
调用 SetReplicates(true) 设置 Actor 可以复制。
调用 SetReplicates(false) 设置 Actor 不可以复制。

当在服务端 Spawn 可复制的 Actor 时,客户端会生成。
当在客户端 Spawn 可复制的 Actor 时,其他端不会生成。

测试下 SetReplicates(bool)bNetLoadOnClient 共同作用是什么样的效果。

RPCActor.cpp

ARPCActor::ARPCActor()
{// bNetLoadOnClient 设置为 true 时,如果该对象是一开始就在场景中的对象,// 客户端连接到服务端时该对象也会存在,与 SetReplicates 是否为 true 没有关系SetReplicates(true);bNetLoadOnClient = false;
}

编译后,将原本场景里的 RPCActor 删除,重新放置一个 RPCActor。运行游戏,结果如图所示:

在这里插入图片描述
又变成了客户端没有 RPCActor,服务端有。说明确实 bNetLoadOnClient 的优先级比 SetReplicates(bool) 更高。

接下来单独测试一下 SetReplicates(bool)。我们给 CubeReplicate 和 CubeNoReplicate 添加一些组件方便观察,前者设置可复制,后者设置不可复制。

CubeReplicate.h

protected:UStaticMeshComponent* CubeMesh;

CubeReplicate.cpp

 // 引入头文件
#include "Components/StaticMeshComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "RPCHelper.h"ACubeReplicate::ACubeReplicate()
{PrimaryActorTick.bCanEverTick = true;// 设置复制SetReplicates(true);RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));CubeMesh->SetupAttachment(RootComponent);// 附加模型ConstructorHelpers::FObjectFinder<UStaticMesh> StaticCubeMesh(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Cylinder.Shape_Cylinder'"));CubeMesh->SetStaticMesh(StaticCubeMesh.Object);
}

CubeNoReplicate.h

protected:UStaticMeshComponent* CubeMesh;

CubeNoReplicate.cpp

 // 引入头文件
#include "Components/StaticMeshComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "RPCHelper.h"ACubeNoReplicate::ACubeNoReplicate()
{PrimaryActorTick.bCanEverTick = true;// 设置不复制SetReplicates(false);RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));CubeMesh->SetupAttachment(RootComponent);// 附加模型ConstructorHelpers::FObjectFinder<UStaticMesh> StaticCubeMesh(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_WideCapsule.Shape_WideCapsule'"));CubeMesh->SetStaticMesh(StaticCubeMesh.Object);
}

来到 RPCActor,让前面的两个 Cube 只在服务端生成。

RPCActor.cpp

// 引入头文件
#include "CubeReplicate.h"
#include "CubeNoReplicate.h"
#include "RPCHelper.h"ARPCActor::ARPCActor()
{//SetReplicates(true);bNetLoadOnClient = true;	// 设置为 网络同步到客户端
}void ARPCActor::BeginPlay()
{Super::BeginPlay();// 判断是不是服务端if (GetWorld()->IsServer()) {GetWorld()->SpawnActor<ACubeReplicate>(ACubeReplicate::StaticClass(), GetActorLocation() + FVector::RightVector * 300.f, FQuat::Identity.Rotator());GetWorld()->SpawnActor<ACubeNoReplicate>(ACubeNoReplicate::StaticClass(), GetActorLocation() - FVector::RightVector * 300.f, FQuat::Identity.Rotator());}
}

编译后,将场景内的 RPCActor 删除。

新建一个 Blueprint 文件夹,在里面创建一个基于 RPCActor 的蓝图,命名为 RPCActor_BP,然后将其拖进场景内。

运行游戏,得到效果如图。可以看到服务端出现了 CubeReplicate 和 CubeNoReplicate 的实例,但是客户端没有 CubeNoReplicate 的实例。说明 SetReplicates(bool) 生效了。

在这里插入图片描述
再试一下只在客户端生成,只需要在判断条件前面加个 ! 取反就可以了。

RPCActor.cpp

void ARPCActor::BeginPlay()
{Super::BeginPlay();// 判断是不是客户端if (!GetWorld()->IsServer()) {GetWorld()->SpawnActor<ACubeReplicate>(ACubeReplicate::StaticClass(), GetActorLocation() + FVector::RightVector * 300.f, FQuat::Identity.Rotator());GetWorld()->SpawnActor<ACubeNoReplicate>(ACubeNoReplicate::StaticClass(), GetActorLocation() - FVector::RightVector * 300.f, FQuat::Identity.Rotator());}
}

编译后再次运行,可以看到这回服务端场景内没有生成两个 Actor 的实例,而客户端出现了 CubeReplicate 和 CubeNoReplicate 的实例。说明客户端内生成对象时,即便这个对象是可复制的,它也不会生成到服务端。

在这里插入图片描述

最后将 RPCCourseCharacter.cpp 的 BeginPlay() 方法里的 Debug 语句注释掉。

4. 联网状态判断

本节课需要看的官方参考文档:网络概述 >>【】

以下知识点内容截取自梁迪老师准备的 RPC 联网文档:

(1)AActor 的 HasAuthority(),返回 true 是说明 Actor 是该端创建的角色。

在关卡蓝图或者是 GameMode 以及默认放在场景中的 Actor 等对象使用这个函数可以用来判断是否是服务端,因为关卡蓝图和 GameMode 与默认放在场景中的对象可以看做是由服务端生成的。

不推荐用 HasAuthority() 来判断当前端是不是服务端。梁迪老师推荐的用法是用来它来做下面这个 Actor 的角色判断。

(2)Actor 的角色判断
AActor 里的 ENetRole Role 枚举是用来识别角色的 Actor 的身份的。ENetRole 的几个值:

ROLE_None:该 Actor 在网络游戏中无角色,不会复制。
ROLE_SimulatedProxy:这个 Actor 是其他客户端在本机客户端的一个模拟代理
ROLE_AutonomousProxy:这个 Actor 是本机客户端的自己控制的角色
ROLE_Authority:这个 Actor 是服务器上的 Actor
ROLE_MAX:官方没有解释,笔者个人猜测应该是代表该枚举的最大枚举值。

(3)是否是服务端判断,不推荐使用 HasAuthority() 来判断
推荐使用 GetWorld()->IsServer() 或者 GetNetMode() 判断

(4)端的判断
使用 GetNetMode() 函数可以获取端的属性 ENetMode,分类如下:

NM_Standalone:单独端,单机游戏
NM_DedicatedServer:专用服务器
NM_ListenServer:监听服务器
NM_Client:客户端
NM_MAX:官方没有解释,笔者个人猜测应该是代表该枚举的最大枚举值。

接下来我们打算测试一下 IsServer()HasAuthority() 在 “判断当前端是不是服务端” 的需求上表现如何。

在复制 Cube 和不复制 Cube 的 BeginPlay() 函数输出一下调用上面两个方法后返回的结果。

CubeReplicate.cpp

void ACubeReplicate::BeginPlay()
{Super::BeginPlay();DDH::Debug() << "IsServer --> " << GetWorld()->IsServer() << " ; HasAuthority() --> " << HasAuthority() << "  ACubeReplicate BeginPlay" << DDH::Endl();
}

CubeNoReplicate.cpp

void ACubeNoReplicate::BeginPlay()
{Super::BeginPlay();DDH::Debug() << "IsServer --> " << GetWorld()->IsServer() << " ; HasAuthority() --> " << HasAuthority() << "  ACubeNoReplicate BeginPlay" << DDH::Endl();	
}

接上一节课结尾的代码,此时两个 Cube 的生成逻辑是在客户端上运行的,所以只有在客户端才会生成这两个 Cube,服务端不会生成。

编译后运行,可以看到左上角客户端输出的两句 Debug 信息,IsServer() 返回的结果是 false,符合预期;而 HasAuthority() 返回的结果是 true,说明这个方法不一定能判断当前端是服务端还是客户端。

在这里插入图片描述
重新调整下,让两个 Cube 在服务端生成。

RPCActor.cpp

void ARPCActor::BeginPlay()
{Super::BeginPlay();// 将判断表达式的 ! 去掉if (GetWorld()->IsServer()) {GetWorld()->SpawnActor<ACubeReplicate>(ACubeReplicate::StaticClass(), GetActorLocation() + FVector::RightVector * 300.f, FQuat::Identity.Rotator());GetWorld()->SpawnActor<ACubeNoReplicate>(ACubeNoReplicate::StaticClass(), GetActorLocation() - FVector::RightVector * 300.f, FQuat::Identity.Rotator());}
}

编译后运行,下面和中间的 Debug 语句是服务端打印的,上面的 Debug 语句是客户端打印的。此时 HasAuthority() 确实在服务端则输出了 true,在客户端输出了 false。两次测试结果相比之下还是 IsServer() 更适合用于判断当前端的性质。

在这里插入图片描述
随后将 CubeReplicate 和 CubeNoReplicate 的 BeginPlay() 内的 Debug 语句注释掉。

接下来我们测试一下 AActor 的 GetNetMode() 方法,用于获取当前端类型 ENetMode 的值。

RPCActor.h

protected:// 获取端类型的枚举后以文本形式输出void EchoNetMode();

RPCActor.cpp

void ARPCActor::BeginPlay()
{// 测试完毕后记得注释掉EchoNetMode();
}void ARPCActor::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;}
}

编译后运行游戏,很明显 Client 是客户端输出的,ListenServer 是服务端输出的。

在这里插入图片描述
如果将运行模式调整如下后运行游戏,则会显示 NM_Standalone。

在这里插入图片描述

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

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

相关文章

Rabbitmq入门与应用(五)-延迟队列的设计与实现

延迟队列设计 在开发过程中涉及到延迟队列的应用&#xff0c;例如订单生成后有30分钟的付款时间&#xff0c;注册是有60秒的邮件或者短信的发送读取时间等。 常规使用rabbitmq设计延迟队列有两种方式 使用创建一个延迟队列阻塞消息使用延迟队列插件 Dead Letter Exchanges —…

Aster实现一台电脑当两台使——副屏使用独立win账号

前言&#xff1a;笔者每年回家&#xff0c;都面临着想要和小伙伴一起玩游戏&#xff0c;但小伙伴没有电脑/只有低配电脑的问题。与此同时&#xff0c;笔者自身的电脑是高配置的电脑&#xff0c;因此笔者想到&#xff0c;能否在自己的电脑上运行游戏&#xff0c;在小伙伴的电脑上…

如何图片无损放大?几个无损放大图片分享

在数字化时代&#xff0c;图片已经成为我们生活中不可或缺的一部分。从社交媒体上的分享&#xff0c;到专业摄影作品的展示&#xff0c;再到网页设计和平面广告的制作&#xff0c;图片的质量往往直接影响到我们的视觉体验和信息传达的效果。然而&#xff0c;有时候&#xff0c;…

2024-02-11 Unity 编辑器开发之编辑器拓展2 —— 自定义窗口

文章目录 1 创建窗口类2 显示窗口3 窗口事件回调函数4 窗口中常用的生命周期函数5 编辑器窗口类中的常用成员6 小结 1 创建窗口类 ​ 当想为 Unity 拓展一个自定义窗口时&#xff0c;只需实现继承 EditorWindow 的类即可&#xff0c;并在该类的 OnGUI 函数中编写面板控件相关的…

开发知识点-JAVA-Jeecgboot

Jeecgboot 介绍fofa语法:漏洞列表jeecg-boot-getDictItemsByTable-sqlinuclei yamljeecg-boot-queryTableData-sqlijeecg-boot-sqlijeecg-queryFieldBySql-rcejeecg-register-login-bypass修复建议介绍 「企业级低代码平台」 前后端分离架构SpringBoot 2.x3.x,SpringCloud,…

【漏洞复现】蓝网科技临床浏览系统信息泄露漏洞

Nx01 产品简介 蓝网科技临床浏览系统是一个专门用于医疗行业的软件系统&#xff0c;主要用于医生、护士和其他医疗专业人员在临床工作中进行信息浏览、查询和管理。 Nx02 漏洞描述 蓝网科技临床浏览系统存在信息泄露漏洞&#xff0c;攻击者可以利用该漏洞获取敏感信息。 Nx03…

《C++ Primer Plus》《4、复合类型》

文章目录 前言&#xff1a;1 数组1.1数组的初始化规则1.2 C11的数组初始化方法 2 字符串2.1 拼接字符串常量2.2在数组中使用字符串2.3 字符串输入2.4 每次读取一行字符串输入2.5 混合输入字符串和数字 3 string类简介3.1 C11字符串初始化3.2 赋值、拼接、附加3.3 string类的其他…

CPU是如何工作的?什么是冯·诺依曼架构和哈弗架构?

《嵌入式工程师自我修养/C语言》系列——CPU是如何工作的&#xff1f;什么是冯诺依曼架构和哈弗架构&#xff1f; 一、CPU内部结构及工作原理1.1 CPU的结构1.2 CPU工作流程举例 二、计算机体系结构2.1 冯诺依曼架构2.2 哈弗架构 三、总结 快速学习嵌入式开发其他基础知识&#…

SpringBoot3 + Vue3 由浅入深的交互 基础交互教学

说明&#xff1a;这篇文章是适用于已经学过SpringBoot3和Vue3理论知识&#xff0c;但不会具体如何实操的过程的朋友&#xff0c;那么我将手把手从教大家从后端与前端交互的过程教学。 目录 一、创建一个SpringBoot3项目的和Vue3项目并进行配置 1.1后端配置: 1.1.1applicatio…

notepad++打开文本文件乱码的解决办法

目录 第一步 在编码菜单栏下选择GB2312中文。如果已经选了忽略这一步 第二步 点击编码&#xff0c;红框圈出来的一个个试。我切换到UTF-8编码就正常了。 乱码如图。下面分享我的解决办法 第一步 在编码菜单栏下选择GB2312中文。如果已经选了忽略这一步 第二步 点击编码&#…

基于ORB-SLAM2与YOLOv8剔除动态特征点

基于ORB-SLAM2与YOLOv8剔除动态特征点 以下方法以https://cvg.cit.tum.de/data/datasets/rgbd-dataset/download#freiburg3_walking_xyz数据集进行实验测试APE 首先在不剔除动态特征点的情况下进行测试&#xff1a; 方法1:segment坐标点集合逐一排查剔除 利用YOLOv8的segm…

自定义Linux登录自动提示语

设置提示语的方式 在Linux系统中&#xff0c;可以通过修改几个特定的文件来实现在用户登录时自动弹出提示语。以下是几个常用的方法&#xff1a; 1. 修改/etc/issue文件&#xff1a; 这个文件用于显示本地登录前的提示信息 sudo vi /etc/issue在项目合作的时候&#xff0c;…

VMware虚拟机安装CentOS7

对于系统开发来说&#xff0c;开发者时常会需要涉及到不同的操作系统&#xff0c;比如Windows系统、Mac系统、Linux系统、Chrome OS系统、UNIX操作系统等。由于在同一台计算机上安装多个系统会占据我们大量的存储空间&#xff0c;所以虚拟机概念应运而生。本篇将介绍如何下载安…

鉴源论坛 · 观模丨形式化工程方法之需求建模(上)

作者 | 杨坤 上海控安可信软件创新研究院系统建模组 版块 | 鉴源论坛 观模 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 引言&#xff1a;需求建模是整个软件开发、测试验证与维护的基础。经过长期研究与实践&#xff0c;工业界与学术界均意识到&…

Linux之Shell

第 1 章 Shell 概述 1&#xff09;Linux 提供的 Shell 解析器有 [zhaohadoop101 ~]$ cat /etc/shells /bin/sh /bin/bash /usr/bin/sh /usr/bin/bash /bin/tcsh /bin/csh2&#xff09;bash 和 sh 的关系 [zhaohadoop101 bin]$ ll | grep bash -rwxr-xr-x. 1 root root 941880…

JVM--- 垃圾收集器详细整理

目录 一、垃圾收集需要考虑的三个事情&#xff1a; 二、垃圾回收针对的区域 三、如何判断对象已死 1.引用计数算法&#xff1a; 2.可达性分析算法 四、引用 五、生存还是死亡&#xff1f; 六、回收方法区 七、垃圾收集算法 1.分代收集理论 2.标记-清除算法 3.标记-复制算…

【qt创建线程两种方式】

QT使用线程的两种方式 1.案例进度条 案例解析&#xff1a; 如图由组件一个进度条和三个按钮组成&#xff0c;当点击开始的时候进度条由0%到100%&#xff0c;点击暂停&#xff0c;进度条保持之前进度&#xff0c;再次点击暂停变为继续&#xff0c;点击停止按钮进度条停止。 案…

案例:CentOS8 在 MySQL8.0 实现半同步复制

异步复制 MySQL 默认的复制即是异步的&#xff0c;主库在执行完客户端提交的事务后会立即将结果返给给客户端&#xff0c;并不关心从库是否已经接收并处理&#xff0c;这样就会有一个问题&#xff0c;主节点如果 crash 掉了&#xff0c;此时主节点上已经提交的事务可能并没有传…

.NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库

一、效果 记录日志为文档 记录日志到数据库 二、添加NuGet包 三、log4net.config代码配置 <?xml version"1.0" encoding"utf-8" ?> <log4net><!-- Debug日志 --><appender name"RollingFileDebug" type"log4net…

Java学习24--异常

异常 软件运行过程中的各种意料之外叫做Exception&#xff0c;比如要读取的文件找不到&#xff0c;准备联网发现没网&#xff0c;等着int参数来了个String 注意Error和exception不一样&#xff0c;error错的比较猛&#xff0c;一般是直接把JAVA整个搞崩了&#xff0c;比如内存…