Sending Data to Python From UE4 via TCP or UDP, partial success requesting help

Dear Friends at Epic,

I have succeed in sending data from python to UE4, and posted that code for the community:

A new, community-hosted Unreal Engine Wiki - Announcements - Unreal Engine Forums,Receive_Binary_Data_From_an_IP/Port_Into_UE4,(Full_Code_Sample)

#Sending Data to Python From UE4

I’ve tried sending data from UE4 to Python, using UDP DGram and TCP Stream

When I use DGram, python gets the message but looks like garbage, and UE4 says 16 bytes were sent (size of FString)

When I use TCP NAME_Stream, UE4 always says that -1 bytes were sent, and python does not get the message.

I am using SOCK_STREAM on the python end of things.

Here is my python TCP code:

from socket import *
import thread

BUFF = 1024
HOST = '127.0.0.1'
PORT = 8890
def response(key):
    return 'Recieved'

def handler(clientsock,addr):
    while 1:
        data = clientsock.recv(BUFF)
        print 'data:' + repr(data)
        if not data: break

if __name__=='__main__':
    ADDR = (HOST, PORT)
    serversock = socket(AF_INET, SOCK_STREAM)
    serversock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    serversock.bind(ADDR)
    serversock.listen(5)
    while 1:
        print 'waiting for connection...'
        clientsock, addr = serversock.accept()
        print '...connected from:', addr
        thread.start_new_thread(handler, (clientsock, addr))

#UE4 Code

Here is my UE4 code where I have the option built in to use UDP or TCP

As I mentioned, TCP version is not working and I have no idea why!

Any ideas?

The socket is valid, but it is always sending -1 bytes!

#Thanks!

Thanks!

Rama

bool ARamaTCPSender::StartTCPSender(
	const FString& YourChosenSocketName,
	const FString& TheIP, 
	const int32 ThePort,
	bool UDP
){
	IsUDP = UDP;
		
	//Create Remote Address.
	RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	
	bool bIsValid;
	RemoteAddr->SetIp(*TheIP, bIsValid);
	RemoteAddr->SetPort(ThePort);
	
	if(!bIsValid)
	{
		ScreenMsg("Rama TCP Sender>> IP address was not valid!", TheIP);
		return false;
	}
	
	//Create Socket
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//						NAME_Stream
	//			 Creates a stream (TCP) socket
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	if(UDP)
	{
		SenderSocket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket( NAME_DGram, TEXT( "RamaTCPSender" ) );
	}
	else
	{
		SenderSocket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket( NAME_Stream, TEXT( "RamaTCPSender" ) );
	}
	
	//Set Send Buffer Size
	int32 SendSize = 2*1024*1024;
	SenderSocket->SetSendBufferSize(SendSize,SendSize);
	SenderSocket->SetReceiveBufferSize(SendSize, SendSize);
	
	if(UDP)
	{
		UE_LOG(Victory,Log,TEXT("\n\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"));
		UE_LOG(Victory,Log,TEXT("Rama ****UDP**** Sender Initialized Successfully!!!"));
		UE_LOG(Victory,Log,TEXT("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n"));
	}
	else
	{
		UE_LOG(Victory,Log,TEXT("\n\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"));
		UE_LOG(Victory,Log,TEXT("Rama TCP Sender Initialized Successfully!!!"));
		UE_LOG(Victory,Log,TEXT("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n"));
	}
	return true;
}
bool ARamaTCPSender::RamaTCPSender_SendString(FString ToSend)
{
	if(!SenderSocket) return false;
	//~~~~~~~~~~~~~~~~
	
	//Add the Line delimiter
	ToSend = TCPSender::LineSeparator + ToSend;
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	int32 BytesSent = 0;
	
	//SEND TO
	SenderSocket->SendTo((uint8*)&ToSend, sizeof(ToSend), BytesSent, *RemoteAddr);
	
	if(BytesSent <= 0)
	{
		const FString Str = "ARamaTCPSender::RamaTCPSender_SendString>>> Socket is valid but the receiver received 0 bytes, make sure it is listening properly!";
		UE_LOG(Victory,Error,TEXT("%s %s"),*Str, *ToSend);
		ScreenMsg(Str);
		
	}
	
	if(IsUDP)
	{
		ScreenMsg("UDP~ Send Succcess! Bytes Sent = ",BytesSent );
	}
	else
	{
		ScreenMsg("TCP~ Send Succcess! Bytes Sent = ",BytesSent );
	}
	return true;
}

#UDP Victory!

I got UDP working!

//SEND TO
	SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(*ToSend), sizeof(ToSend), BytesSent, *RemoteAddr);

I still can’t figure how to make TCP sending from UE4 to Python work though, it always says -1 bytes sent

any ideas?

Rama, I was able to get TCP from UE4 to python working between my game and a different computer running the python code. I am having issues receiving data back. If you wish to see my code just let me know.

