Communication Between UE4 and a Python UDP Server

All,

I have spent a while searching and learning on how to implement the talking between UE4 and Python. I have used 's code as well as some random ones on the internet to get me where I am. Below is the code I am using to talk to a Python UDP server that will handle all my non game related network code (e.g. Logging into the server and checking user info, sending remember tokens, retrieving saved characters and anything else you can think of that doesn’t rely on the game engine.) I hope this helps.

Python Code

UDPServer.py

You could do the below differently and even put the handler in a different thread.


##################################
## UDP Server to Talk to UE4
##################################

### Imports
import socket
import sys
from UDPRequestHandler import *

HOST = ''
PORT = 8800

# Create the UDP Socket
try:
    socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    #socket.setblocking(false)
    print("Socket Created")
except:
    print("Failed to create socket.")
    sys.exit()

# Bind socket to local host and port
try:
    socket.bind((HOST, PORT))
except:
    print("Bind failed.")
    sys.exit()
    
print("Socket bind complete.")

# Now keep talking to clients

while 1:

    # Receive data from client (data, addr)
    d = socket.recvfrom(1024)
    data = str(d[0], "utf-8")
    addr = d[1]

    # Print to the server who made a connection.
    print("{} wrote:".format(addr))
    print(data)
        
    # Now have our UDP handler handle the data
    myUDPHandler = UDPRequestHandler()
    myResponse = myUDPHandler.handle(data)

    # Respond back
    print(myResponse)
    socket.sendto(bytes(myResponse, 'utf-8'), addr)
    
socket.close()

UDPRequestHandler.py

This is called to handle each connection.


########################
## UDPHandler Class
########################

### Imports
import socketserver
import json
from passlib.hash import pbkdf2_sha256
from UDPPostgreSQLConnector import *

class UDPRequestHandler:

    def checkLogin(self, email, password):

        # This checks to see if the player is who they say they are.
        # If so send back a dict with the players ID and remember_token.

        myDB = UDPPostgreSQLConnector()

        pswdToCheck = pbkdf2_sha256.encrypt(password, rounds=200000,
                                            salt_size=16)
 
        dbResponse = myDB.selectWhere("game.players",
                        "*", "email = '" + email + "'")
        return dbResponse
 
        #if (pbkdf2_sha256.verify(password, dbResponse'password'])):

        #   return "Logged In"
        #else:
        #   return "Invalid Password"

    def handle(self, data):
        myData = json.loads(data)
              

        if myData'Request'] == 'Login':

            ResponseStr = "{'Return': " + self.checkLogin(myData'Email'],
                                                          myData'Password'])
        elif myData'Request'] == 'Test':

            ResponseStr = pbkdf2_sha256.encrypt(myData'Password'], rounds=5000,
                                            salt_size=16)
        else:
            ResponseStr = "I hear you"
                                     
        response = ResponseStr
        
        return response

UDPPostgreSQLConnector.py

This handles all database work. This is basic and will be expanded as needed. But you should get the idea. Also, sensitive info stripped.


#######################
## PostgreSQL Class
#######################

import psycopg2 as mdb
import psycopg2.extras
import sys


class UDPPostgreSQLConnector:

    connection = None         

    def openConnection(self, db):

        conString = "host='999.888.77.6' dbname='game' user='secret' password='secret'"

        # opens a connection to the db
        self.connection = mdb.connect(conString)

    def closeConnection(self):

        #Closes the connection
        self.connection.close()

    def selectWhere(self, table, columns, where):

        # Use like
        #selectWhere("users", "id", "email='lll'")

        try:

            self.openConnection(self.db)

            cur = self.connection.cursor(cursor_factory=mdb.extras.RealDictCursor)

            cur.execute("SELECT " + columns + " FROM " + table +
                        " WHERE " + where) 

            data = cur.fetchone()

            if (data == ""):
                data = "No users."

            cur.close()

        except mdb.DatabaseError as e:

            data = 'Error %s' % e
            #sys.exit(1)

        finally:

            if self.connection:
                 self.closeConnection()

        return data

The python code is all stored on my Ubuntu server and not on my development computer. Below is the different classes and code for my test game.

UE4 C++ Code

UDP Networking Wrapper (H)

This is the main object I use in blueprints to send and receive.


#include "Object.h"
#include "Networking.h"
#include "Json.h"
#include "UDPReceiveWorker.h"
#include "UDPNetworkingWrapper.generated.h"

/**
 * 
 */
