接着今天给大家介绍一下UE4与UE4之间的通信。
这里使用的是TCP的通信方式。
首先是服务器端,新建一个工程,然后新建一个C++类,然后就会产生两个文件:.cpp文件和.h文件
.cpp文件代码
#include "TCPServe.h"
#include "TCPSocketServer.h"
// Sets default values
ATCPSocketServer::ATCPSocketServer()
{
//初始化
PrimaryActorTick.bCanEverTick = true;
SocketServer = NULL;
SocketClient = NULL;
}
void ATCPSocketServer::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
if (SocketServer)
{
//关闭,销毁
SocketServer->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SocketServer);
}
if (SocketClient)
{
//关闭,销毁
SocketClient->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SocketClient);
}
}
bool ATCPSocketServer::createSoc()
{
SocketServer = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);
if (!SocketServer) {
return false;
}
// SocketServer.SetNonBlocking(false);
//SocketServer->SetNonBlocking(false);
return true;
}
bool ATCPSocketServer::bindSoc(const FString & TheIP, const int32 ThePort)
{
FIPv4Address::Parse(TheIP, ip);
TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
addr->SetIp(ip.Value);
addr->SetPort(ThePort);
bool bBind = SocketServer->Bind(*addr);
return bBind;
}
bool ATCPSocketServer::listenSoc(const int32 MaxBacklog)
{
bool bListen = SocketServer->Listen(MaxBacklog);
return bListen;
}
bool ATCPSocketServer::acceptSoc(const FString & TheIP, const int32 ThePort)
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
FIPv4Address::Parse(TheIP, ip);
targetAddr->SetIp(ip.Value);
targetAddr->SetPort(ThePort);
SocketClient = SocketServer->Accept(*targetAddr, "aaa");
if (!SocketClient)
return false;
return true;
}
bool ATCPSocketServer::sendSoc(const FString & sendMessage)
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
//FIPv4Address::Parse(TheIP, ip);
//targetAddr->SetIp(ip.Value);
//targetAddr->SetPort(ThePort);
FString serialized = sendMessage;
bool bsend;
TCHAR *seriallizedChar = serialized.GetCharArray().GetData();
int32 size = FCString::Strlen(seriallizedChar) + 1;
int32 sent = 0;
//注意,要用客户端这个socket
bsend = SocketClient->SendTo((uint8*)TCHAR_TO_UTF8(seriallizedChar), size, sent, *targetAddr);
if (bsend)
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("_____Send Succ!"));
UE_LOG(LogTemp, Warning, TEXT("_____Send Succ!"));
}
else
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("_____Send failed!"));
UE_LOG(LogTemp, Warning, TEXT("_____Send failed!"));
}
return bsend;
}
int ATCPSocketServer::recvSoc()
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
TArray<uint8> ReceivedData;//定义一个接收器
uint32 Size;
int32 clientNum;
if (SocketClient->HasPendingData(Size))
{
uint8 *Recv = new uint8[Size];
int32 BytesRead = 0;
//将数组调整到给定数量的元素。 新元素将被初始化。
ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u));
//注意,要用客户端这个socket
SocketClient->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr);
if (ReceivedData.Num() > 0)
{
//打印
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data Bytes Read ~> %d"), ReceivedData.Num()));
FString ReceivedUE4String = StringFromBinaryArray(ReceivedData);
GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::Red, FString::Printf(TEXT("As String Data ~> %s"), *ReceivedUE4String));
clientNum = ReceivedData.Num() - 1;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("AAAAAAAAAAAA ~> %d"), clientNum));
//判断是否发送了相对的指令,进行对应事件调用
if (ReceivedUE4String.Equals("1"))
{
sendSoc("server auto send ");
}
}
}
/*GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data Bytes Read ~> %d"), clientNum));
return clientNum;*/
//clientNum = ReceivedData.Num() - 1;
return clientNum;
}
FString ATCPSocketServer::StringFromBinaryArray(const TArray<uint8>& BinaryArray)
{
return FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(BinaryArray.GetData())));
}
.h文件代码
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Networking.h"
#include "TCPSocketServer.generated.h"
UCLASS()
class TCPSERVE_API ATCPSocketServer : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATCPSocketServer();
public:
/** Called whenever this actor is being removed from a level */
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION(BlueprintCallable, Category = "ServerTemp")
bool createSoc();
UFUNCTION(BlueprintCallable, Category = "ServerTemp")
bool bindSoc(const FString& TheIP, const int32 ThePort);
UFUNCTION(BlueprintCallable, Category = "ServerTemp")
bool listenSoc(const int32 MaxBacklog);
UFUNCTION(BlueprintCallable, Category = "ServerTemp")
bool acceptSoc(const FString& TheIP, const int32 ThePort);
UFUNCTION(BlueprintCallable, Category = "ServerTemp")
bool sendSoc(const FString& sendMessage);
UFUNCTION(BlueprintCallable, Category = "ServerTemp")
int recvSoc();
FString StringFromBinaryArray(const TArray<uint8>& BinaryArray);
FSocket* SocketServer;
FSocket* SocketClient;
FIPv4Address ip;
};
还有一个工程的.Build.cs的文件需要修改
using UnrealBuildTool;
public class SOCKETNOW : ModuleRules
{
public SOCKETNOW(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore","Networking","Sockets" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}
回到引擎,点击编译,编译完成后,新建一个蓝图类继承刚才新建的C++类,打开蓝图类
客户端重新新建一个工程,同样的新建一个C++类,生成.cpp文件和.h文件。
.cpp文件代码
#include "SOCKETNOW.h"
#include "SOCKET.h"
// Sets default values
ASOCKET::ASOCKET()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SocketClient = NULL;
}
// Called when the game starts or when spawned
void ASOCKET::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
if (SocketClient)
{
//关闭,销毁
SocketClient->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SocketClient);
}
}
// Called every frame
void ASOCKET::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
bool ASOCKET::SocketCreate(FString IPStr, int32 port)
{
FIPv4Address::Parse(IPStr, ip);
TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
addr->SetIp(ip.Value);
addr->SetPort(port);
SocketClient = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);
if (SocketClient->Connect(*addr))
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("Connect Succ!"));
UE_LOG(LogTemp, Warning, TEXT("Connect Succ!"));
return true;
}
else
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("Connect failed!"));
UE_LOG(LogTemp, Warning, TEXT("Connect failed!"));
return false;
}
}
void ASOCKET::SocketSend(FString meesage)
{
TCHAR *seriallizedChar = meesage.GetCharArray().GetData();
int32 size = FCString::Strlen(seriallizedChar) + 1;
int32 sent = 0;
if (SocketClient->Send((uint8*)TCHAR_TO_UTF8(seriallizedChar), size, sent))
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("_____Send Succ!"));
UE_LOG(LogTemp, Warning, TEXT("_____Send Succ!"));
}
else
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("_____Send failed!"));
UE_LOG(LogTemp, Warning, TEXT("_____Send failed!"));
}
}
void ASOCKET::SocketReceive(bool & bReceive, FString & recvMessage)
{
recvMessage = "";
bReceive = false;
if (!SocketClient)
{
return;
}
TArray<uint8> ReceiveData;
uint32 size;
uint8 element = 0;
while (SocketClient->HasPendingData(size))
{
ReceiveData.Init(element, FMath::Min(size, 65507u));
int32 read = 0;
SocketClient->Recv(ReceiveData.GetData(), ReceiveData.Num(), read);
}
if (ReceiveData.Num() <= 0)
{
return;
}
FString log = "Total Data read! num: " + FString::FromInt(ReceiveData.Num() <= 0);
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, log);
UE_LOG(LogTemp, Warning, TEXT("Recv log: %s"), *log);
const FString ReceivedUE4String = StringFromBinaryArray(ReceiveData);
log = "Server:" + ReceivedUE4String;
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, log);
UE_LOG(LogTemp, Warning, TEXT("*** %s"), *log);
recvMessage = ReceivedUE4String;
bReceive = true;
}
void ASOCKET::DataBaseRecive(int32 clickNum)
{
GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Blue, TEXT("___Pangkai"));
UE_LOG(LogTemp, Warning, TEXT("_____my name is nownow!!"));
}
FString ASOCKET::StringFromBinaryArray(TArray<uint8> BinaryArray)
{
return FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(BinaryArray.GetData())));
}
.h文件代码
#pragma once
#include "GameFramework/Actor.h"
#include "Networking.h"
#include "SOCKET.generated.h"
UCLASS()
class SOCKETNOW_API ASOCKET : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASOCKET();
public:
/** Called whenever this actor is being removed from a level */
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "MySocket")
bool SocketCreate(FString IPStr, int32 port);
UFUNCTION(BlueprintCallable, Category = "MySocket")
void SocketSend(FString meesage);
UFUNCTION(BlueprintPure, Category = "MySocket")
void SocketReceive(bool& bReceive, FString& recvMessage);
UFUNCTION(BlueprintCallable, Category = "MySocket")
void DataBaseRecive(int32 clickNum);
FString StringFromBinaryArray(TArray<uint8> BinaryArray);
FSocket *SocketClient;
FIPv4Address ip;
};
同样的.Build.cs文件个服务器端一样。
新建蓝图类继承C++类,打开蓝图类
这里还要说一下如何获取本机的ip和端口,Win+R打开运行,输入cmd打开命令控制面板,输入netstat -an就可以获取服务器ip和端口号,选择状态为TIME_WAIT的一组输入就行了。
记得要把蓝图类拖入到map中,先运行服务器端,在运行客户端,按C键连接。
按C键连接并传值2到服务器端
服务器端已接收到值2.
Demo地址:https://download.youkuaiyun.com/download/sxx930923/10903665