UE4修改自Rama的UDP通信蓝图插件

UE4.15没有提供蓝图UDP的组件,可以在网上找到一个ID叫Rama写的源代码,我把它封装成插件了(MyUdpPlugin),方便在各个UE4版本工程中使用UDP通信。

使用方式:

1、在自己的工程根目录下新建一个Plugins文件夹,将MyUdpPlugin拷贝进去

2、一般情况下需要重新编译一下插件,最终你会得到一个插件的dll

3、新建一个蓝图类。接收UDP信息就选择MyUDPReceiver,发送UDP就选择MyUDPSender,这两个类都继承自Actor

4、将新建的蓝图类拖拽一个实例到关卡蓝图中

5、编辑蓝图。可以使用网络调试助手调试通信是否成功。

关于ListenPort,RemotePort和RemoteIP,如果在工程打包以后想改变,可以手动添加配置文件,在配置文件中修改

 

以下是插件源代码

MyUdpPlugin.uplugin

{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "1.0",
    "FriendlyName": "MyUdpPlugin",
    "Description": "",
    "Category": "Other",
    "CreatedBy": "",
    "CreatedByURL": "",
    "DocsURL": "",
    "MarketplaceURL": "",
    "SupportURL": "",
    "CanContainContent": true,
    "IsBetaVersion": false,
    "Installed": false,
    "Modules": [
        {
            "Name": "MyUdpPlugin",
            "Type": "Runtime",
            "LoadingPhase": "PreLoadingScreen"
        }
    ]
}

MyUdpPlugin.Build.cs

// Some copyright should be here...

using UnrealBuildTool;

public class MyUdpPlugin : ModuleRules
{
    public MyUdpPlugin(TargetInfo Target)
    {
        
        PublicIncludePaths.AddRange(
            new string[] {
                "MyUdpPlugin/Public"
                
                // ... add public include paths required here ...
            }
            );
                
        
        PrivateIncludePaths.AddRange(
            new string[] {
                "MyUdpPlugin/Private",
                
                // ... add other private include paths required here ...
            }
            );
            
        
        PublicDependencyModuleNames.AddRange(
            new string[]
            {
                "Core",
                // ... add other public dependencies that you statically link with here ...
            }
            );
            
        
        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "CoreUObject",
                "Engine",
                "Slate",
                "SlateCore",
                "Sockets", "Networking"
                // ... add private dependencies that you statically link with here ...    
            }
            );
        
        
        DynamicallyLoadedModuleNames.AddRange(
            new string[]
            {
                // ... add any modules that your module loads dynamically here ...
            }
            );
    }
}

MyUdpPlugin.h

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "ModuleManager.h"

class FMyUdpPluginModule : public IModuleInterface
{
public:

    /** IModuleInterface implementation */
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;
};

MyUdpPlugin.cpp

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "MyUdpPlugin.h"

#define LOCTEXT_NAMESPACE "FMyUdpPluginModule"

void FMyUdpPluginModule::StartupModule()
{
    // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
    
}

void FMyUdpPluginModule::ShutdownModule()
{
    // This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
    // we call this function before unloading the module.
    
}

#undef LOCTEXT_NAMESPACE
    
IMPLEMENT_MODULE(FMyUdpPluginModule, MyUdpPlugin)

MyUDPReceiver.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Networking.h"
//#include "AnyCustomData.h"//如果发送、接收方都是UE4,可以使用FArchive对传输数据进行序列化与反序列化
#include "GameFramework/Actor.h"
#include "Engine.h"
#include "MyUDPReceiver.generated.h"

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

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

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

    /** Data has been received!! */
    UFUNCTION(BlueprintImplementableEvent, Category = "MyUDPSender")
    void DataReceived(const TArray<uint8>& rawData, const FString& tryToString);
    
    FSocket* ListenSocket;

    FUdpSocketReceiver* UDPReceiver = nullptr;
    void Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt);

    /*
    * Trying read ListenPort parameter from GameIni config, if ListenPort!=0,
    * the parameter ListenPort will be overridden.
    * You can config them in YourProject\Config\DefaultGame.ini or
    * YourPackagedProject\Saved\Config\WindowsNoEditor\Game.ini.
    * [MyUDP]
    * ListenPort=2016
    */
    UFUNCTION(BlueprintCallable, Category = "MyUDPSender")
    bool StartUDPReceiver(const FString& YourChosenSocketName, int32 ListenPort = 2017);

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyUDPSender")
    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 int32 Value)
    {
        if (!ShowOnScreenDebugMessages) return;
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d"), *Msg, Value));
    }
    FORCEINLINE void ScreenMsg(const FString& Msg, const int32 Value, const FString& Msg2)
    {
        if (!ShowOnScreenDebugMessages) return;
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d %s"), *Msg, Value, *Msg2));
    }
    /** Called whenever this actor is being removed from a level */
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};