UCLASS(BlueprintType, Blueprintable)
class GAME_API UUDPNetworkingWrapper : public UObject
{
	GENERATED_BODY()

public:
	//////////////////////////////////////////////////////////////////////////
	// Construction

	/**
	* Creates and initializes a new UDPNetworking object.
	*
	* @param Description - The description of the socket for debug purposes.
	* @param SenderSocketName - Name of the sender socket for debug purposes.
	* @param TheIP - IP of the the machine you want to send a message too.
	* @param ThePort - The port of the machine you are trying to talk to.
	* @param BufferSize - The size of the buffer for the socket
	* @param AllowBroadCast - Allow broadcasting on this socket?
	* @param Bound - Bind socket to the ip and port?
	* @param Reusable - Is this socket reusable?
	* @param Blocking - Is this socket blocking other data?
	*/
	UFUNCTION(BlueprintPure, Category = "UDPNetworking")
		static UUDPNetworkingWrapper* ConstructUDPWrapper(const FString& Description, const FString& SenderSocketName, const FString& TheIP, const int32 ThePort, const int32 BufferSize,
			const bool AllowBroadcast, const bool Bound, const bool Reusable, const bool Blocking);

	static UUDPNetworkingWrapper* Constructor();

	/**
	* Sends the supplied message
	* @param Message The message to be sent.
	*/
	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
		bool sendMessage(FString Message);

	//////////////////////////////////////////////////////////////////////////
	// Destruction and reset

	/** Destroys all data */
	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
		void UDPDestructor();

	//// Grab Data
	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
		bool anyMessages();


	/** Test Look for message */
	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
		FString GrabWaitingMessage();

	static FString StringFromBinaryArray(const TArray<uint8>&  BinaryArray);
	
private:

	// Holds the socket we are sending on
	FSocket* SenderSocket;

	// Description for debugging
	FString SocketDescription;

	// Remote Address
	FIPv4Endpoint RemoteEndPoint;
	FIPv4Address RemoteAdress;

	// Socket Subsystem
	ISocketSubsystem* SocketSubsystem;

	//UDPReceiveWorker* myRecieverWorker;
	// The data
	TArray<uint8> ReceivedData;

UDP Networking Wrapper (C)

CPP


#include "Game.h"
#include "UDPNetworkingWrapper.h"

UUDPNetworkingWrapper* UUDPNetworkingWrapper::Constructor()
{
	return (UUDPNetworkingWrapper*)StaticConstructObject(UUDPNetworkingWrapper::StaticClass());
}
UUDPNetworkingWrapper* UUDPNetworkingWrapper::ConstructUDPWrapper(const FString& Description, const FString& SenderSocketName, const FString& TheIP, const int32 ThePort, const int32 BufferSize,
	const bool AllowBroadcast, const bool Bound, const bool Reusable, const bool Blocking)
{

	UUDPNetworkingWrapper* wrapper = Constructor();

	wrapper->SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
	FIPv4Address::Parse(TheIP, wrapper->RemoteAdress);

	wrapper->RemoteEndPoint = FIPv4Endpoint(wrapper->RemoteAdress, ThePort);

	// First set our socket null
	wrapper->SenderSocket = nullptr;

	if (wrapper->SocketSubsystem != nullptr) // If socket subsytem is good
	{
		wrapper->SenderSocket = wrapper->SocketSubsystem->CreateSocket(NAME_DGram, *Description, true);

		if (wrapper->SenderSocket != nullptr) // Is our socket created
		{
			// Setup the socket 
			bool Error = !wrapper->SenderSocket->SetNonBlocking(!Blocking) ||
				!wrapper->SenderSocket->SetReuseAddr(Reusable) ||
				!wrapper->SenderSocket->SetBroadcast(AllowBroadcast) ||
				!wrapper->SenderSocket->SetRecvErr();

			if (!Error)
			{
				if (Bound)
				{
					Error = !wrapper->SenderSocket->Bind(*wrapper->RemoteEndPoint.ToInternetAddr());
				}
			}

			if (!Error)
			{
				int32 OutNewSize;

				wrapper->SenderSocket->SetReceiveBufferSize(BufferSize, OutNewSize);
				wrapper->SenderSocket->SetSendBufferSize(BufferSize, OutNewSize);
			}

			if (Error)
			{
				GLog->Logf(TEXT("FUdpSocketBuilder: Failed to create the socket %s as configured"), *Description);

				wrapper->SocketSubsystem->DestroySocket(wrapper->SenderSocket);
				wrapper->SenderSocket = nullptr;
			}
		}

	}


	return wrapper;
}

bool UUDPNetworkingWrapper::sendMessage(FString Message)
{
	if (!SenderSocket) return false;

	int32 BytesSent;
	FTimespan waitTime = FTimespan(10);
	TCHAR *serializedChar = Message.GetCharArray().GetData();
	int32 size = FCString::Strlen(serializedChar);
	int32 sent = 0;

	// Send to
	//myRecieverWorker = UDPReceiveWorker::JoyInit(SenderSocket, waitTime);
	bool success = SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(serializedChar), size, BytesSent, *RemoteEndPoint.ToInternetAddr());
	if (success && BytesSent > 0) // Success
	{
		return true;
	}
	else
	{
		return false;
	}
}

