HeartlessLD/UE4-TCP-Plugin: 简单的UE4 tcp通信插件 (github.com)
下面的代码有瑕疵 只做参考用
代码类分别是TCPServer TCPClient SocketRSThread这个是负责在线程中收发数据 没有什么特别的东西 直接上代码
服务器类:
TCPServer.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SocketRSThread.h"
#include "TCPServer.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FServerSocketCreateDelegate, bool, bSuccess);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FConnectReceiveDelegate, FString, RemoteIP, int32, RemotePort);
UCLASS(BlueprintType, Blueprintable)
class BJ_SCENE_API ATCPServer : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATCPServer();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
/**
* 创建服务器Socket
* @ServerIP-服务器的IP地址
* @ServerPort-服务器的端口号
* @ReceiveBufferSize-接受数据大小
* @SendBufferSize-发送数据大小
*/
UFUNCTION(BlueprintCallable, Category = Network)
void CreateServer(const FString& ServerIP, int32 ServerPort, int32 ReceiveBufferSize = 1024, int32 SendBufferSize = 1024);
/** 关闭Server */
UFUNCTION(BlueprintCallable, Category = Network)
void CloseServer();
/** 检测是否有客户端连入 */
void ConnectCheck();
UFUNCTION(BlueprintCallable, Category = Network)
void SendToClient(FString Message);
UFUNCTION(Category = Network)
void OnClientDisconnect(class USocketRSThread* pThread);
protected:
int32 SendDataSize;
int32 RecDataDize;
class FSocket* serverSocket;
UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
FServerSocketCreateDelegate SocketCreateDelegate;
UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
FConnectReceiveDelegate ConnectReceiveDelegate;
UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
FReceiveSocketDataDelegate ReceiveSocketDataDelegate;
FTimerHandle ConnectCheckHandler;
TArray RecThreads;
};
TCPServer.CPP
// Fill out your copyright notice in the Description page of Project Settings.
#include "TCPServer.h"
#include "TcpSocketBuilder.h"
#include "SocketRSThread.h"
// Sets default values
ATCPServer::ATCPServer()
{
// 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;
}
// Called when the game starts or when spawned
void ATCPServer::BeginPlay()
{
Super::BeginPlay();
}
void ATCPServer::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
CloseServer();
Super::EndPlay(EndPlayReason);
}
// Called every frame
void ATCPServer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATCPServer::CreateServer(const FString& ServerIP, int32 ServerPort, int32 ReceiveBufferSize, int32 SendBufferSize)
{
this->RecDataDize = ReceiveBufferSize;
this->SendDataSize = SendBufferSize;
FIPv4Address ServerAddr;
if (!FIPv4Address::Parse(ServerIP, ServerAddr))
{
UE_LOG(LogTemp, Error, TEXT("Server Ip %s is illegal"), *ServerIP);
}
serverSocket = FTcpSocketBuilder(TEXT("Socket Listener"))
.AsReusable()
.AsBlocking()
.BoundToAddress(ServerAddr)
.BoundToPort(ServerPort)
.Listening(8)
.WithReceiveBufferSize(ReceiveBufferSize)
.WithSendBufferSize(SendBufferSize);
if (serverSocket)
{
UE_LOG(LogTemp, Warning, TEXT("Server Create Success!"), *ServerIP);
SocketCreateDelegate.Broadcast(true);
GetWorld()->GetTimerManager().SetTimer(ConnectCheckHandler, this, &ATCPServer::ConnectCheck, 1, true);
}
else
{
UE_LOG(LogTemp, Error, TEXT("Server Create Failed!"));
SocketCreateDelegate.Broadcast(false);
}
}
void ATCPServer::CloseServer()
{
if (serverSocket)
{
serverSocket->Close();
for (auto RecThreald : RecThreads)
{
RecThreald->Stop();
}
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(serverSocket);
}
}
void ATCPServer::ConnectCheck()
{
bool bPending = false;
if (serverSocket->HasPendingConnection(bPending) && bPending)
{
//有新的socket连接进来
TSharedRef RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
FSocket* RecSocket = serverSocket->Accept(*RemoteAddress, TEXT("Receive Socket"));
USocketRSThread* RSThread = NewObject();
RecThreads.Add(RSThread);
RSThread->ReceiveSocketDataDelegate = ReceiveSocketDataDelegate;
RSThread->LostConnectionDelegate.AddDynamic(this, &ATCPServer::OnClientDisconnect);
RSThread->Start(RecSocket, SendDataSize, RecDataDize);
ConnectReceiveDelegate.Broadcast(RemoteAddress->ToString(false), RemoteAddress->GetPort());
}
}
void ATCPServer::SendToClient(FString Message)
{
for (auto SocketThread : RecThreads)
{
SocketThread->Send(Message);
}
}
void ATCPServer::OnClientDisconnect(USocketRSThread* pThread)
{
UE_LOG(LogTemp, Warning, TEXT("Client lost"));
RecThreads.Remove(pThread);
}
客户端:
TCPClient.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SocketRSThread.h"
#include "TCPClient.generated.h"
UCLASS(BlueprintType, Blueprintable)
class SCENE_ANDROID_API ATCPClient : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATCPClient();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION()
void OnServerDisconnect(class USocketRSThread* pThread);
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable, Category = Network)
void CreateClientAndConnect(FString ServerIP, int32 Port, int32 ReceiveSize = 1024, int32 SendSize = 1024);
UFUNCTION(Category = Network)
bool ConnectServer(FString ServerIP, int32 Port);
UFUNCTION(BlueprintCallable, Category = Network)
void SendToServer(FString Message);
UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
FReceiveSocketDataDelegate ReceiveSocketDataDelegate;
protected:
class FSocket* ClientSocket;
int32 SendDataSize;
int32 RecDataDize;
TArray RecThreads;
};
TCPClient.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TCPClient.h"
#include "TcpSocketBuilder.h"
#include "SocketRSThread.h"
// Sets default values
ATCPClient::ATCPClient()
{
// 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;
}
// Called when the game starts or when spawned
void ATCPClient::BeginPlay()
{
Super::BeginPlay();
}
void ATCPClient::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (ClientSocket)
{
for (auto RecThreald : RecThreads)
{
RecThreald->Stop();
}
}
}
void ATCPClient::OnServerDisconnect(USocketRSThread* pThread)
{
UE_LOG(LogTemp, Warning, TEXT("Server lost"));
RecThreads.Remove(pThread);
}
// Called every frame
void ATCPClient::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATCPClient::CreateClientAndConnect(FString ServerIP, int32 Port, int32 ReceiveSize, int32 SendSize)
{
this->SendDataSize = SendSize;
this->RecDataDize = ReceiveSize;
ClientSocket = FTcpSocketBuilder(TEXT("Client Socket"))
.AsReusable()
.AsBlocking()
.WithReceiveBufferSize(ReceiveSize)
.WithSendBufferSize(SendSize);
if (!ClientSocket)
{
UE_LOG(LogTemp, Error, TEXT("Create Client Socket Error!"));
}
else
{
ConnectServer(ServerIP, Port);
}
}
bool ATCPClient::ConnectServer(FString ServerIP, int32 Port)
{
FIPv4Endpoint ServerEndpoint;
FIPv4Endpoint::Parse(ServerIP, ServerEndpoint);
TSharedPtr addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
bool Success = true;
addr->SetIp(*ServerIP, Success);
if (!Success)
{
return false;
}
addr->SetPort(Port);
if (ClientSocket->Connect(*addr))
{
USocketRSThread* RSThread = NewObject();
RecThreads.Add(RSThread);
RSThread->ReceiveSocketDataDelegate = ReceiveSocketDataDelegate;
RSThread->LostConnectionDelegate.AddDynamic(this, &ATCPClient::OnServerDisconnect);
RSThread->Start(ClientSocket, SendDataSize, RecDataDize);
UE_LOG(LogTemp, Warning, TEXT("Client Connect Success"));
return true;
}
else
{
ESocketErrors LastErr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode();
UE_LOG(LogTemp, Warning, TEXT("Connect failed with error code (%d) error (%s)"), LastErr, ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError(LastErr));
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ClientSocket);
}
return false;
}
void ATCPClient::SendToServer(FString Message)
{
for (auto SocketThread : RecThreads)
{
SocketThread->Send(Message);
}
}
收发数据线程类:
SocketRSThread.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Sockets.h"
#include "HAL/Runnable.h"
#include "SocketRSThread.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FReceiveSocketDataDelegate, FString, Data);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLostConnectionDelegate, USocketRSThread*, Thread);
/**
* Socket数据收 发线程
*/
UCLASS()
class SCENE_ANDROID_API USocketRSThread : public UObject, public FRunnable
{
GENERATED_BODY()
public:
// 线程接口
virtual bool Init() override { return true; }
virtual uint32 Run() override;
virtual void Stop() override;
virtual void Exit() override {}
// End FRunnable Interface
void Start(FSocket* Socket, uint32 SendDataSize, uint32 RecDataSize);
void Send(FString Message);
FReceiveSocketDataDelegate ReceiveSocketDataDelegate;
FLostConnectionDelegate LostConnectionDelegate;
protected:
FSocket* ConnectSocket;
uint32 SendDataSize;
uint32 RecDataSize;
TArray ReceiveData;
/** 线程相关 */
FRunnableThread* pThread;
bool bThreadStop;
};
SocketRSThread.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SocketRSThread.h"
#include "SocketSubsystem.h"
uint32 USocketRSThread::Run()
{
while (!bThreadStop)
{
//这个地方是之前将socket设置为阻塞模式 在这里5s内判断是否断开连接
uint32 Size;
bool LostConnect = false;
ConnectSocket->HasPendingConnection(LostConnect);
ConnectSocket->Wait(ESocketWaitConditions::WaitForReadOrWrite, FTimespan(0, 0, 5));
if (LostConnect)
{
//UE_LOG(LogTemp, Warning, TEXT(" doesn't Connect "));
Stop();
LostConnectionDelegate.Broadcast(this);
continue;
}
/** 处理接收数据 */
if (ConnectSocket && ConnectSocket->HasPendingData(Size))
{
ReceiveData.Init(0, FMath::Min(Size, RecDataSize));
int32 Readed;
ConnectSocket->Recv(ReceiveData.GetData(), RecDataSize, Readed);
FString ReceivedString = FString(ANSI_TO_TCHAR(reinterpret_cast(ReceiveData.GetData())));
ReceiveSocketDataDelegate.Broadcast(ReceivedString);
}
}
return 0;
}
void USocketRSThread::Start(FSocket* Socket, uint32 SendDataSize, uint32 RecDataSize)
{
this->ConnectSocket = Socket;
this->SendDataSize = SendDataSize;
this->RecDataSize = RecDataSize;
FRunnableThread::Create(this, TEXT("Receive Threald"));
}
void USocketRSThread::Stop()
{
bThreadStop = true;
ConnectSocket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ConnectSocket);
ConnectSocket = nullptr;
}
void USocketRSThread::Send(FString Message)
{
///** 处理发送数据 */
TCHAR* SendMessage = Message.GetCharArray().GetData();
int32 size = FCString::Strlen(SendMessage) + 1;
int32 sent = 0;
if (size >= (int32)SendDataSize)
{
UE_LOG(LogTemp, Error, TEXT("Send Data Size is Larger than Max Size for set"));
}
else
{
if (ConnectSocket && ConnectSocket->Send((uint8*)TCHAR_TO_UTF8(SendMessage), size, sent))
{
UE_LOG(LogTemp, Warning, TEXT("___Send Succeed!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("___Send Failed!"));
}
}
}