Przeglądaj źródła

feat: 添加简单网络介绍

NiceTry12138 5 miesięcy temu
rodzic
commit
ede4b0f8be

BIN
UE5/网络/网络基础/Image/005.png


BIN
UE5/网络/网络基础/Image/006.png


BIN
UE5/网络/网络基础/Image/007.png


BIN
UE5/网络/网络基础/Image/008.png


BIN
UE5/网络/网络基础/Image/009.png


+ 103 - 0
UE5/网络/网络基础/README.md

@@ -0,0 +1,103 @@
+# 网络
+
+## 基本类型
+
+### FSocket
+
+代码太多了,直接贴文件路径吧 
+
+[TestActor.h文件](./Src/TestSocket/TestActor.h)
+
+[TestActor.cpp文件](./Src/TestSocket/TestActor.cpp)
+
+核心代码就两部分
+
+- 对于客户端来说,主要是 连接 和 发送信息
+
+```cpp
+ISocketSubsystem* System = ISocketSubsystem::Get();
+
+// 创建地址
+Addr = System->CreateInternetAddr();
+// 创建 Socket
+Host = System->CreateSocket(NAME_Stream, TEXT("default"), false);
+// 绑定 Socket 和 地址 进行连接
+Host->Connect(*Addr);
+// 发送信息
+Host->Send((uint8*)TCHAR_TO_UTF8(Data), Size, Send);
+```
+
+- 对于服务器来说,主要是 监听 和 收取信息
+
+```cpp
+ISocketSubsystem* System = ISocketSubsystem::Get();
+Socket = System->CreateSocket(NAME_Stream, TEXT("default"), false);
+// 绑定 IP 和 端口
+Socket->Bind(*Addr);
+// 监听事件
+Socket->Listen(5);
+
+while(true)
+{
+    // 检查是否有待处理的连接
+    Socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(2));
+
+    // 接受新连接
+    ClientSocket = Socket->Accept(TEXT("ClientConnection"));
+
+    // 接收数据
+    TArray<uint8> ReceiveData;
+    ClientSocket->Recv(ReceiveData.GetData(), ReceiveData.Num(), Read);
+    FString ReceivedString = FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(ReceiveData.GetData())));
+}
+```
+
+这里  `CreateSocket` 的参数是 `NAME_Stream`,除了 `NAME_Stream` 之外还有 `Name_DGram`
+
+- `Name_Stream` 是数据流,用于 TCP 连接
+- `Name_DGram` 是 `DataGram` 的简写,表示是数据报,用于 UDP 连接
+
+另外 `CreateSocket` 还有第三个参数 `ProtocolType`,有两个属性值 `FNetworkProtocolTypes::IPv4` 和 `FNetworkProtocolTypes::IPv6` 分别对应 IPV4 和 IPV6 两个
+
+`FSocket` 属于网络中最基础的地方,根据不同的平台底层实现不同
+
+比如 `FSocketWindows` 就是用于 Windows 平台的
+
+### UNetDriver
+
+是一个比较重要的网络管理类,可以简单看下其结构
+
+
+
+## 初始化流程
+
+### Server 的初始化流程
+
+![](Image/005.png)
+
+在 `Listen` 函数中会创建 `NetDriver`、`Socket` 并绑定
+
+`InURL` 配置如下,监听接口为 17777
+
+![](Image/006.png)
+
+`Listen` 代码的执行流程如下
+
+![](Image/007.png)
+
+### Client 的初始化流程
+
+![](Image/008.png)
+
+默认创建下面这些 `Channel`,都是继承自 `UChannel` 基类,用于表示传递某种类型的信息
+
+| Name | 对象 |
+| --- | --- |
+| Control | UControlChannel |
+| Voice | UVoiceChannel |
+| DataStream | UDataStreamChannel |
+| Actor | UActorChannel |
+
+![](Image/009.png)
+
+

+ 165 - 0
UE5/网络/网络基础/Src/TestSocket/TestActor.cpp