FString UUDPNetworkingWrapper::GrabWaitingMessage()
{
	uint32 Size;

	TSharedRef<FInternetAddr> Sender = SocketSubsystem->CreateInternetAddr();
	
	while (SenderSocket->HasPendingData(Size))
	{
		int32 Read = 0;
		ReceivedData.Init(FMath::Min(Size, 65507u));
		SenderSocket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), Read, *Sender);
	}
	

	return StringFromBinaryArray(ReceivedData);

}

bool UUDPNetworkingWrapper::anyMessages()
{

	uint32 Size;

	if (SenderSocket->HasPendingData(Size))
	{
		return true;
	}
	
	return false;
}

FString UUDPNetworkingWrapper::StringFromBinaryArray(const TArray<uint8>& BinaryArray)
{
	//Create a string from a byte array!
	const std::string cstr(reinterpret_cast<const char*>(BinaryArray.GetData()), BinaryArray.Num());

	//FString can take in the c_str() of a std::string
	return FString(cstr.c_str());

}

void UUDPNetworkingWrapper::UDPDestructor()
{
	SocketSubsystem->DestroySocket(SenderSocket);
	SenderSocket = nullptr;
	SocketSubsystem = nullptr;
	//myRecieverWorker = nullptr;
}

Then in the editor just create an object with the UDPNetworkingWrapper class. Make a call to the constructor passing all the info. Then with the object you can send out messages and make a check for any new messages. Below is my test setup in a pawn blueprint.

  • Josh
3 Likes

Amazing, thanks for sharing!

Thanks for sharing the code. I’ve been working on communication with a master server and UE4 for a while, and this code will help a bunch. I noticed that UDPReceiveWorker.h is included in UDPNetworkingWrapper.h but isn’t posted; could you post it?

Seriously, thanks so much for sharing the code. I’ve managed to start talking with my master server. Up till I found this post I probably spent ~10+ hours trying to make sense out of the internal networking of UE4.

Josh, thank you very much. This was very helpful. It is much more straightforward that the UdpMessaging plugin.

Any references to what may have been contained in UDPReceiveWorker.h seem to be commented out. I commented out this include with no side effects.

No worries glad this helped some people. I honestly spent forever looking as well. As far as the UDPReceiveWorker.h goes, that was something I was working on as to get the handling of receiving messages in another thread so the game does wait for a response. I never could get it to work properly.

UDPReceiveWorker.h


#include "Networking.h"
#include "UDPNetworkingWrapper.h"
#include "NetworkingFunctionLibrary.h"
#include <string>



/**
 * This class holds the multi threading data for receiving UDP packets
 */
class VIRTUALTABLETOP_API UDPReceiveWorker : public FRunnable
{

	// Singleton instance, can access the thread at anytime.
	static UDPReceiveWorker* Runnable;

	// The data
	TArray<uint8>* ReceivedDataRef;

	/** Stop this thread? Uses Thread Safe Counter */
	FThreadSafeCounter StopTaskCounter;

	


public:
	UDPReceiveWorker(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime);
	virtual ~UDPReceiveWorker();



	// Begin FRunnable interface.
	virtual bool Init();
	virtual uint32 Run();
	virtual void Stop();
	// End FRunnable interface

	void EnsureCompletion();

	static UDPReceiveWorker* JoyInit(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime);

	/** Shuts down the thread. Static so it can easily be called from outside the thread context */
	static void Shutdown();

private:

	// Holds the network socket.
	FSocket* Socket;

	// Holds a pointer to the socket sub-system.
	ISocketSubsystem* SocketSubsystem;

	// Holds a flag indicating that the thread is stopping.
	bool Stopping;

	// Holds the thread object.
	FRunnableThread* Thread;

	// Holds the amount of time to wait for inbound packets.
	FTimespan WaitTime;

	
};

UDPReceiverWorker.cpp


