Local UDP broadcast with FSocket

Hi,

I’d like to allow my Android application to automatically connect to my game on a local network.
In order to do this I am trying to broadcast the host ip address on the local network using an udp FSocket.

I’m testing it between two computers on the same network. I can see the packets coming with Wireshark but I fail to catch them in unreal engine.
I tried several things:

  1. Creating an UDPReceiver with a callback. With this solution the callback is never called.

bool AUdpDataReceiver::StartUDPReceiver(const FString& YourChosenSocketName, const int32 ThePort)
{
	int32 BufferSize = 2 * 1024 * 1024;

	ListenSocket = FUdpSocketBuilder(*YourChosenSocketName)
		.AsNonBlocking()
		.AsReusable()
		.BoundToAddress(FIPv4Address::Any)
		.BoundToPort(ThePort)
		.WithReceiveBufferSize(BufferSize)
		.Build();

	FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
	UDPReceiver = new FUdpSocketReceiver(ListenSocket, ThreadWaitTime, TEXT("UDP RECEIVER"));
	UDPReceiver->OnDataReceived().BindUObject(this, &AUdpDataReceiver::Recv);
	UDPReceiver->Start();
	return true;
}

void AUdpDataReceiver::Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt)
{
	FString data;
	ScreenMsg("Recv data");
	*ArrayReaderPtr << data;		//Now de-serializing
	ScreenMsg(data);
	UE_LOG(LogTemp, Warning, TEXT("DATADATADATA %s"), *data);
}

I tried this solution without BoundToAddress(FIPv4Address::Any) and with WithBroadcast().

  1. Calling Recv or RecvFrom in Tick to catch the data.

void AUdpDataReceiver::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

	int bytesRead;
	int32 BufferSize = 2 * 1024 * 1024;
	uint8* data = nullptr;
	TSharedRef < FInternetAddr > addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	addr->SetAnyAddress();
	ListenSocket->RecvFrom(data, BufferSize, bytesRead, *addr);
	ScreenMsg("DATADATADATA",(float) bytesRead);
	UE_LOG(LogTemp, Warning, TEXT("DATADATADATA %f"), (float)bytesRead);
}

bool AUdpDataReceiver::StartUDPReceiver(const FString& YourChosenSocketName, const int32 ThePort)
{
	int32 BufferSize = 2 * 1024 * 1024;

	ListenSocket = FUdpSocketBuilder(*YourChosenSocketName)
		.AsNonBlocking()
		.AsReusable()
		.BoundToAddress(FIPv4Address::Any)
		.BoundToPort(ThePort)
		.WithReceiveBufferSize(BufferSize)
		.Build();

return true;

}

I tried this solution with the alternative ways of creating the sockets I explained before.

I tried everything I could think of but never seem to receive anything, did I miss something?

HI ,
It’s not mandatory use 2 computers for debugging, you can test with your own, by connecting sockets with self connection 127.0.0.1
I made that example some time ago, I connect my little toy car with arduino and my cel phone and yeah!!, I use UDP sockets.

take a look:

I create a simple class inheriting from UActorComponent with few methods inside, I share you:

my header:



#pragma once
#include "Networking.h"
#include "Engine.h"
#include "SocketPower.Generated.h"

USTRUCT(BlueprintType)
struct FAnyCustomData
{
	GENERATED_USTRUCT_BODY()

		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DATA")
		float motor = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DATA")
		float servo = 0;

	FAnyCustomData()
	{}
};

FORCEINLINE FArchive& operator<<(FArchive &Ar, FAnyCustomData& TheStruct)
{
	Ar << TheStruct.motor;
	Ar << TheStruct.servo;
	return Ar;
}

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGetReceivedData, const FAnyCustomData&, receivedData);

UCLASS(meta = (BlueprintSpawnableComponent), ClassGroup = (Hardware))
class USocketPower : public UActorComponent
{
	GENERATED_BODY()

private:
	bool isServer = false;
	FSocket* socket;
	FUdpSocketReceiver* UDPReceiver = nullptr;
	FAnyCustomData dataRev;

public:

	TSharedPtr<FInternetAddr>	RemoteAddr;

	UPROPERTY(BlueprintAssignable)
		FGetReceivedData OnSuccess;

	UFUNCTION(BlueprintPure, Category = "Internet")
		FAnyCustomData getDataRev();