MyUDPReceiver.cpp

#include "MyUdpPlugin.h"

#include "MyUDPReceiver.h"
#include <string>

// Sets default values
AMyUDPReceiver::AMyUDPReceiver()
{
     // 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;
    ShowOnScreenDebugMessages = true;
}

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

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

}

void AMyUDPReceiver::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);

    delete UDPReceiver;
    UDPReceiver = nullptr;

    //Clear all sockets!
    //makes sure repeat plays in Editor don't hold on to old sockets!
    if (ListenSocket)
    {
        ListenSocket->Close();
        ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenSocket);
    }
}

//Start UDP Receiver
bool AMyUDPReceiver::StartUDPReceiver(const FString& YourChosenSocketName, int32 ListenPort)
{
    int32 listenPortFromGameIni = 0;
    GConfig->GetInt(TEXT("MyUDP"), TEXT("ListenPort"), listenPortFromGameIni, GGameIni);
    //ScreenMsg(GGameIni);
    ScreenMsg("ListenPort From Game.ini: [MyUDP] ListenPort=", listenPortFromGameIni);
    if (listenPortFromGameIni!=0)
    {
        ListenPort = listenPortFromGameIni;
    }

    //Create Socket
    FIPv4Endpoint Endpoint(FIPv4Address::Any, ListenPort);

    //BUFFER SIZE
    int32 BufferSize = 2 * 1024 * 1024;

    ListenSocket = FUdpSocketBuilder(*YourChosenSocketName)
        .AsNonBlocking()
        .AsReusable()
        .BoundToEndpoint(Endpoint)
        .WithReceiveBufferSize(BufferSize);
    ;

    FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
    UDPReceiver = new FUdpSocketReceiver(ListenSocket, ThreadWaitTime, TEXT("UDP RECEIVER"));
    UDPReceiver->OnDataReceived().BindUObject(this, &AMyUDPReceiver::Recv);

    UDPReceiver->Start();

    ScreenMsg("UDP Receiver Initialized Successfully!!!");
    return true;
}

void AMyUDPReceiver::Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt)
{
    //FAnyCustomData Data;
    //*ArrayReaderPtr << Data;        //Now de-serializing! See AnyCustomData.h
    //BPEvent_DataReceived(Data);    //BP Event

    int32 dataByteNum = ArrayReaderPtr->Num();
    TArray<uint8> ReceivedData;
    for (int i = 0; i < dataByteNum; i++)
    {
        uint8 tmp;
        *ArrayReaderPtr << tmp;
        ReceivedData.Add(tmp);
    }
    ReceivedData.Add('\0');
    FString tryToString(reinterpret_cast<const char*>(ReceivedData.GetData()));
    ReceivedData.RemoveSingle('\0');
    ScreenMsg("Received from " + EndPt.ToString() + ", Received bytes = ", dataByteNum, ", Received String =" + tryToString);
    DataReceived(ReceivedData, tryToString);
}

MyUDPSender.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Networking.h"
#include "GameFramework/Actor.h"
#include "MyUDPSender.generated.h"

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

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

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

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

    TSharedPtr<FInternetAddr> RemoteAddr;
    FSocket* SenderSocket;

    UFUNCTION(BlueprintCallable, Category = "MyUDPSender")
    bool SendString(FString ToSend);
    
    /*
    * Trying read parameters from GameIni config, if RemoteIP!="" or RemotePort!=0,
    * the two parameters(RemoteIP and RemotePort) will be overridden.
    * You can config them in YourProject\Config\DefaultGame.ini or
    * YourPackagedProject\Saved\Config\WindowsNoEditor\Game.ini.
    * [MyUDP]
    * RemoteIP=192.168.1.117
    * RemoterPort=2016
    */
    UFUNCTION(BlueprintCallable, Category = "MyUDPSender")
    bool StartUDPSender(const FString& YourChosenSocketName, 
        FString RemoteIP="127.0.0.1",int32 RemotePort=2017);

    //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 int32 Value)
    {
        if (!ShowOnScreenDebugMessages) return;
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d"), *Msg, Value));
    }
    FORCEINLINE void ScreenMsg(const FString& Msg, const int32 Value, const FString& Msg2)
    {
        if (!ShowOnScreenDebugMessages) return;
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("%s %d %s"), *Msg, Value, *Msg2));
    }

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

