Wallace
Wallace

Reputation: 293

C++ Receiving 2 or more UDP Messages at same time

I am trying to receive UDP messages in my main.cxx. I have created a UDP server method getUdp(char *buffer) to listen for incoming UDP messages in my while(true) infinite loop.

Here is the problem that I am facing. This UDP server is able to listen for one message at one time. When two or more UDP messages come at the same time, it is not queued into the buffer. I figured it is because the socket is everytime the method is called in the infinite loop, the getUdp() method opens a socket, gets the message and closes the socket, resulting in the server not being able to queue the messages.

How am I able to tweak this code to receive 2 or more UDP messages? Appreciate any advice.

Thanks.

UdpServer.cpp

#include <stdio.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <winsock.h>
#include <time.h>

#include "UdpServer.h"
#include "stdafx.h"

using namespace std;

#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 4096

void getUDP(char *buffer);

void UDPServer::MyUDP::getUDP(char *buffer)
{
    WSADATA w;                          /* Used to open windows connection */
    int client_length;                  /* Length of client struct */
    int bytes_received;                 /* Bytes received from client */
    SOCKET sd;                          /* Socket descriptor of server */
    struct sockaddr_in server;          /* Information about the server */
    struct sockaddr_in client;          /* Information about the client */
    struct hostent *hp;                 /* Information about this computer */
    char host_name[256];                /* Name of the server */
    time_t current_time;                /* Current time */

    /* Open windows connection */
    if (WSAStartup(0x0101, &w) != 0)
    {
        fprintf(stderr, "Could not open Windows connection.\n");
    }

    /* Open a datagram socket */
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd == INVALID_SOCKET)
    {
        fprintf(stderr, "Could not create socket.\n");
        WSACleanup();
    }

    /* Clear out server struct */
    memset((void *)&server, '\0', sizeof(struct sockaddr_in));

    /* Set family and port */
    server.sin_family = AF_INET;
    server.sin_port = htons(11000);

    /* Set address automatically if desired */
    /* Get host name of this computer */
    gethostname(host_name, sizeof(host_name));
    hp = gethostbyname(host_name);

    /* Check for NULL pointer */
    if (hp == NULL)
    {
        fprintf(stderr, "Could not get host name.\n");
        closesocket(sd);
        WSACleanup();
    }

    unsigned int a = 127;
    unsigned int b = 0;
    unsigned int c = 0;
    unsigned int d = 1;

    /* Assign the address */
    server.sin_addr.S_un.S_un_b.s_b1 = a;
    server.sin_addr.S_un.S_un_b.s_b2 = b;
    server.sin_addr.S_un.S_un_b.s_b3 = c;
    server.sin_addr.S_un.S_un_b.s_b4 = d;

    /* Bind address to socket */
    if (bind(sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1)
    {
        fprintf(stderr, "Could not bind name to socket.\n");
        closesocket(sd);
        WSACleanup();
    }

    /* Print out server information */
    printf("Server running on %u.%u.%u.%u\n", (unsigned char)server.sin_addr.S_un.S_un_b.s_b1,
                                                (unsigned char)server.sin_addr.S_un.S_un_b.s_b2,
                                                (unsigned char)server.sin_addr.S_un.S_un_b.s_b3,
                                                (unsigned char)server.sin_addr.S_un.S_un_b.s_b4);
    printf("Press CTRL + C to quit\n");

    /* Loop and get data from clients */
    client_length = (int)sizeof(struct sockaddr_in);

    /* Receive bytes from client */
    bytes_received = recvfrom(sd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client, &client_length);

    if (bytes_received < 0)
    {
        fprintf(stderr, "Could not receive datagram.\n");
        closesocket(sd);
        WSACleanup();
    }

    current_time = time(NULL);
    closesocket(sd);
    WSACleanup();
}

main.cxx

int main(int argc, char** argv)
{
    while(true)
    {
        //! wait for UDP message
        UDPServer::MyUDP myudp;
        myudp.getUDP(buffer);

        if(buffer[0] != 0)
        {
            string udpMsg(buffer);

            if(udpMsg == "ProcessThisMessage")
            {
                memset(&buffer[0], 0, sizeof(buffer));

                cout << "UDP Message: " + udpMsg;
            }

            ...
        }
    }
}

Upvotes: 1

Views: 4458

Answers (1)

Jeremy Friesner
Jeremy Friesner

Reputation: 73041

I figured it is because the socket is everytime the method is called in the infinite loop, the getUdp() method opens a socket, gets the message and closes the socket, resulting in the server not being able to queue the messages.

Your intuition is correct. When you have a UDP socket bound to a port, the networking stack will buffer up (a finite number of) incoming UDP packets for you, so that (assuming you call recv() in a relatively timely manner), no incoming packets should get lost. But when you closesocket() the socket, that buffer is released, and of course during the times when no socket is bound to the UDP port and a UDP packet is received, the incoming UDP packet will simply be dropped (i.e. never buffered at all) because no sockets are bound to that port.

How am I able to tweak this code to receive 2 or more UDP messages? Appreciate any advice.

Conceptually, at least, you'll need to split the getUdp() method into three separate parts: a Setup() part, that you call once when your program starts up, a Receive() part (containing just the recv() call) that you can call as many times as you like, to receive the next packet, and the finally a Cleanup() part that closes the socket and shuts down the TCP stack (which you would call only when your program is about to exit). That way the UDP socket remains valid and bound to the port the whole time your program is running, so that the OS will reliably buffer up the incoming UDP packets to give to your program via recv().

Upvotes: 4

Related Questions