	UFUNCTION(BlueprintCallable, Category = "Internet")
		bool sendData(FAnyCustomData data);

	UFUNCTION(BlueprintCallable, Category = "Internet")
		bool tryToConnect(FString socketname, FString IP, int32 port, bool UDP = false);

	UFUNCTION(BlueprintCallable, Category = "Internet")
		bool startServer(FString socketname, FString IP, int32 port);

	UFUNCTION(BlueprintCallable, Category = "Internet")
		void closeConnection();

	void Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt);

};


my cpp:



#include "SocketFuncPrivatePCH.h"
#include "SocketPower.h"

bool USocketPower::startServer(FString socketname, FString IP, int32 port)
{
	FIPv4Address Addr;
	FIPv4Address::Parse(IP, Addr);

	//Create Socket
	FIPv4Endpoint Endpoint(Addr, port);

	//BUFFER SIZE
	int32 BufferSize = 2 * 1024 * 1024;

	socket = FUdpSocketBuilder(*socketname)
		.AsNonBlocking()
		.AsReusable()
		.BoundToEndpoint(Endpoint)
		.WithReceiveBufferSize(BufferSize);
	;

	FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
	UDPReceiver = new FUdpSocketReceiver(socket, ThreadWaitTime, TEXT("UDP RECEIVER"));
	UDPReceiver->OnDataReceived().BindUObject(this, &USocketPower::Recv);

	if (socket)
	{
		isServer = true;
	}
	return isServer;
}

bool USocketPower::tryToConnect(FString socketname, FString IP, int32 port, bool UDP)
{
	RemoteAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	bool bIsValid;
	RemoteAddr->SetIp(*IP, bIsValid);
	RemoteAddr->SetPort(port);

	if (!bIsValid)
	{
		GEngine->AddOnScreenDebugMessage(-1,3,FColor::Red,"IP address was not valid!");
		return false;
	}

	socket = FUdpSocketBuilder(*socketname).AsReusable().WithBroadcast();
	//Set Send Buffer Size
	int32 SendSize = 2 * 1024 * 1024;
	socket->SetSendBufferSize(SendSize, SendSize);
	socket->SetReceiveBufferSize(SendSize, SendSize);

	if (socket)
	{
		return true;
	}

	return false;
}

bool USocketPower::sendData(FAnyCustomData data)
{
	if (!socket || isServer)
	{
		GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Red, "no sender socket");
		return false;
	}

	FArrayWriter Writer;
	Writer << data;
	int32 BytesSent = 0;
	socket->SendTo(Writer.GetData(), Writer.Num(), BytesSent, *RemoteAddr);
	if (BytesSent <= 0)
	{
		GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Red, "invalid send");
		return false;
	}

	return true;
}

void USocketPower::closeConnection()
{
	if (UDPReceiver)
	{
		delete UDPReceiver;
		UDPReceiver = nullptr;
	}

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

void USocketPower::Recv(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt)
{
	FAnyCustomData Data;
	*ArrayReaderPtr << Data;
	FAnyCustomData copy;
	copy.motor = Data.motor;
	copy.servo = Data.servo;
	dataRev = copy;
}

FAnyCustomData USocketPower::getDataRev()
{
	return dataRev;
}


It’s super simple, I think is enough clear, but if you have some troubles, let me know

Cheers!

Hi ZkarmaKun

Thanks a lot for sharing your code, it really helps to have another example.
I do not see anything wrong comparing your code with mine. Can you tell which ip you give to your socket in your startServer function?

Also using two computers isn’t a problem and as I said I do receive the packets on the client, so I should be able to catch them. I can let the server run I don’t have to change anything on it :slight_smile:

Well in my example I hardcode my IP by setting in my modem, Really not matter at all, when you start a server just pass the IPv4 of the server, for example use 192.168.1.XX and all the clients need to connect to the same IP

Well my problem is that I want to broadcast on my local network, not just set an ip address and bind the sockets. I think the problem I have is when I try to receive any udp packet on a specific port, no matter where it comes from.

UDP packets are just targeted at an IP addr, not a port. In general when you want to send a packet to everyone in the local network you set the broadcast to true.

Yes I did that. I can see the packets coming on my computer with wireshark so the broadcast works. I just can’t get the data on the receiver part of the code. HasPendingData returns false everytime and the registered callback is never called.

can you share the blueprint of this code???