MyUDPSender.cpp

#include "MyUdpPlugin.h"

//#include "AnyCustomData.h"//如果发送、接收方都是UE4,可以使用FArchive对传输数据进行序列化与反序列化
#include "MyUDPSender.h"
#include <string>
#include "StringConv.h"

// Sets default values
AMyUDPSender::AMyUDPSender()
{
     // 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 AMyUDPSender::BeginPlay()
{
    Super::BeginPlay();
    
}

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

}

void AMyUDPSender::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);

    if (SenderSocket)
    {
        SenderSocket->Close();
        ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket);
    }
}

bool AMyUDPSender::StartUDPSender(
    const FString& YourChosenSocketName,
    FString RemoteIP /*= "127.0.0.1"*/, int32 RemotePort /*= 2017*/
    )
{
    FString remoteIPFromGameIni = TEXT("");
    int32 remotePortFromGameIni = 0;
    GConfig->GetString(TEXT("MyUDP"), TEXT("RemoteIP"), remoteIPFromGameIni, GGameIni);
    GConfig->GetInt(TEXT("MyUDP"), TEXT("RemotePort"), remotePortFromGameIni, GGameIni);
    //ScreenMsg(GGameIni);
    ScreenMsg("RemoteAddress From Game.ini: [MyUDP] RemoteIP="+ remoteIPFromGameIni +", RemotePort=", remotePortFromGameIni);
    if (!remoteIPFromGameIni.IsEmpty())
    {
        RemoteIP = remoteIPFromGameIni;
    }
    if (remotePortFromGameIni!=0)
    {
        RemotePort = remotePortFromGameIni;
    }

    //Create Remote Address.
    RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();

    bool bIsValid;
    RemoteAddr->SetIp(*RemoteIP, bIsValid);
    RemoteAddr->SetPort(RemotePort);

    if (!bIsValid)
    {
        ScreenMsg("IP address was not valid!" + RemoteIP);
        return false;
    }

    SenderSocket = FUdpSocketBuilder(*YourChosenSocketName)
        .AsReusable()
        .WithBroadcast()
        ;

    //check(SenderSocket->GetSocketType() == SOCKTYPE_Datagram);

    //Set Send Buffer Size
    int32 SendSize = 2 * 1024 * 1024;
    SenderSocket->SetSendBufferSize(SendSize, SendSize);
    SenderSocket->SetReceiveBufferSize(SendSize, SendSize);

    ScreenMsg("UDP Sender Initialized Successfully!!!");
    return true;
}

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

    int32 BytesSent = 0;

    //FAnyCustomData NewData;
    //NewData.Scale = FMath::FRandRange(0, 1000);
    //NewData.Count = FMath::RandRange(0, 100);
    //NewData.Color = FLinearColor(FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), 1);
    //FArrayWriter Writer;
    //Writer << NewData; //Serializing our custom data, thank you UE4!
    //SenderSocket->SendTo(Writer.GetData(), Writer.Num(), BytesSent, *RemoteAddr);

    TCHAR* pSendData = ToSend.GetCharArray().GetData();
    int32 nDataLen = FCString::Strlen(pSendData);
    uint8* dst = (uint8*)TCHAR_TO_UTF8(pSendData);
    SenderSocket->SendTo(dst, nDataLen, BytesSent, *RemoteAddr);

    if (BytesSent <= 0)
    {
        const FString Str = "Socket is valid but the sender sent 0 bytes!";
        UE_LOG(LogTemp, Error, TEXT("%s"), *Str);
        ScreenMsg(Str);
        return false;
    }

    ScreenMsg("UDP Send Success! Bytes Sent = ", BytesSent, ", ToSend String =" + ToSend+", To "+ RemoteAddr->ToString(true));

    return true;
}

AnyCustomData.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
#include "AnyCustomData.generated.h"

USTRUCT(BlueprintType)
struct FAnyCustomData
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color")
    FString Name = "Victory!";

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color")
    int32 Count = 1;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color")
    float Scale = 1.f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Joy Color")
    FLinearColor Color = FLinearColor::Red;

    FAnyCustomData() {};
    ~FAnyCustomData() {};
};
FORCEINLINE FArchive& operator<<(FArchive &Ar, FAnyCustomData& TheStruct)
{
    Ar << TheStruct.Name;
    Ar << TheStruct.Count;
    Ar << TheStruct.Scale;
    Ar << TheStruct.Color;

    return Ar;
}

 

posted @ 2017-07-13 11:28  酷熊  阅读(6105)  评论(0编辑  收藏  举报