TCP Socket Listener, Receiving Binary Data into UE4 From a Python Script!

Dear Community,

I have just posted my tutorial and entire code for how you can set up a TCP Socket listener to then receive data from an external program!

In my case I was using a simple python script!

The code I am sharing with you in this wiki tutorial is fully functional!

Wiki Tutorial Link


**Picture**

https://www.mediafire.com/convkey/df72/pi10dce0d3y3bs16g.jpg

(open image in new tab to see in better detail)

Entire CPP Code For You

I had to use** std::string** for one very essential part of this process, which was converting the received binary data into a UE4 FString!


**.H**



```


**#include "Networking.h"**

public:
        FSocket* ListenerSocket;
	FSocket* ConnectionSocket;
	FIPv4Endpoint RemoteAddressForConnection;
	
	bool StartTCPReceiver(
		const FString& YourChosenSocketName,
		const FString& TheIP, 
		const int32 ThePort
	);
	
	FSocket* CreateTCPConnectionListener(
		const FString& YourChosenSocketName,
		const FString& TheIP, 
		const int32 ThePort, 
		const int32 ReceiveBufferSize = 2*1024*1024
	);
	
	//Timer functions, could be threads
	void TCPConnectionListener(); 	//can thread this eventually
	void TCPSocketListener();		//can thread this eventually
	
	
	//Format String IP4 to number array
	bool FormatIP4ToNumber(const FString& TheIP, uint8 (&Out)[4]);
	
	//'s StringFromBinaryArray
	FString StringFromBinaryArray(const TArray<uint8>& BinaryArray);


```



~~~

**.CPP**




