Announcement

Collapse
No announcement yet.

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

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    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
    https://wiki.unrealengine.com/TCP_So...e_Sample)#.cpp

    ~~~

    Picture


    (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

    Code:
    #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]);
    	
    	//Rama's StringFromBinaryArray
    	FString StringFromBinaryArray(const TArray<uint8>& BinaryArray);
    ~~~

    .CPP


    Code:
    #include <string>
    
    void AJoyPCEditor::StartsTheWholeProcess!()
    {
    	if( ! StartTCPReceiver("RamaSocketListener", "127.0.0.1", 8890))
    	{
    		VShow("Could not create the Socket Listener!");
    		return;
    	}
    }
    
    //Rama's Start TCP Receiver
    bool AJoyPCEditor::StartTCPReceiver(
    	const FString& YourChosenSocketName,
    	const FString& TheIP, 
    	const int32 ThePort
    ){
    	//Rama'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[i] = FCString::Atoi( *Parts[i] );
    	}
    	
    	return true;
    }
    
    //Rama'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;	
    }
    
    
    //Rama'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);	
    		}
    	}
    }
    
    //Rama'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());
    }
    
    //Rama'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()));
    	
    	
    	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    	//						Rama'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!

    Code:
    //Rama'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!

    Rama
    Last edited by Rama; 07-14-2014, 03:26 AM.
    UE4 Marketplace: Melee Weapon Plugin & Compressed Binary Save System Plugin | Rama's C++ AI Jumping Videos | Vertex Snap Editor Plugin

    ♥ Rama

    #2
    Always interesting to see how there are sooo many ways to the same solution in programming

    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:
    Code:
    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?
    Last edited by alfalfasprossen; 07-14-2014, 08:36 PM.

    Comment


      #3
      @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

      Code:
      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()

      Code:
      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!



      Rama

      PS: I emailed the author of the python script about sharing it publically
      Last edited by Rama; 07-14-2014, 11:19 PM.
      UE4 Marketplace: Melee Weapon Plugin & Compressed Binary Save System Plugin | Rama's C++ AI Jumping Videos | Vertex Snap Editor Plugin

      ♥ Rama

      Comment


        #4
        yes, you can add it to the wiki, never hurts to have more information in one place
        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

        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


        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

        Comment


          #5
          @Alfalfasprossen,

          ~~~

          I've added you to my wiki tutorial!

          https://wiki.unrealengine.com/TCP_So...AlfalfaProssen
          UE4 Marketplace: Melee Weapon Plugin & Compressed Binary Save System Plugin | Rama's C++ AI Jumping Videos | Vertex Snap Editor Plugin

          ♥ Rama

          Comment


            #6
            Rama, 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

            Comment


              #7
              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!

              Comment


                #8
                Originally posted by pwsnow View Post
                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

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

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

                Rama
                UE4 Marketplace: Melee Weapon Plugin & Compressed Binary Save System Plugin | Rama's C++ AI Jumping Videos | Vertex Snap Editor Plugin

                ♥ Rama

                Comment


                  #9
                  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.

                  Comment


                    #10
                    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

                    Comment


                      #11
                      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

                      Comment


                        #12
                        hi , there
                        i m very new to c++ programming
                        i was trying to implement Rama'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
                        & i couldn't understand this one

                        can u suggest something
                        thanks in advance

                        Comment


                          #13
                          Dear Rama,

                          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.

                          Yours,
                          NoobsDeSroobs

                          Comment


                            #14
                            Originally posted by SimenA View Post
                            Dear Rama,

                            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.
                            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/Garbag...age_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

                            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

                            Good luck!



                            Rama
                            Last edited by Rama; 07-24-2015, 05:01 PM.
                            UE4 Marketplace: Melee Weapon Plugin & Compressed Binary Save System Plugin | Rama's C++ AI Jumping Videos | Vertex Snap Editor Plugin

                            ♥ Rama

                            Comment


                              #15
                              Hi Rama,

                              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

                              Comment

                              Working...
                              X