I revised the code of the client quite a bit, here it is:
/*
==============================================================================
udpSocket.h
Created: 12 Jul 2025 9:09:54pm
Author: Michael Gross
==============================================================================
*/
#pragma once
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
class UdpThread : public juce::Thread, public juce::ChangeBroadcaster
{
#define PORT 8080
#define MAXLINE 16
#define MSG_CONFIRM 0
public:
UdpThread ()
: Thread ("Udp Thread")
{
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
registerToServer();
//implementing polling structure
fds.fd = sockfd;
fds.events = POLLIN;
}
~UdpThread() override
{
// stopThread wait must be longer than polling timeout for thread to finish
stopThread (2000);
close(sockfd);
}
void run() override
{
while (! threadShouldExit())
{
//check if there are new events on socket
poll(&fds, 1, 1000);
if(fds.revents &POLLIN)
{
//empty buffer
memset(buffer, 0, sizeof(buffer));
//if recv fails it will return -1
if (auto n = recv(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL) < 0)
{
perror("receive from server failed");
}
else
{
message = juce::String(buffer, MAXLINE);
//only if server sends rotary data first char is digit, otherwise letter
if (std::isdigit(buffer[0]))
{
buffer[sizeof(buffer) - 1] = '\0';
rotaryValue.store(std::atoi(buffer), std::memory_order_relaxed);
}
else
{
if (message.substring(0, 8) == "ClientId")
{
clientId = message.substring(10).getIntValue();
message = "new ClientId";
}
if (message.substring(0, 12) == "unregistered")
{
clientId = 0;
}
}
sendChangeMessage();
}
}
}
//when thread is exiting
unregisterFromServer();
}
void registerToServer()
{
auto n = sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
//if recv fails it will return -1, continue anyways
if (n < 0) perror("register to server failed");
startThread();
if (clientId == 0)
{
message = "register to server failed";
sendChangeMessage();
}
}
void unregisterFromServer()
{
auto n = sendto(sockfd, (const char *)goodbye, strlen(goodbye), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
//if recv fails it will return -1, continue anyways
if (n < 0) perror("unregister from server failed");
}
int getClientId()
{
return clientId;
}
int getRotaryValue()
{
return rotaryValue.load(std::memory_order_relaxed);
}
juce::String getMessage()
{
return message;
}
private:
int sockfd;
char buffer[MAXLINE];
struct sockaddr_in servaddr;
struct pollfd fds;
const char * hello = "register";
const char * goodbye = "unregister";
std::atomic<int> rotaryValue = 0;
int clientId = 0;
juce::String message;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UdpThread)
};
```