```


**#include <string>**

**void AJoyPCEditor::StartsTheWholeProcess!()**
{
	if( ! StartTCPReceiver("RamaSocketListener", **"127.0.0.1"**, **8890**))
	{
		VShow("Could not create the Socket Listener!");
		return;
	}
}

//'s Start TCP Receiver
**bool AJoyPCEditor::StartTCPReceiver(**
	const FString& YourChosenSocketName,
	const FString& TheIP, 
	const int32 ThePort
){
	//'s CreateTCPConnectionListener
	ListenerSocket = CreateTCPConnectionListener(YourChosenSocketName,TheIP, ThePort);
	
	//Not created?
	if(!ListenerSocket)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("StartTCPReceiver>> Listen socket could not be created! ~> %s %d"), *TheIP, ThePort));
		return false;
	}
	
	//Start the Listener! //thread this eventually
	GetWorldTimerManager().SetTimer(this, 
		&AJoyPCEditor::TCPConnectionListener, 0.01, true);	
	
	return true;
}

//Format IP String as Number Parts
**bool AJoyPCEditor::FormatIP4ToNumber(const FString& TheIP, uint8 (&Out)[4])**
{
	//IP Formatting
	TheIP.Replace( TEXT(" "), TEXT("") );

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//						   IP 4 Parts
	
	//String Parts
	TArray<FString> Parts;
	TheIP.ParseIntoArray( &Parts, TEXT("."), true );
	if ( Parts.Num() != 4 )
		return false;

	//String to Number Parts
	for ( int32 i = 0; i < 4; ++i )
	{
		Out* = FCString::Atoi( *Parts* );
	}
	
	return true;
}

//'s Create TCP Connection Listener
**FSocket* AJoyPCEditor::CreateTCPConnectionListener(const FString& YourChosenSocketName,const FString& TheIP, const int32 ThePort,const int32 ReceiveBufferSize)
{**
	uint8 IP4Nums[4];
	if( ! FormatIP4ToNumber(TheIP, IP4Nums))
	{
		VShow("Invalid IP! Expecting 4 parts separated by .");
		return false;
	}
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	//Create Socket
	FIPv4Endpoint Endpoint(FIPv4Address(IP4Nums[0], IP4Nums[1], IP4Nums[2], IP4Nums[3]), ThePort);
	FSocket* ListenSocket = FTcpSocketBuilder(*YourChosenSocketName)
		.AsReusable()
		.BoundToEndpoint(Endpoint)
		.Listening(8);

	//Set Buffer Size
	int32 NewSize = 0;
	ListenSocket->SetReceiveBufferSize(ReceiveBufferSize, NewSize);
	
	//Done!
	return ListenSocket;	
}


//'s TCP Connection Listener
**void AJoyPCEditor::TCPConnectionListener()
{**
	//~~~~~~~~~~~~~
	if(!ListenerSocket) return;
	//~~~~~~~~~~~~~
	
	//Remote address
	TSharedRef<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	bool Pending;

	// handle incoming connections
	if (ListenerSocket->HasPendingConnection(Pending) && Pending)
	{
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//Already have a Connection? destroy previous
		if(ConnectionSocket)
		{
			ConnectionSocket->Close();
			ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ConnectionSocket);
		}
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		
		//New Connection receive!
		ConnectionSocket = ListenerSocket->Accept(*RemoteAddress, TEXT("RamaTCP Received Socket Connection"));

		if (ConnectionSocket != NULL)
		{
			//Global cache of current Remote Address
			RemoteAddressForConnection = FIPv4Endpoint(RemoteAddress);
			
			VShow("Accepted Connection! WOOOHOOOO!!!");
			
			//can thread this too
			GetWorldTimerManager().SetTimer(this, 
				&AJoyPCEditor::TCPSocketListener, 0.01, true);	
		}
	}
}

**//'s String From Binary Array
//This function requires 
//		#include <string>**
**FString AJoyPCEditor::StringFromBinaryArray(const TArray<uint8>& BinaryArray)
{**
	//Create a string from a byte array!
	const std::string cstr( reinterpret_cast<const char*>(BinaryArray.GetData()), BinaryArray.Num() );
	return FString(cstr.c_str());
}

//'s TCP Socket Listener
**void AJoyPCEditor::TCPSocketListener()
{**
	//~~~~~~~~~~~~~
	if(!ConnectionSocket) return;
	//~~~~~~~~~~~~~
	
	
	//Binary Array!
	TArray<uint8> ReceivedData;
	
	uint32 Size;
	while (ConnectionSocket->HasPendingData(Size))
	{
		ReceivedData.Init(FMath::Min(Size, 65507u));

		int32 Read = 0;
		ConnectionSocket->Recv(ReceivedData.GetData(), ReceivedData.Num(), Read);

		//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data Read! %d"), ReceivedData.Num()));
	}
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	if(ReceivedData.Num() <= 0)
	{
		//No Data Received
		return;
	}
	
	VShow("Total Data read!", ReceivedData.Num() );
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data Bytes Read ~> %d"), ReceivedData.Num()));
	
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//						's String From Binary Array
	const FString ReceivedUE4String = StringFromBinaryArray(ReceivedData);
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	
	VShow("As String!!!!! ~>",ReceivedUE4String);	
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("As String Data ~> %s"), *ReceivedUE4String));
}


```



~~~

**The Hardest Part**

The hardest part for me was converting the Python Binary Data received from the TCP socket into a UE4 FString!

Here's the code I wrote to do it!



```


**//'s String From Binary Array
//This function requires 
//		#include <string>**
FString AJoyPCEditor::****StringFromBinaryArray****(const TArray<uint8>& BinaryArray)
{
	//Create a string from a byte array!
	const std::string cstr( reinterpret_cast<const char*>(BinaryArray.GetData()), BinaryArray.Num() );

	return FString(cstr.c_str());
}


```



~~~

Enjoy!

Always interesting to see how there are sooo many ways to the same solution in programming :smiley:

I did something similar, or should I say, the same, but with a very different code build.
You can take a look at my source code here: https://bitbucket.org/m2u/m2uue4plugin/src

I also had the problem of converting the received bytes to an FString, the problem here is that FString is created from TCHAR only, which defaults to wchar_t (Unicode) on most systems, while the default strings in python (<3.0) are ansi. There is no direct constructor for FString from ANSICHAR arrays. Iā€™m using the TStringConvert class which uses FPlatrformString internally to do the conversion from ANSICHAR to TCHAR.
It is amazing, how much stuff there already is to be found in the engine source codes which does helper stuff where one would write clumsy own code sometimes. But pity, it is not very well documented in the code files, I think.

And, for the fun of it, my simple python code to send messages to a tcp port:


import socket

# client code
s=None

def openConnection(TCP_PORT=3939):
    TCP_IP = '127.0.0.1'
    BUFFER_SIZE = 1024
    global s
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((TCP_IP, TCP_PORT))
    

