I noticed a couple of things about your code:
- You define and parse an IP address, but you don’t use it to configure your TCP socket or listener.
- You create an FTcpListener to wrap around the raw socket, but you don’t make use of it anywhere. Rather, you proceed to poll and read from the raw socket.
The whole point of the FTcpListener class is to hide the complexities of dealing with raw sockets and background threads, and allow you to simply deal with incoming connections if and when they come. FTcpListener can take care of creating the socket, so you don’t have to do that yourself, and will create a background thread that polls the socket for incoming connections. All you need to do is register a function that handles incoming connections and then reads & processes the data from a single connection.
Here’s an example that I tested and that worked for me. I put this inside a generic AMyActor example class, so you’ll have to adapt the code to your situation:
Header
#include "Networking.h"
#include "MyActor.generated.h"
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
bool HandleConnectionAccepted(FSocket* Socket, const FIPv4Endpoint& Endpoint);
FTcpListener* MyListener;
};
Source
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// Accept connections from any remote IP address. Change this to FIPv4Address::InternalLoopback if you only want to accept connections from the same machine.
FIPv4Address ip = FIPv4Address::Any;
uint16 port = 8888;
// Create a TCP listener and configure it with an endpoint. This way, the TcpListener will take care of creating and destroying the underlying socket.
FIPv4Endpoint Endpoint(ip, port);
MyListener = new FTcpListener(Endpoint);
// Bind a function to handle incoming TCP connections and read data from the socket.
// We're using BindUObject here because this is being called from a UObject-derived class. Use BindRaw when calling this from a non-UObject class.
MyListener->OnConnectionAccepted().BindUObject(this, &AMyActor::HandleConnectionAccepted);
UE_LOG(LogTemp, Log, TEXT("TCP server listening at: %s"), *Endpoint.ToString());
}
void AMyActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Make sure we stop the TCP listener and its background thread when ending the game.
if (MyListener != nullptr)
{
MyListener->OnConnectionAccepted().Unbind();
delete MyListener;
MyListener = nullptr;
}
Super::EndPlay(EndPlayReason);
}
bool AMyActor::HandleConnectionAccepted(FSocket* Socket, const FIPv4Endpoint& Endpoint)
{
uint32 DataSize;
uint8 Data[1000];
int32 BytesRead;
UE_LOG(LogTemp, Log, TEXT("Connection accepted from %s"), *Endpoint.ToString());
// Keep reading until there is no more data left on the socket
while (Socket->HasPendingData(DataSize))
{
UE_LOG(LogTemp, Log, TEXT("TCP socket has %d bytes of pending data"), DataSize);
// Read a single chunk of data from the socket
if (Socket->Recv(Data, sizeof(Data), BytesRead))
{
UE_LOG(LogTemp, Log, TEXT("Received %d bytes from %s"), BytesRead, *Endpoint.ToString());
// Convert the raw byte data in UTF-8 encoding to an FString, if that's what you want to do
FUTF8ToTCHAR Converted(reinterpret_cast<const ANSICHAR*>(Data), BytesRead);
UE_LOG(LogTemp, Log, TEXT("Received string: %s"), *FString(Converted.Length(), Converted.Get()));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Error receiving data from TCP socket!"));
}
}
return true;
}