@@ -0,0 +1,165 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "TestActor.h"
+#include "Interfaces/IPv4/IPv4Address.h"
+#include "SocketSubsystem.h"
+#include "SocketTypes.h"
+
+bool ATestActor::BeginConnect()
+{
+	ISocketSubsystem* System = ISocketSubsystem::Get();
+	Host = System->CreateSocket(NAME_Stream, TEXT("default"), false);
+	Host->SetReuseAddr(true);
+
+	uint32 IPAddr;
+	Addr->GetIp(IPAddr);
+	auto OutPort = Addr->GetPort();
+
+	if (!Host->Connect(*Addr))
+	{
+		ESocketErrors ErrorCode = System->GetLastErrorCode();
+		UE_LOG(LogTemp, Log, TEXT("Host Connect Faild %s"), System->GetSocketError(ErrorCode));
+		return false;
+	}
+
+	return true;
+}
+
+bool ATestActor::SendMessage(const FString& Msg)
+{
+	auto Data = Msg.GetCharArray().GetData();
+
+	int32 Size = FCString::Strlen(Data) + 1;
+	int32 Send = 0;
+
+	if (Host->Send((uint8*)TCHAR_TO_UTF8(Data), Size, Send))
+	{
+		UE_LOG(LogTemp, Log, TEXT("Send Message Success"));
+		return true;
+	}
+
+	UE_LOG(LogTemp, Log, TEXT("Send Message Faild"));
+	return false;
+}
+
+void ATestActor::BeginPlay()
+{
+	Super::BeginPlay();
+
+	FIPv4Address IpAddr;
+	FIPv4Address::Parse(Ip, IpAddr);
+	ISocketSubsystem* System = ISocketSubsystem::Get();
+
+	Addr = System->CreateInternetAddr().ToSharedPtr();
+	Addr->SetIp(IpAddr.Value);
+	Addr->SetPort(Port);
+
+	Thread = FRunnableThread::Create(new FSocketListener(Addr.Get()), TEXT("Listener"));
+}
+
+void ATestActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
+{
+	if (Thread)
+	{
+		Thread->Kill();
+		Thread = nullptr;
+	}
+	ISocketSubsystem* System = ISocketSubsystem::Get();
+	if (Host)
+	{
+		Host->Close();
+		System->DestroySocket(Host);
+		Host = nullptr;
+	}
+}
+
+bool FSocketListener::Init()
+{
+	return true;
+}
+
+uint32 FSocketListener::Run()
+{
+	do
+	{
+		InitSocket();
+	} while (Socket == nullptr);
+
+	TArray<uint8> ReceiveData;
+	uint8 Element = 0;
+	int32 BytesRead(0);
+
+	while (bEnabled)
+	{
+		// 接受客户端连接
+		if (AcceptClient())
+		{
+			// 处理客户端数据
+			ProcessClientData();
+		}
+	}
+	return 0;
+}
+
+void FSocketListener::InitSocket()
+{
+	ISocketSubsystem* System = ISocketSubsystem::Get();
+	Socket = System->CreateSocket(NAME_Stream, TEXT("default"), false);
+	Socket->SetReuseAddr(true);
+	if(!Socket->Bind(*Addr))
+	{
+		System->DestroySocket(Socket);
+		Socket = nullptr;
+		return;
+	}
+
+	Socket->Listen(5);
+}
+
+bool FSocketListener::AcceptClient()
+{       
+	// 检查是否有待处理的连接
+	if (!Socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(2)))
+	{
+		return false;
+	}
+
+	// 接受新连接
+	ClientSocket = Socket->Accept(TEXT("ClientConnection"));
+	if (!ClientSocket)
+	{
+		return false;
+	}
+
+	UE_LOG(LogTemp, Log, TEXT("New client connected"));
+	return true;
+}
+
+void FSocketListener::ProcessClientData()
+{
+	TArray<uint8> ReceiveData;
+	ReceiveData.SetNumUninitialized(1024);
+
+	uint32 BytesRead = 0;
+
+	// 接收数据
+	while (ClientSocket->HasPendingData(BytesRead))
+	{
+		int32 Read = 0;
+		if (ClientSocket->Recv(ReceiveData.GetData(), ReceiveData.Num(), Read))
+		{
+			if (Read > 0)
+			{
+				// 转换为字符串 (注意: 实际应用中需要处理二进制数据)
+				FString ReceivedString = FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(ReceiveData.GetData())));
+				UE_LOG(LogTemp, Warning, TEXT("Received: %s"), *ReceivedString.Left(Read));
+			}
+		}
+	}
+
+	// 关闭客户端连接
+	ClientSocket->Close();
+	ISocketSubsystem::Get()->DestroySocket(ClientSocket);
+	ClientSocket = nullptr;
+}

+ 79 - 0
UE5/网络/网络基础/Src/TestSocket/TestActor.h

@@ -0,0 +1,79 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "HAL/Runnable.h"
+
+#include "IPAddress.h"
+#include "Sockets.h"
+
+#include "TestActor.generated.h"
+
+//class FSocket;
+
+class FRunnableThread;
+
+/**
+* Helper class inhibiting screen saver by e.g. moving the mouse by 0 pixels every 50 seconds.
+*/
+class FSocketListener : public FRunnable
+{
+public:
+	FSocketListener(FInternetAddr* InAddr)
+		: bEnabled(true), Addr(InAddr)
+	{}
+
+protected:
+
+	bool Init() override;
+
+	void Stop() override
+	{
+		bEnabled = false;
+		FPlatformMisc::MemoryBarrier();
+	}
+
+	uint32 Run() override;
+
+	void InitSocket();
+	bool AcceptClient();
+	void ProcessClientData();
+
+protected:
+	FInternetAddr* Addr = nullptr;
+	FSocket* Socket = nullptr;
+	FSocket* ClientSocket = nullptr;
+	bool bEnabled = true;
+};
+
+
+UCLASS(BlueprintType)
+class EMPTY53_API ATestActor : public AActor
+{
+	GENERATED_BODY()
+	
+public:
+	UFUNCTION(BlueprintCallable)
+	bool BeginConnect();
+
+	UFUNCTION(BlueprintCallable)
+	bool SendMessage(const FString& Msg);
+
+public:
+	virtual void BeginPlay() override;
+	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
+
+public:
+	UPROPERTY(BlueprintReadWrite, EditAnywhere)
+	FString Ip;
+	
+	UPROPERTY(BlueprintReadWrite, EditAnywhere)
+	int32 Port;
+
+
+	FSocket* Host;
+	TSharedPtr<FInternetAddr> Addr;
+	FRunnableThread* Thread = nullptr;
+};