Skip to main content
included explaining comment
Source Link

I revised the code of the client quite a bitbit;

apart from the obvious (documentation, here iterror checking, some formal issues) the main improvement is in the threads run-method: instead of calling recv every time in the while loop, now I call it only, when there is actually new data, as poll tells me if there's a new event.

I revised the code of the client quite a bit, here it is:

I revised the code of the client quite a bit;

apart from the obvious (documentation, error checking, some formal issues) the main improvement is in the threads run-method: instead of calling recv every time in the while loop, now I call it only, when there is actually new data, as poll tells me if there's a new event.

added 2 characters in body
Source Link
toolic
  • 15.8k
  • 6
  • 29
  • 217
/*
  ==============================================================================

    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)
};
```
/*
  ==============================================================================

    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)
};
```
/*
  ==============================================================================

    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)
};
Source Link

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)
};
```