def sendMessage(MESSAGE):
    s.send(MESSAGE)
    #s.close()

def receiveMessage():
    BUFFER_SIZE = 1024
    data = s.recv(BUFFER_SIZE)
    print u"client received data:", data.decode("utf-8")

def closeConnection():
    s.close()


(donā€™t know about the receive part using decode, I didnā€™t try to send unicode back from UE4 to pythonā€¦ yet)
what are you using? what is VRSender? :smiley:

2 Likes

@Alfalfasprossen

Oh wow thank you for sharing your code too with everyone!

This should get everyone going with sockets in UE4 quite easily!

Do you want me to add a link to your bitbucket to my wiki?



My favorite thing from your code base, other than your own personal way of converting the stream to FString as you mentioned, was your use of the *TCPListener* class!

Very nice!


One thing you might want to watch out for, if your module is ever used in conjunction with PIE, is the complete shutting down of the



```

FSocket* Client;

```



I found that in PIE, the socket connection would be held on to by the Editor, but in command prompt running of the game / editor closed this did not occur.

**The result was that if I tried to reconnect to the socket in successive PIE instances, it would only work the first time :)**

My solution was to ensure that I called this, which you'd probably put in your ShutdownModule()



```

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

```



I dont think this will come up in your case but I wanted to mention it for future readers as well as something I encountered and the solution I found.

:)


Thanks again for sharing Alfafasprossen!

:)



PS: I emailed the author of the python script about sharing it publically :)

yes, you can add it to the wiki, never hurts to have more information in one place :wink:
Itā€™s still under constant development, so the code files will be a bit messy over the next time, but I will refactor everything into multiple files when everything works :smiley:

Good to know about the PIE stuff, I didnā€™t try it with a running game, because my Plugin is supposed to be editor-only (a remote control to use Maya or Max for level-building). But if I try to get it working in-game, I already know what to look out for :slight_smile:

Another nice read about using sockets (thatā€™s what got me started) is the LiveEditorListenServer Plugin code. Again, it is only useful if you know how stuff works generally, because it is not documented at all :frowning:

@Alfalfasprossen,


**I've added you to my wiki tutorial!**

https://wiki.unrealengine.com/TCP_Socket_Listener,_Receive_Binary_Data_From_an_IP/Port_Into_UE4,_(Full_Code_Sample)#Another_TCP_Source_Code.2C_by_AlfalfaProssen

, where would you put this code?

In a C++ project of course but a Game Mode or World Settings class via the Add Code option?

Sorry for the newbie question!

Peter

Ok I put the code in a a Actor code, it complies so should work but dont know how to invoke it from UE4.

Iā€™m guessing that putting it in a Actor parent class was the wrong thing to do!

I and anyone else who would want to help needs more info about your current setup, and what youā€™ve done, and what you are trying to do :slight_smile:

Personally I recommend you use a Player Controller class, just to be consistent with my sample code :slight_smile:

Please note you must make sure you are using your new PC class via World Settings and a custom game mode.

My bad, Iā€™m still quite new at using UE4.

Basically I have created a new blank c++ project, added in your code as a new PC. It complies fine.

Iā€™ve followed your instructions to add the PC to the world settings via a custom game node but when I run the project nothing happens.

I should add that my python code is running in the background, Iā€™ve tested my code with another python programme so I know my orginial python code is working :slight_smile:

Then you should try to find out why it is not working, starting from ā€œis everything getting called as i expect it toā€ by printing to the log or on-screen-debug messages. Is you python code registering another socket trying to connect? If not, your c++ code is most likely not getting called. If your code connects, but sending/receiving messages does not work, the problem would be a completely other direction. We canā€™t help you without knowing more precisely what is working and what not :wink:

hi , there
i m very new to c++ programming
i was trying to implement 's code inside a new project so that i can play my game from an andriod device
but getting error for
#include <String>

Info ERROR: The first include statement in source file ā€˜ā€¦\EDU_Projects\Car_CPP\Source\Car_CPP\Car_CPPPawn.cppā€™ is trying to include the file ā€˜Stringā€™ as the precompiled header, but that file could not be located in any of the moduleā€™s include search paths.

