UDP Socket C++ tutorial Updated 2021

So, I’ve spent the last few days digging through tons of posts and troves of documentation to get a working UDP client going. There’s plenty of posts but they all seem to be ancient and usually only address one side of the UDP question, namely sending and not receiving or receiving and not sending. As I’m sure you’ve figured out I’ve figured it out. So I’m going to share my results to update at the very least the forum documentation on UDP send/receive.

This is a mashup of several different posts all who based their original code on Rama’s. Some initialized the socket directly, I opted to use FUdpSocketBuilder to do the initialization. The main problem I was having was that I couldn’t initialize a socket for sending data, eventually I realized I could use the bound socket for receiving also to send because I had in fact configured it for both, just didn’t realize it.

NOTE:
This is designed to be an actor in your world, but will work any way you decide. Create a new C++ class based on actor then just place the actor in your scene.

Build.cs

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class UdptestClient : ModuleRules
{
	public UdptestClient(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "NavigationSystem", "AIModule", "Networking", "Sockets", "InputCore" });
    }
}

udp_module.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Networking.h"
#include "udp_module.generated.h"
#define print(text) if (GEngine) GEngine->AddOnScreenDebugMessage(1, 1.5f, FColor::White, text, false);

UCLASS()
class UDPTESTCLIENT_API Audp_module : public AActor
{
	GENERATED_BODY()
	
public:	
	Audp_module();
	~Audp_module();

	FSocket* Socket;

	// Local Endpoint
	FString SocketDescription = "UDP Listen Socket";
	FIPv4Endpoint LocalEndpoint;
	uint16 LocalPort = 54000;
	int32 SendSize;
	TArray<uint8> ReceivedData;

	// Remote Endpoint
	FIPv4Endpoint RemoteEndpoint;
	FIPv4Address RemoteAddress;
	uint16 RemotePort = 54001;
	int32 BufferSize;
	FString IP = "192.168.50.232";
	//FString IP = "127.0.0.1";

	ISocketSubsystem* SocketSubsystem;

protected:
	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

public:	
	virtual void Tick(float DeltaTime) override;

	void Listen();

	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
		bool sendMessage(FString Message);

	void messageQueue();
};

udp_module.cpp

#include "udp_module.h"
#include <string>

Audp_module::Audp_module()
{
	PrimaryActorTick.bCanEverTick = true;
	Socket = nullptr;
}

Audp_module::~Audp_module()
{
}

void Audp_module::BeginPlay()
{
	Super::BeginPlay();
	SocketSubsystem = nullptr;
	if (SocketSubsystem == nullptr)	SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);

	SendSize = 2 * 1024 * 1024;
	BufferSize = 2 * 1024 * 1024;

	LocalEndpoint = FIPv4Endpoint(FIPv4Address::Any, LocalPort);

	FIPv4Address::Parse(IP, RemoteAddress);
	RemoteEndpoint = FIPv4Endpoint(RemoteAddress, RemotePort);

	Socket = nullptr;

	if (SocketSubsystem != nullptr)
	{
		if (Socket == nullptr)
		{
			Socket = FUdpSocketBuilder(SocketDescription)
				.AsNonBlocking()
				.AsReusable()
				.BoundToEndpoint(LocalEndpoint)
				.WithReceiveBufferSize(SendSize)
				.WithReceiveBufferSize(BufferSize)
				.WithBroadcast();
		}
	}
}

void Audp_module::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
	SocketSubsystem->DestroySocket(Socket);
	Socket = nullptr;
	SocketSubsystem = nullptr;
}

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

	Listen(); // Listen for messages

	FString t = "test222";
	sendMessage(t); // Send Message Test
}

void Audp_module::Listen()
{
	TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	uint32 Size;
	while (Socket->HasPendingData(Size))
	{
		uint8* Recv = new uint8[Size];
		int32 BytesRead = 0;

		ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u));
		Socket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr);

		char ansiiData[1024];
		memcpy(ansiiData, ReceivedData.GetData(), BytesRead);
		ansiiData[BytesRead] = 0;

		FString data = ANSI_TO_TCHAR(ansiiData);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "Message by UDP: " + data);
	}
}

bool Audp_module::sendMessage(FString Message)
{
	if (!Socket) return false;
	int32 BytesSent;

	FTimespan waitTime = FTimespan(10);

	TCHAR* serializedChar = Message.GetCharArray().GetData();
	int32 size = FCString::Strlen(serializedChar);

	bool success = Socket->SendTo((uint8*)TCHAR_TO_UTF8(serializedChar), size, BytesSent, *RemoteEndpoint.ToInternetAddr());
	UE_LOG(LogTemp, Warning, TEXT("Sent message: %s : %s : Address - %s : BytesSent - %d"), *Message, (success ? TEXT("true"):TEXT("false")), *RemoteEndpoint.ToString(), BytesSent);

	if (success && BytesSent > 0) return true;
	else return false;
}

void Audp_module::messageQueue()
{
}

This is a simple C++ server and client that will let you test the send and receive functionality. At the time of writing I haven’t combined them yet into one server but I will once I do some more testing for other things, but as of now both the UE4 client and this pure C++ client/server work together. Expanding will be up to you.

The server and client use Winsock. You can watch Sloan Kelly’s tutorial to get a deeper explanation about it.

Starter UDP Server And Client in C++ - YouTube

server.h

#pragma once
#include "includes.h"

