UE4——UDP广播消息

1、首先要创建两个C++类,我们就命名为Send和Recive。
下面是Send类的代码:
Send.h:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Networking/Public/Networking.h"
#include "UDPSend.generated.h"

UCLASS()
class GAME_API AUDPSend : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AUDPSend();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	
	//新建isudp的bool变量,表示用UDP进行通信
	bool IsUDP;
	
	//新建函数RamaUDPSender_SendString(),用于发送消息
	UFUNCTION(BlueprintCallable, Category = "UDP")
		bool RamaUDPSender_SendString(FString ToSend);

	
public:
	TSharedPtr<FInternetAddr> RemoteAddr;

	FSocket* SenderSocket;

	// SocketName,IP,Port,IsUdp
	UFUNCTION(BlueprintCallable, Category = "UDP")
		bool StartUDPSender(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, bool UDP);

	//此函数用于在虚幻C++内获取本地IP,转化为String类型
	UFUNCTION(BlueprintPure, Category = "UDP")
		FString GetIP();


	UFUNCTION(BlueprintCallable, Category = "UDP")
		bool SendGameTime(FString ToSend);



public:

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UDP")
		bool ShowOnScreenDebugMessages;

	FORCEINLINE void ScreenMsg(const FString& Msg)
	{
		if (!ShowOnScreenDebugMessages) return;
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg);
	}
	FORCEINLINE void ScreenMsg(const FString& Msg, const float Value)
	{
		if (!ShowOnScreenDebugMessages) return;
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %f"), *Msg, Value));
	}
	FORCEINLINE void ScreenMsg(const FString& Msg, const FString& Msg2)
	{
		if (!ShowOnScreenDebugMessages) return;
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %s"), *Msg, *Msg2));
	}

public:
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

};

Send.cpp

#include "UDPSend.h"


// Sets default values
AUDPSend::AUDPSend()
{
 	// 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;


	SenderSocket = NULL;
	ShowOnScreenDebugMessages = true;

}

// Called when the game starts or when spawned
void AUDPSend::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AUDPSend::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

bool AUDPSend::RamaUDPSender_SendString(FString ToSend)
{
	if (!SenderSocket)
	{
		ScreenMsg("No sender socket");
		return false;
	}

	int32 BytesSent = 0;
	FString serialized = ToSend;
	TCHAR *serializedChar = serialized.GetCharArray().GetData();
	int32 size = FCString::Strlen(serializedChar);
	int32 sent = 0;
	SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(serializedChar), size, BytesSent, *RemoteAddr);//发送给远端地址
	if (BytesSent <= 0)
	{
		const FString Str = "Socket is valid but the receiver received 0 bytes, make sure it is listening properly!";
		UE_LOG(LogTemp, Error, TEXT("%s"), *Str);
		ScreenMsg(Str);
		return false;
	}
	return true;

}

bool AUDPSend::StartUDPSender(const FString & YourChosenSocketName, const FString & TheIP, const int32 ThePort, bool UDP)
{
	RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	bool bIsValid;
	RemoteAddr->SetIp(*TheIP, bIsValid);
	RemoteAddr->SetBroadcastAddress();
	RemoteAddr->SetPort(ThePort);
	if (!bIsValid)
	{
		return false;
	}

	SenderSocket = FUdpSocketBuilder(*YourChosenSocketName)
		.AsReusable()//使绑定的地址可以被其他套接字重用
		.WithBroadcast()
		.WithSendBufferSize(2 * 1024 * 1024)
		/*.BoundToEndpoint(Endpoint)*/
		;

	check(SenderSocket->GetSocketType() == SOCKTYPE_Datagram);
	int32 SendSize = 2 * 1024 * 1024;
	SenderSocket->SetSendBufferSize(SendSize, SendSize);
	SenderSocket->SetReceiveBufferSize(SendSize, SendSize);
	if (bIsValid)
	{
		bIsValid = true;
	}
	return bIsValid;

}


void AUDPSend::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
	if (SenderSocket)
	{
		SenderSocket->Close();
		ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket);
	}
}



FString AUDPSend::GetIP()
{
	bool canBind = false;
	TSharedRef<FInternetAddr> localIP = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalHostAddr(*GLog, canBind);
	// 获得本机的IP
	FString ipStr = (localIP->IsValid() ? localIP->ToString(false) : "");

	return ipStr;
}

bool AUDPSend::SendGameTime( FString gametime)
{
	if (!SenderSocket)
	{
		ScreenMsg("No sender socket");
		return false;
	}

	int32 BytesSent = 0;
	FString serialized = gametime;
	TCHAR *serializedChar = serialized.GetCharArray().GetData();
	int32 size = FCString::Strlen(serializedChar);
	int32 sent = 0;
	SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(serializedChar), size, BytesSent, *RemoteAddr);//发送给远端地址
	if (BytesSent <= 0)
	{
		const FString Str = "Socket is valid but the receiver received 0 bytes, make sure it is listening properly!";
		UE_LOG(LogTemp, Error, TEXT("%s"), *Str);
		ScreenMsg(Str);
		return false;
	}
	return true;
}