i tried this but didnā€™t help :frowning:
& i couldnā€™t understand this one

can u suggest something
thanks in advance

Dear ,

As always you are a great aid to me learning unreal. I followed your tutorial and I managed to connect and send data. For a small while, that is. After about 30-45 seconds the FSocket pointer changes value to 0xdddddddddddd for some reason. I have no idea why. It does not matter what I do to it, it is always the same amount of time. No matter how much I send to or read from the socket, the time is the same. And it is almost always 0xdddddddddddddd. A few times it was 0xdcdcdcdcdcdcdcdcdc, but i have been unable to reproduce it.

Do you have any idea what this could be caused by?

I made the class that controls the server, ANetwork, to inherit from the AGameNetworkManager class. My Custom PlayerController class used by a custom gameMode class controlls the server via function calls to the ANetwork class.

If you want I can send you the source code, but it is pretty much a copy paste of your code.

Hi there!

This sounds like something is getting garbage collected, but I am not sure what! Do you have any UObject* involved in your project that are not UPROPERTY() that might be getting GCā€™ed ?

Wiki on UE4 Garbage Collection
https://wiki.unrealengine.com/Garbage_Collection_%26_Dynamic_Memory_Allocation#Preventing_Garbage_Collection

Other than Garbage Collection I am not aware of anything else that would have such a consistent time out.

I have several clients that use my TCP code, for hours at a time, without issue so there must be something going on in your project that is causing this strange timeout.

It sounds like your ANetwork class is disappearing for some reason, and it contains the FSocket ptr, can you try running all your code from the player controller entirely?

Move all ANetwork code into player controller as a test?

That would help narrow down the issue, as thereā€™s no way the PC would get GCā€™ed somehow or disappear :slight_smile:

Elimate the use of an instanced actor ANetwork (and use only PC) and let me know how it goes!

This is just to test the matter, not saying it is a final solution :slight_smile:

Good luck!

:slight_smile:

Hi ,

thank you for sharing your code.
But I have a few problems. After adding a new c++ class via unreal engine i added your code to the .h and .cpp file but i keep getting the following errors:

1>C:\Users\Max\Documents\Unreal Projects\BlueSocketTest\Source\BlueSocketTest\BlueRamaSocket.h(6): fatal error C1083: Cannot open include file: ā€˜Networking.hā€™: No such file or directory
1>c:\users\max\documents\unreal projects\bluesockettest\source\bluesockettest\BlueRamaSocket.h(6): fatal error C1083: Cannot open include file: ā€˜Networking.hā€™: No such file or directory
1> -------- End Detailed Actions Stats -----------------------------------------------------------
1>ERROR : UBT error : Failed to produce item: C:\Users\Max\Documents\Unreal Projects\BlueSocketTest\Binaries\Win64\UE4Editor-BlueSocketTest-3819.dll

Could you tell me why i have this problem with the include of the networking.h file? please let me know if you need any further information.
lermaster

I think you forgot to include networking module in your Build.cs
Check the wiki article from the first post

Thank you for that point, but I added netwokting and sockets to my build.cs file as shown in the wiki article.

Are you using Intellisense? Mine keep accusing missing includes when they arenā€™t inside the project file (like the Networking.h that is inside Engine folder). It stopped to happen when I changed to Visual Assistā€¦

Guys, Iā€™m trying to reproduce the code, but Iā€™m having trouble building itā€¦ Iā€™m getting the following errors:

&stc=1

Itā€™s a clean project (Iā€™ve created just to test the code). Unreal 4.9 and Iā€™m using a Player Controller class. Any ideas?

edit: Ok, found the issue:
SetTimer usage changed since 4.7 4.7 C++ Transition Guide - C++ - Unreal Engine Forums
ParseIntoArray parameters changed since 4.8: https://answers.unrealengine.com/questions/241371/48-parseintoarray-error.html

Did anyone actually tried this with an IP address other than localhost? Anything different from 127.0.0.1 causes the application to crash hereā€¦ When debugging, the UEditor triggers the first breakpoint after crashing here:

ā€œBox2DIntegration.cppā€

&stc=1

Thatā€™s oddā€¦ Iā€™ve also tried to input the IP directly via FIPv4Address parameters to exclude any issue with the IP address conversion, but the issue persistsā€¦