2

I'm trying to extend the example from here, to having to services receiving the same message from the same UDP port.

From this question, I understand that I should use SO_REUSEADDR to avoid the error of "address already in use". I have one client sending a "hello" message om port 8080 and two identical services, which simply prints out the received message from the port. SO_REUSEADDR solved the problem of using the same address, however only one of the services receives and prints out the message, while the other keep waiting.

Would it not be possible to have the same message received by both services?

The client:

// Client side implementation of UDP client-server model 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 

#define PORT     8080 
#define MAXLINE 1024 

// Driver code 
int main() { 
    int sockfd; 
    char buffer[MAXLINE]; 
    char *hello = "Hello from client"; 
    struct sockaddr_in     servaddr; 

    // 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; 

    int n, len; 

    sendto(sockfd, (const char *)hello, strlen(hello), 
        MSG_CONFIRM, (const struct sockaddr *) &servaddr,  
            sizeof(servaddr)); 
    printf("Hello message sent.\n"); 

    close(sockfd); 
    return 0; 
} 

One of the two identical services:

// Server side implementation of UDP client-server model 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 

#define PORT     8080 
#define MAXLINE 1024 

// Driver code 
int main() { 
    int sockfd; 
    char buffer[MAXLINE]; 
    char *hello = "Hello from server"; 
    struct sockaddr_in servaddr, cliaddr; 

    // Creating socket file descriptor 
    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
        perror("socket creation failed"); 
        exit(EXIT_FAILURE); 
    } 

    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
        perror("setsockopt(SO_REUSEADDR) failed");

#ifdef SO_REUSEPORT
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) 
        perror("setsockopt(SO_REUSEPORT) failed");
#endif

    memset(&servaddr, 0, sizeof(servaddr)); 
    memset(&cliaddr, 0, sizeof(cliaddr)); 

    // Filling server information 
    servaddr.sin_family    = AF_INET; // IPv4 
    servaddr.sin_addr.s_addr = INADDR_ANY; 
    servaddr.sin_port = htons(PORT); 

    // Bind the socket with the server address 
    if ( bind(sockfd, (const struct sockaddr *)&servaddr,  
            sizeof(servaddr)) < 0 ) 
    { 
        perror("bind failed"); 
        exit(EXIT_FAILURE); 
    } 

    int len, n; 

    len = sizeof(cliaddr);  //len is value/resuslt 

    n = recvfrom(sockfd, (char *)buffer, MAXLINE,  
                MSG_WAITALL, ( struct sockaddr *) &cliaddr, 
                &len); 
    buffer[n] = '\0'; 
    printf("Client : %s\n", buffer); 

    return 0; 
} 
Rasmus
  • 77
  • 7
  • 2
    If you want a UDP packet to be delivered to all of the sockets bound to the destination UDP port, that UDP packet will need to be sent to either a multicast or a broadcast address. Unicast traffic is designed to go to one destination only, so a packet sent to a unicast address will only be handed to one receiving socket. (Actually I'm surprised your UDP packet is received by anyone at all, since it has its destination address set to INADDR_ANY, which is defined to be 0.0.0.0, and is not a valid IPv4 address at all) – Jeremy Friesner May 18 '20 at 14:08
  • Or, if you want multiple of your own servers to receive something you might write a little UDP forwarder that receives these packets and sends them to UNIX sockets for example, that way you can have as many processes behind that all receive the data. – FlorianB May 18 '20 at 14:14
  • 1
    @JeremyFriesner Thank you. Changing to a multicast address made it work. – Rasmus May 18 '20 at 18:47

1 Answers1

1

I think that Sockets act like a queue to your application. Each message received from the network gets placed in that queue, and applications calling recv() or recvfrom() pop messages off of that queue.

The two clients using the same UDP port will share the same queue. I think calling recvfrom() on one client will pop a message off of the queue for that client, and make that message unavailable for the other client.

I think that generally the best approach is to have a one-to-one relationship between clients and sockets.

But, you're curious and really want the message to be available for both clients, you could experiment with passing the MSG_PEEK flag to recvfrom(). That flag changes recvfrom() to not consume the next message from the socket queue, so the other client could also receive it.

GandhiGandhi
  • 1,029
  • 6
  • 10
  • 2
    The `MSG_PEEK` approach would suffer from race conditions -- i.e. another process could (and sometimes would) "steal* the packet away from you before you had a chance to inspect it. – Jeremy Friesner May 18 '20 at 15:46