class udp_server
{
	WSADATA data;
	WORD version = MAKEWORD(2, 2);
	int wsOk = WSAStartup(version, &data);
	SOCKET in = socket(AF_INET, SOCK_DGRAM, 0);
	sockaddr_in serverHint;
	sockaddr_in client;
	int clientLength = sizeof(client);

public:
	static const int buffer_size = 2 * 1024 * 1024;
	static const int port = 54001;
private:
	char buffer[buffer_size];

public:
	udp_server();
	~udp_server();
	void init();
	void listen();
	void shutdown(SOCKET in);
};

server.cpp

#include "server.h"

udp_server::udp_server()
{
	init();
}

udp_server::~udp_server() {}

void udp_server::init()
{
	cout << "Initializing Winsock..." << endl;
	if (wsOk != 0)
	{
		cout << "Can't start Winsock! " << wsOk;
		return;
	}
	cout << "Winsock initialized!" << endl;

	serverHint.sin_addr.S_un.S_addr = ADDR_ANY;
	serverHint.sin_family = AF_INET;
	serverHint.sin_port = htons(port);

	cout << "Binding Socket..." << endl;
	if (bind(in, (sockaddr*)&serverHint, sizeof(serverHint)) == SOCKET_ERROR)
	{
		cout << "Issue binding socket! " << WSAGetLastError() << endl;
		return;
	}
	cout << "Socket ready!" << endl;

	listen();

	return;
}

void udp_server::listen()
{
	cout << "Listening on port: " << port << endl;
	while (true)
	{
		ZeroMemory(&client, clientLength);
		ZeroMemory(buffer, buffer_size);

		int bytesIn = recvfrom(in, buffer, buffer_size, 0, (sockaddr*)&client, &clientLength);
		if (bytesIn == SOCKET_ERROR)
		{
			cout << "Error receiving from client " << WSAGetLastError() << endl;
			continue;
		}

		char clientIp[256];
		ZeroMemory(clientIp, 256);

		inet_ntop(AF_INET, &client.sin_addr, clientIp, 256);

		cout << "Message recv from " << clientIp << " : " << buffer << endl;
	}
}

void udp_server::shutdown(SOCKET in)
{
	closesocket(in);
	WSACleanup();
}

Client.cpp

#include "../Server/includes.h"

using namespace std;

void main(int argc, char* argv[]) 
{
	////////////////////////////////////////////////////////////
	// INITIALIZE WINSOCK
	////////////////////////////////////////////////////////////

	// Structure to store the WinSock version. This is filled in
	// on the call to WSAStartup()
	WSADATA data;
	int port = 54001;
	// To start WinSock, the required version must be passed to
	// WSAStartup(). This server is going to use WinSock version
	// 2 so I create a word that will store 2 and 2 in hex i.e.
	// 0x0202
	WORD version = MAKEWORD(2, 2);

	// Start WinSock
	int wsOk = WSAStartup(version, &data);
	if (wsOk != 0)
	{
		// Not ok! Get out quickly
		cout << "Can't start Winsock! " << wsOk;
		return;
	}

	////////////////////////////////////////////////////////////
	// CONNECT TO THE SERVER
	////////////////////////////////////////////////////////////

	// Create a hint structure for the server
	sockaddr_in server;
	server.sin_family = AF_INET; // AF_INET = IPv4 addresses
	server.sin_port = htons(port); // Little to big endian conversion
	inet_pton(AF_INET, "192.168.50.232", &server.sin_addr); // Convert from string to byte array

	// Socket creation, note that the socket type is datagram
	SOCKET out = socket(AF_INET, SOCK_DGRAM, 0);

	// Write out to that socket
	string s(argv[1]);
	int sendOk = sendto(out, s.c_str(), s.size() + 1, 0, (sockaddr*)&server, sizeof(server));

	if (sendOk == SOCKET_ERROR)
	{
		cout << "That didn't work! " << WSAGetLastError() << endl;
	}

	// Close the socket
	closesocket(out);

	// Close down Winsock
	WSACleanup();
}

To use the client you’ll want to compile it and run it through the command line with an argument, that argument will be the data that is sent. cd to the location of the exe and use something like

.\client.exe “Test”

the server should show “Message recv from ... : test”

Good luck guys.

6 Likes

I think you might have a memory leak there in udp_module.cpp. Also I don’t see any usage for the array you created so maybe it’s just a left over from testing

2 Likes

Hey, dude, have you tried to send a longer information to the server? I tested my project today, I was sorry to find that the “recv” function return false after the client sended a buffer which was more than 240 bytes. I modified the length of buffer, from 50 to 400. Lower than 240 always worked, more than 400, sometimes worked, others never worked :face_exhaling: . I’m sure I set the receive buffer size to 1024, but still not work. I hope you can try this and tell me the result. Thank you.

1 Like

I think you might be right. It’s been a couple of years since I looked at this, but I will take a look at it again and update it.

I’m going to look at this again and update it as I’ve built out my server tech, I’ll look at the buffer size when I do and get back to you.

Do you have an example of spinning up a cudp client in unreal? All the examples we have seen are listeners, and cannot recvfrom.

1 Like

So this example should allow you to send and recv to and from the client. If you look in the Tick method you can see I send an FString “test222” using the sendMessage method. I haven’t tested this in a while but if you’re having trouble with it I can setup again and see if this is still working in UE5. It should still work though.