简介
UE4 版本 : 4.20.3
Visual Studio 版本:2017
在UE4上创建TCP Server并监听客户端接入建立通讯
建立通讯后进行双向数据传输
先决条件
在xxx.Build.cs 文件中添加以下部分
“Sockets”, “Networking”
修改后:
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
public class AVR_Server : ModuleRules
{
public AVR_Server(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Sockets", "Networking" });
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
}
}
代码
首先创建BlueprintFunctionLibrary名为TCPSocketServer
H文件
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Runtime/Networking/Public/Networking.h"
#include "Engine.h"
#include "TCPSocketServer.generated.h"
/**
*
*/
UCLASS()
class AVR_SERVER_API UTCPSocketServer : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "MySocket")
static bool createSoc();
UFUNCTION(BlueprintCallable, Category = "MySocket")
static void closeSoc();
UFUNCTION(BlueprintCallable, Category = "MySocket")
static bool bindSoc(const FString& TheIP, const int32 ThePort);
UFUNCTION(BlueprintCallable, Category = "MySocket")
static bool listenSoc(const int32 MaxBacklog);
UFUNCTION(BlueprintCallable, Category = "MySocket")
static bool acceptSoc(FString& TheIP,int32 & ThePort);
UFUNCTION(BlueprintCallable, Category = "MySocket")
static bool sendSoc(const FString& sendMessage);
UFUNCTION(BlueprintCallable, Category = "MySocket")
static FString recvSoc();
static FString StringFromBinaryArray(const TArray<uint8>& BinaryArray);
};
C文件
// Fill out your copyright notice in the Description page of Project Settings.
#include "TCPSocketServer.h"
FSocket* SocketServer;
FSocket* SocketClient;
FIPv4Address ip;
void UTCPSocketServer::closeSoc()
{
if (SocketServer)
{
//关闭,销毁
SocketServer->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SocketServer);
}
if (SocketClient)
{
//关闭,销毁
SocketClient->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SocketClient);
}
}
bool UTCPSocketServer::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 UTCPSocketServer::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 UTCPSocketServer::listenSoc(const int32 MaxBacklog)
{
bool bListen = SocketServer->Listen(MaxBacklog);
return bListen;
}
bool UTCPSocketServer::acceptSoc(FString & TheIP,int32 & ThePort)
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
FIPv4Address::Parse(TheIP, ip);
uint32 ipd;
//targetAddr->SetIp(ip.Value);
//targetAddr->SetPort(ThePort);
SocketClient = SocketServer->Accept(*targetAddr, "aaa");
ThePort = targetAddr->GetPort();
targetAddr->GetIp(ipd);
char strTemp[20];
sprintf(strTemp, "%d.%d.%d.%d",
(ipd & 0xff000000) >> 24,
(ipd & 0x00ff0000) >> 16,
(ipd & 0x0000ff00) >> 8,
(ipd & 0x000000ff));
TheIP = FString(strTemp);
if (!SocketClient)
return false;
return true;
}
bool UTCPSocketServer::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;
}
FString UTCPSocketServer::recvSoc()
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
TArray<uint8> ReceivedData;//定义一个接收器
uint32 Size;
//注意,要用客户端这个socket
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);
return ReceivedUE4String;
//GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::Red, FString::Printf(TEXT("As String Data ~> %s"), *ReceivedUE4String));
//判断是否发送了相对的指令,进行对应事件调用
//if (ReceivedUE4String.Equals("jiegege"))
//{
// sendSoc("server auto send ");
//}
}
}
return FString();
}
FString UTCPSocketServer::StringFromBinaryArray(const TArray<uint8>& BinaryArray)
{
return FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(BinaryArray.GetData())));
}
蓝图调用
存在的问题
不支持发送非ASCII的数据
接收也是一样的,需要发送HEX数据的人自己修改
如果不修改直接接收/发送HEX格式数据 会导致崩溃