v/r

Josh

sure if you’d like to post your code here, great, or you can pm me on the forums!

#:heart:

Rama

Well I take it back. I was able to send and receive using TCP but read that UDP is preferable for networked games so I rewrote my code to use UDP. Thing is, I can’t get data back from my server running python. When I move everything to run on my local machine data transmittal works both to python and back. I am not sure how to post the code all fancy like but I will try.

Below is my c++ in engine. It is very rough, I’m not using classes right and I could easily add multithreading like your tutorial.

FString UNetworkingFunctionLibrary::TestMsgToServer(const FString SocketName, const FString& ip, const int32 port, const FString message)
{

	TSharedRef<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	TSharedRef<FInternetAddr> MyAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	FSocket* SenderSocket;
	int32 BufferSize = 1024;

	bool bIsValid;
	RemoteAddress->SetIp(*ip, bIsValid);
	RemoteAddress->SetPort(port);


	if (!bIsValid)
	{
		/*GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Not Valid IP"));*/
		return FString("IP Not Valid");
	}

	// Create the socket.
	SenderSocket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(FName(NAME_DGram), TEXT("TCP Server Test"));


	SenderSocket->SetSendBufferSize(BufferSize, BufferSize);
	SenderSocket->SetReceiveBufferSize(BufferSize, BufferSize);

	SenderSocket->SetNonBlocking(true);

	//FString serialized = message;
	//TCHAR *serializedChar = serialized.GetCharArray().GetData();

	int32 sent = 0;

	bool successful = SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(*message), sizeof(message), sent, *RemoteAddress);

	if (!successful)
	{
		return FString("Send Not Successful");
		SenderSocket->Close();
	}

	TArray<uint8> ReceivedData;
	TSharedRef<FInternetAddr> ReceivedAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();

	uint32 Size;
	bool bIsDataReceived = false;

	while (!bIsDataReceived)
	{
		if (SenderSocket->HasPendingData(Size))
		{
			ReceivedData.Init(FMath::Min(Size, 65507u));

			int32 Read = 0;
			SenderSocket->RecvFrom(ReceivedData.GetData(), BufferSize, Read, *ReceivedAddr);

			if (Read > 0)
			{
				bIsDataReceived = true;
			}
		}
	}
	
	SenderSocket->Close();

	return StringFromBinaryArray(ReceivedData);


}

FString UNetworkingFunctionLibrary::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());
}

Below is my 3 seperate python scripts that run my server in its very rough test state.

Main Server code

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

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

HOST = 'localhost'
PORT = 8800

# Create the UDP Socket
try:
    socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    print("Socket Created")
except socket.error:
    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 = d[0]
    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()

Handler Class

########################
## TCPHandler 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 = TCPPostgreSQLConnector()

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

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

    def handle(self, data):
        myData = str(data.strip())
              

        if myData == 'Login':

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

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

And my Database code. Import stuff like ips and passwords removed.

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

import psycopg2 as mdb
import psycopg2.extras
import sys


class UDPPostgreSQLConnector:

    connection = None        
    host = 'secret'
    port = secret
    dbUser = 'secret'
    dbPassword = 'secret'
    db = 'secret'

    def openConnection(self, db):

        conString = "host='secret' dbname='secret' 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

Also, if you are using TCP - NAME_Stream - you have to make a connection. So in your code above, for TCP you need it to read:

 bool ARamaTCPSender::RamaTCPSender_SendString(FString ToSend)
{
if(!SenderSocket) return false;
//~~~~~~~~~~~~~~~~
//Add the Line delimiter
ToSend = TCPSender::LineSeparator + ToSend;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
int32 BytesSent = 0;
//SEND TO
SenderSocket->Connect();
SenderSocket->Send((uint8*)TCHAR_TO_UTF8(*ToSend), sizeof(ToSend), BytesSent);
if(BytesSent <= 0)
{
const FString Str = "ARamaTCPSender::RamaTCPSender_SendString>>> Socket is valid but the receiver received 0 bytes, make sure it is listening properly!";
UE_LOG(Victory,Error,TEXT("%s %s"),*Str, *ToSend);
ScreenMsg(Str);
}
if(IsUDP)
{
ScreenMsg("UDP~ Send Succcess! Bytes Sent = ",BytesSent );
}
else
{
ScreenMsg("TCP~ Send Succcess! Bytes Sent = ",BytesSent );
}

SenderSocket->Close();

return true;
}

Hope that helps any. You can also use the SenderSocket->Recv function to receive anything back. You will have to thread that because you will need to loop until you get something. Just like how your python to TCP tutorial talks about.

Thanks for sharing all this code here Lyons!

I will check it out as soon as I can, in the meantime I hope others benefit from it being posted here :slight_smile:

Have fun today!

Rama