UE4 UDP通信

发表于2018-01-31
评论1 5.9k浏览
本篇文章给大家介绍的是UE4中的UDP通信,用C++创建两个Actor类,分别为UDPSend(发送)   和  UDPRecive(接收)。

代码如下:
UDPSend.h
// Fill out your copyright notice in the Description page of Project Settings.  
#pragma once  
#include "Networking.h"  
#include "GameFramework/Actor.h"  
#include "UDPSend.generated.h"  
UCLASS()  
class UDP_API AUDPSend : public AActor  
{  
    GENERATED_BODY()  
public:   
    // Sets default values for this actor's properties  
    AUDPSend();  
    // Called when the game starts or when spawned  
    virtual void BeginPlay() override;  
    // Called every frame  
    virtual void Tick( float DeltaSeconds ) override;  
public:  
    bool IsUDP;  
    UFUNCTION(BlueprintCallable, Category = "UDP")  
        bool RamaUDPSender_SendString(FString ToSend);  
public:  
    TSharedPtr<FInternetAddr> RemoteAddr;  
    FSocket* SenderSocket;  
    UFUNCTION(BlueprintCallable, Category = "UDP")  
        bool StartUDPSender(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, bool UDP);  
public:  
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UDP")  
        bool ShowOnScreenDebugMessages;  
    //ScreenMsg  
    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:  
    /** Called whenever this actor is being removed from a level */  
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;  
};  

UDPSend.cpp
// Fill out your copyright notice in the Description page of Project Settings.  
#include "UDP.h"  
#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 );  
}  
void AUDPSend::EndPlay(const EEndPlayReason::Type EndPlayReason)  
{  
    Super::EndPlay(EndPlayReason);  
    //~~~~~~~~~~~~~~~~  
    if (SenderSocket) //Clear all sockets!  
    {  
        SenderSocket->Close();  
        ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket);  
    }  
}  
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(Writer.GetData(), Writer.Num(), BytesSent, *RemoteAddr);  
    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;  
    }  
    ScreenMsg("UDP Send Succcess! INFO Sent = ", ToSend);  
    return true;  
}  
bool AUDPSend::StartUDPSender(const FString & YourChosenSocketName, const FString & TheIP, const int32 ThePort, bool UDP)///////////初始化远端IP 发送信息前  
{  
    //FIPv4Endpoint Endpoint(FIPv4Address::Any, 6789);  
    //Create Remote Address.  
    RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();  
    bool bIsValid;  
    RemoteAddr->SetIp(*TheIP, bIsValid);  
    RemoteAddr->SetPort(ThePort);  
    if (!bIsValid)  
    {  
        ScreenMsg("Rama UDP Sender>> IP address was not valid!", TheIP);  
        return false;  
    }  
    SenderSocket = FUdpSocketBuilder(*YourChosenSocketName)  
        .AsReusable()  
        .WithBroadcast()/////////////广播  
        .WithSendBufferSize(2 * 1024 * 1024)  
        //.BoundToEndpoint(Endpoint)  
        ;  
    //check(SenderSocket->GetSocketType() == SOCKTYPE_Datagram);  
    //Set Send Buffer Size  
    int32 SendSize = 2 * 1024 * 1024;  
    SenderSocket->SetSendBufferSize(SendSize, SendSize);  
    SenderSocket->SetReceiveBufferSize(SendSize, SendSize);  
    if (bIsValid)  
    {  
        bIsValid = true;  
    }  
    return bIsValid;  
}  

UDPRecive.h
// Fill out your copyright notice in the Description page of Project Settings.  
#pragma once  
#include "Networking.h"  
#include "GameFramework/Actor.h"  
#include "UDPRecive.generated.h"  
UCLASS()  
class UDP_API AUDPRecive : public AActor  
{  
    GENERATED_BODY()  
public:   
    // Sets default values for this actor's properties  
    AUDPRecive();  
    // Called when the game starts or when spawned  
    virtual void BeginPlay() override;  
    // Called every frame  
    virtual void Tick( float DeltaSeconds ) override;  
public:  
    FSocket* ListenSocket;  
    FUdpSocketReceiver* UDPReceiver = nullptr;  
    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);  
    //ScreenMsg  
    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;   
}; 

UDPRecive.cpp
// Fill out your copyright notice in the Description page of Project Settings.  
#include "UDP.h"  
#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);  
    //~~~~~~~~~~~~~~~~  
    delete UDPReceiver;  
    UDPReceiver = nullptr;  
    //Clear all sockets!  
    //      makes sure repeat plays in Editor dont hold on to old sockets!  
    if (ListenSocket)  
    {  
        ListenSocket->Close();  
        ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenSocket);  
    }  
}  
//Start UDP 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);  
    //Create Socket  
    FIPv4Endpoint Endpoint(FIPv4Address::Any, ThePort);  //所有ip地址本地  
                                                         //FIPv4Endpoint Endpoint(Addr, ThePort);                 //指定ip地址  
    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)  
    {  
        ScreenMsg("No socket");  
        success = false;  
    }  
    if (ListenSocket)  
    {  
        ScreenMsg("The receiver is initialized");  
        success = true;  
    }  
    //return true;  
}  
void AUDPRecive::DataRecv(FString& str, bool& success)              //接收消息处理  
{  
    if (!ListenSocket)  
    {  
        ScreenMsg("No sender socket");  
        success = false;  
        //return success;  
    }  
    TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();  
    TArray<uint8> ReceivedData;//定义一个接收器  
    uint32 Size;  
    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;  
    }  
    //return success;  
}  

最后在Bulid.cs里面增加如下代码:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Sockets", "Networking" });

然后把工程文件关闭重新生成一下,这个是解决Networking报错的方法。

接着用蓝图继承以上两个类,在蓝图里调用C++开放的方法,拖入场景进行实例化。

参考:https://wiki.unrealengine.com/UDP_Socket_Sender_Receiver_From_One_UE4_Instance_To_Another

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论