UE4 使用TCPSocket通信 包含客户端和服务器代码

代码类分别是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<USocketRSThread*> 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<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
		FSocket* RecSocket = serverSocket->Accept(*RemoteAddress, TEXT("Receive Socket"));
		USocketRSThread* RSThread = NewObject<USocketRSThread>();
		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<class USocketRSThread*> 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<FInternetAddr> 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<USocketRSThread>();
		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<uint8> 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<const char*>(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!"));
		}
	}


}
发布了144 篇原创文章 · 获赞 15 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/maxiaosheng521/article/details/103684384