include "VirtualTabletop.h"
#include "UDPReceiveWorker.h"

UDPReceiveWorker* UDPReceiveWorker::Runnable = NULL;

UDPReceiveWorker::UDPReceiveWorker(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime)
	: Socket(InSocket)
	, StopTaskCounter(0)
	, WaitTime(InWaitTime)
	, Stopping(false)
{

	const bool bAutoDeleteSelf = false;
	const bool bAutoDeleteRunnable = false;

	// Link the data
	ReceivedDataRef = &ReceivedData;

	SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
	Thread = FRunnableThread::Create(this, TEXT("UDPReceiveWorker"), 0, TPri_BelowNormal); //windows default = 8mb for thread, could specify more

}

UDPReceiveWorker::~UDPReceiveWorker()
{
	delete Thread;
	Thread = NULL;
	SocketSubsystem = nullptr;
	Socket = nullptr;
	ReceivedDataRef = nullptr;
	//myNetworkingWrapper = nullptr;
}

// Init
bool UDPReceiveWorker::Init()
{
	return true;
}

//Run
uint32 UDPReceiveWorker::Run()
{
	TSharedRef<FInternetAddr> Sender = SocketSubsystem->CreateInternetAddr();

	while (!Stopping)
	{
		if (!Socket->Wait(ESocketWaitConditions::WaitForRead, WaitTime))
		{
			continue;
		}

		uint32 Size;

		while (Socket->HasPendingData(Size))
		{

			int32 Read = 0;
			Socket->RecvFrom(ReceivedDataRef->GetData(), ReceivedDataRef->Num(), Read, *Sender);

		}

	}

	

	return 0;
}


// Stop
void UDPReceiveWorker::Stop()
{
	StopTaskCounter.Increment();
}

UDPReceiveWorker* UDPReceiveWorker::JoyInit(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime)
{
	//Create new instance of thread if it does not exist
	//		and the platform supports multi threading!
	if (!Runnable && FPlatformProcess::SupportsMultithreading())
	{
		Runnable = new UDPReceiveWorker(ReceivedData, InSocket, InWaitTime);
	}
	return Runnable;
}

void UDPReceiveWorker::EnsureCompletion()
{
	Stop();
	Thread->WaitForCompletion();
}

void UDPReceiveWorker::Shutdown()
{
	if (Runnable)
	{
		Runnable->EnsureCompletion();
		delete Runnable;
		Runnable = NULL;
	}
}

//
//bool UDPReceiveWorker::IsThreadFinished()
//{
//	if (Runnable) return Runnable->IsFinished();
//	return true;
//}

If you get this working please share with us.

So I am using this code in UE 4.7.6 and I have it working to send out data, but it isn’t receiving data. Any ideas? I have verified that the packets are reaching my computer, but the message waiting function isn’t flagging true.

I have switched to a TCP setup now. Post what your code looks like and I’ll take a look at it.

It’s actually working now, I think it may have just been an issue with cached data or how unreal generated the headers because it just started working after a couple reboots.

Okay cool, glad it’s working.

Is there any way to modify this code to send integers and floats in the UDP packet instead of a string? I just barely understand C++ code enough to keep my head above water, so I wouldn’t know how to change the lines myself.

You can convert strings to ints or floats. You can put many values into a single string using delimiters and using parse string to array.

I think what he means is: sending byte, float, int,etc formats directly instead of a string; for performance reasons - packet size.

I’m communicating with a server that is looking for a specific packet size and an .xml datagram that is parsing specific data types into its structure, so, like mentioned, I need to be able to send floats and ints with a certain packet size for the server to accept the packet.

Hi, I am having the same issue as above… The Unreal - to - client send is working perfectly, but I cannot send data TO UE. Code as above, C# in place of python though. Sending to another c++ programme works fine, but in UE, the ‘anyMessages’ bool is never hit. Has anyone had/solved this ?

Thanks!

First C++ code I’ve used in a poject … but how did you manage to create a constructor within the blueprint? I do not have such an option.

I’m having the same problem - I’ve tried all UDP related samples I could find on the internet, but they all have the same result: no UDP packets being received by Unreal. It’s the same use case as the OP, a python program on the local host sending out packets. Receiving works just fine from a C# program, so the issue is definitely with my Unreal project. Any light shed on the matter would be greatly appreciated!

I have the same problems :frowning:

:smiley: Thanks for share.

You are now my personal hero!
I’ve been looking everywhere to find a way to implement PostgreSQL connectivity into UE4. This seems like it would be a great foundation for what I need for my project.