Home > other >  Reading UDP packets with several clients
Reading UDP packets with several clients

Time:01-31

I have an application installed locally (not developed by me), which broadcasts UDP packets every second. Reading the packets from my application (developed in C in Windows) which also is locally installed, works fine.

WSADATA data;
WORD version = MAKEWORD(2, 2);
int wsOK = WSAStartup(version, &data);

SOCKET serverIn = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in serverHint;
serverHint.sin_addr.S_un.S_addr = INADDR_ANY;
serverHint.sin_family = AF_INET;
serverHint.sin_port = htons(UDP_RECEIVE_PORT);

bind(serverIn, (sockaddr*)&serverHint, sizeof(serverHint));

sockaddr_in client;
int clientSize = sizeof(client);

int RECIEVE_BUFFER_SIZE = 65507;
char* recieveBuffer = new char[RECIEVE_BUFFER_SIZE];

while(updating)
{
    int bytesIn = recvfrom(serverIn, recieveBuffer, RECIEVE_BUFFER_SIZE, 0, (sockaddr*)&client, &clientSize);
}

closesocket(serverIn);
WSACleanup();

But I recently noticed while I was testing some code, while my app was running, that the bind(...) function returned an error code of 10048 (WSAEADDRINUSE). Hence, it seems the first client bound to listen for the UDP packets is the only one who can listen, and the other clients is unable to read the broadcasted UDP packets.

So then I added the SO_REUSEADDR option before calling the bind(...) function to be able to bind successfully to the socket:

BOOL bOptVal = TRUE;
int bOptLen = sizeof(BOOL);
setsockopt((SOCKET)serverIn, SOL_SOCKET, SO_REUSEADDR, (char*)&bOptVal, bOptLen);

That works, but the recvfrom(...) function then does not recieve any data at all! I guess it waits for the other client to close its socket.

Next solution is to initialize the socket with SOCK_RAW instead. The above option SO_REUSEADDR is now not needed, and remove it:

SOCKET serverIn = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

This works, I can read the data now! Though, Windows now requires the adminstrator rights for my application. Also I do recieve the UDP information in the data which I do not need.

Is there any better method to do this without requiring administrator rights, any possibility to discard the header information in the buffer?

CodePudding user response:

Below is a little program I wrote to demonstrate that IPv4 UDP broadcast can and does work as expected under Windows (i.e. without requiring raw-sockets or Administrator privileges).

Run it with the command line argument "server" and it will send out one broadcast UDP packet per second.

Then also run several more instances of the same program, with no command line arguments, to receive the UDP packets and print a line of text to stdout whenever they do. The expected behavior should look like this:

screenshot of expected behavior in several DOS boxes

As for why it's not working for you -- one possible guess is that your UDP-packet-sending program is actually sending out unicast UDP packets rather than broadcast. If that's the case, then I would expect that only one client program would receive packets (even if multiple clients are bound to the same port). A network trace tool like Wireshark might be able to help you determine if the UDP packets being sent are broadcast or unicast.

Anyway, here's the code:

#include <stdio.h>
#include <ws2tcpip.h>

#pragma comment(lib,"WS2_32")

static int BindUDPSocket(SOCKET sock, unsigned short port, bool allowPortSharing)
{
   if (sock == INVALID_SOCKET) return -1;

   if (allowPortSharing)
   {
      const BOOL trueValue = true;
      if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &trueValue, sizeof(trueValue)) < 0) return -1;
   }

   struct sockaddr_in bindAddr; memset(&bindAddr, 0, sizeof(bindAddr));
   bindAddr.sin_family      = AF_INET;
   bindAddr.sin_addr.s_addr = INADDR_ANY;  // aka 0.0.0.0
   bindAddr.sin_port        = htons(port);
   return bind(sock, (struct sockaddr *) &bindAddr, sizeof(bindAddr));
}

int main(int argc, char ** argv)
{
   WSADATA data;
   WORD version = MAKEWORD(2, 2);
   (void) WSAStartup(version, &data);

   const unsigned short TEST_PORT = 12345;

   SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
   if (sock<0) {printf("socket() failed\n"); exit(10);}

   if ((argc > 1)&&(strcmp(argv[1], "server") == 0))
   {
      if (BindUDPSocket(sock, 0, false)<0) {printf("BindUDPSocket() failed for server\n"); exit(10);}

      const BOOL allowBroadcast = true;
      if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *) &allowBroadcast, sizeof(allowBroadcast)) < 0)
      {
         printf("setsockopt(SO_BROADCAST) failed\n");
         exit(10);
      }

      const char buf[] = {0x01, 0x02, 0x03, 0x04};  // dummy data
      struct sockaddr_in toAddr; memset(&toAddr, 0, sizeof(toAddr));
      toAddr.sin_family      = AF_INET;
      toAddr.sin_addr.s_addr = INADDR_BROADCAST;  // aka 255.255.255.255
      toAddr.sin_port        = htons(TEST_PORT);

      printf("Sending outgoing broadcast UDP sockets on port %u, once per second\n", TEST_PORT);
      while(true)
      {
         if (sendto(sock, buf, sizeof(buf), 0, (const sockaddr *) &toAddr, sizeof(toAddr)) == sizeof(buf))
         {
            printf("Sent %zu bytes of broadcast UDP data\n", sizeof(buf));
         }
         else printf("sendto() failed!\n");

         Sleep(1000);  // wait 1 second
      }
   }
   else
   {
      if (BindUDPSocket(sock, TEST_PORT, true)<0) {printf("BindUDPSocket() failed for client\n"); exit(10);}

      printf("Waiting to receive incoming broadcast UDP sockets on port %u\n", TEST_PORT);
      while(true)
      {
         char buf[1024];
         const int ret = recv(sock, buf, sizeof(buf), 0L);
         printf("Received %i bytes of incoming UDP data\n", ret);
      }
   }
}
  • Related