Send创建完毕后再UE4编辑器内蓝图继承后,
UE4——UDP广播消息
调用c++函数输入广播地址,广播地址为255.255.255.255,如果需要单播发送信息,输入指定IP即可,端口可以直接设置,前提是没有被占用,勾选UPD,用Tick发送信息。

接下来是Recive的代码

Recive.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Networking/Public/Networking.h"
#include "UDPRecive.generated.h"

UCLASS()
class GAME_API AUDPRecive : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AUDPRecive();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

public:

	FSocket* ListenSocket;

	FUdpSocketReceiver* UDPReceiver = nullptr;

	TSharedPtr<FInternetAddr> RemoteAddr;

	UFUNCTION(BlueprintCallable, Category = "UDP")
		void StartUDPReceiver(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, bool& success);
//接受消息
	UFUNCTION(BlueprintPure, Category = "UDP")
		void DataRecv(FString& str, bool& success);

	FORCEINLINE void ScreenMsg(const FString& Msg)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *Msg);
	}
	FORCEINLINE void ScreenMsg(const FString& Msg, const float Value)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %f"), *Msg, Value));
	}
	FORCEINLINE void ScreenMsg(const FString& Msg, const FString& Msg2)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %s"), *Msg, *Msg2));
	}

public:
	/** Called whenever this actor is being removed from a level */
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

};

Recive.cpp

#include "UDPRecive.h"


// Sets default values
AUDPRecive::AUDPRecive()
{
 	// 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;

	ListenSocket = NULL;
}

// Called when the game starts or when spawned
void AUDPRecive::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void AUDPRecive::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}


//结束时出发事件
void AUDPRecive::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
	//UDPReceiver置空
	delete UDPReceiver;
	UDPReceiver = nullptr;

	//Clear all sockets!
	if (ListenSocket)
	{
		ListenSocket->Close();
		ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenSocket);
	}
}

//初始化Receiver
void AUDPRecive::StartUDPReceiver(const FString & YourChosenSocketName, const FString & TheIP, const int32 ThePort, bool & success)
{
	//TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	FIPv4Address Addr;
	FIPv4Address::Parse(TheIP, Addr);

	//使用指定的NetID和端口创建并初始化新的IPv4端点。
	// Any: Defines the wild card endpoint, which is 0.0.0.0:0
	FIPv4Endpoint Endpoint(FIPv4Address::Any, ThePort);  //所有ip地址本地
	//FUdpSocketBuilder: Implements a fluent builder for UDP sockets.
	ListenSocket = FUdpSocketBuilder(*YourChosenSocketName)
		.AsNonBlocking()//将套接字操作设置为非阻塞。 这个实例(用于方法链)。
		.AsReusable()//使绑定的地址可以被其他套接字重用。 这个实例(用于方法链)。
		.BoundToEndpoint(Endpoint)//设置将端口绑定到本地端点。 这个实例(用于方法链)。
		.WithReceiveBufferSize(2 * 1024 * 1024)//设置接收数据大小
		;
	//BUFFER SIZE
	int32 BufferSize = 2 * 1024 * 1024;
	ListenSocket->SetSendBufferSize(BufferSize, BufferSize);
	ListenSocket->SetReceiveBufferSize(BufferSize, BufferSize);

	if (ListenSocket)
	{
		success = true;
	}
	else {
		ScreenMsg("No socket");
		success = false;
	}

	//return true;
}

void AUDPRecive::DataRecv(FString & str, bool & success)
{
	if (!ListenSocket)
	{
		ScreenMsg("No sender socket");
		success = false;

	}

	TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	TArray<uint8> ReceivedData;//定义一个接收器
	uint32 Size;
	//ListenSocket->HasPendingData(Size) 查询套接字以确定队列中是否有挂起的数据,如果套接字有数据,则为true,否则为false           Size参数指示单个recv调用的管道上有多少数据
	if (ListenSocket->HasPendingData(Size))
	{
		success = true;
		str = "";
		uint8 *Recv = new uint8[Size];
		int32 BytesRead = 0;

		//将数组调整到给定数量的元素。 新元素将被初始化。
		ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u));
		ListenSocket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr);
		char ansiiData[1024];
		memcpy(ansiiData, ReceivedData.GetData(), BytesRead);//拷贝数据到接收器
		ansiiData[BytesRead] = 0;                            //判断数据结束
		FString debugData = ANSI_TO_TCHAR(ansiiData);         //字符串转换
		str = debugData;
		// memset(ansiiData,0,1024);//清空
	}
	else
	{
		success = false;
	}
}

创建完成后一样的蓝图继承,
UE4——UDP广播消息
注意,端口设置要和发送端的端口一样,否则无法接受到消息,
设置完成后分别拖入场景中实例化,运行,就能收到消息了。
(这篇文章也是为了自己学习所用,如果发现侵权,还请告知)