Writing UDP Data Sender and Receiver in C

UDP Data Sender and Receiver in C

Sender Program (sender.c):

The sender program is a UDP datagram sender that can be configured through command line options. It initializes Winsock, creates a UDP socket, and either connects to a recipient’s IP address or uses sendto() to send datagram messages. The program allows customization of parameters such as the remote port, recipient’s IP address, message count, buffer size, and the character used to fill the buffer.

Compile:

cl -o Sender Sender.c ws2_32.lib

Command line options:

sender [-p:int] [-r:IP] [-c] [-n:x] [-b:x] [-d:c]
-p:int Remote port
-r:IP Recipient’s IP address or hostname
-c Connect to remote IP first
-n:x Number of times to send message
-b:x Size of buffer to send
-d:c Character to fill buffer with

Receiver Program (receiver.c):

The receiver program complements the sender by receiving UDP datagrams. It initializes Winsock, creates a UDP socket, binds to a specified local IP and port, and enters a loop to continuously receive datagram messages using recvfrom(). The program supports command line options for configuring the local port, local IP address, and buffer size. Received data can be processed within the loop, making it suitable for handling incoming UDP messages.

Compile:

cl -o Receiver Receiver.c ws2_32.lib

Command line options:

sender [-p:int] [-i:IP][-n:x] [-b:x]
-p:int Local port
-i:IP Local IP address to listen on
-n:x Number of times to send message
-b:x Size of buffer to send

// Module Name: Sender.c
//
// Description:
//    This sample sends UDP datagrams to the specified recipient.
//    The -c option first calls connect() to associate the 
//    recipient's IP address with the socket handle so that the 
//    send() function can be used as opposed to the sendto() call.
//
// Compile:
//    cl -o Sender Sender.c ws2_32.lib
//
// Command line options:
//    sender [-p:int] [-r:IP] [-c] [-n:x] [-b:x] [-d:c]
//           -p:int   Remote port
//           -r:IP    Recipient's IP address or hostname
//           -c       Connect to remote IP first
//           -n:x     Number of times to send message
//           -b:x     Size of buffer to send
//           -d:c     Character to fill buffer with
//

#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_PORT            5150
#define DEFAULT_COUNT           25
#define DEFAULT_CHAR            'a'
#define DEFAULT_BUFFER_LENGTH   64

BOOL  bConnect = FALSE;                 // Connect to recipient first
int   iPort    = DEFAULT_PORT;          // Port to send data to
char  cChar    = DEFAULT_CHAR;          // Character to fill buffer 
DWORD dwCount  = DEFAULT_COUNT,         // Number of messages to send
      dwLength = DEFAULT_BUFFER_LENGTH; // Length of buffer to send
char  szRecipient[128];                 // Recipient's IP or hostname

//
// Function: usage
//
// Description:
//    Print usage information and exit
//
void usage()
{
    printf("usage: sender [-p:int] [-r:IP] "
     "[-c] [-n:x] [-b:x] [-d:c]\n\n");
    printf("       -p:int   Remote port\n");
    printf("       -r:IP    Recipients IP address or hostname\n");
    printf("       -c       Connect to remote IP first\n");
    printf("       -n:x     Number of times to send message\n");
    printf("       -b:x     Size of buffer to send\n");
    printf("       -d:c     Character to fill buffer with\n\n");
    ExitProcess(1);
}

//
// Function: ValidateArgs
//
// Description:
//    Parse the command line arguments, and set some global flags to
//    indicate what actions to perform
//
void ValidateArgs(int argc, char **argv)
{
    int i;

    for(i = 1; i < argc; i++)
    {
        if ((argv[i][0] == '-') || (argv[i][0] == '/'))
        {
            switch (tolower(argv[i][1]))
            {
                case 'p':        // Remote port
                    if (strlen(argv[i]) > 3)
                        iPort = atoi(&argv[i][3]);
                    break;
                case 'r':        // Recipient's IP addr
                    if (strlen(argv[i]) > 3)
                        strcpy(szRecipient, &argv[i][3]);
                    break;
                case 'c':        // Connect to recipients IP addr
                    bConnect = TRUE;
                    break;
                case 'n':        // Number of times to send message
                    if (strlen(argv[i]) > 3)
                        dwCount = atol(&argv[i][3]);
                    break;
                case 'b':        // Buffer size
                    if (strlen(argv[i]) > 3)
                        dwLength = atol(&argv[i][3]);
                    break;
                case 'd':  // Character to fill buffer
                    cChar = argv[i][3];
                    break;
                default:
                    usage();
                    break;
            }
        }
    }
}

//
// Function: main
//
// Description:
//    Main thread of execution. Initialize Winsock, parse the command
//    line arguments, create a socket, connect to the remote IP
//    address if specified, and then send datagram messages to the
//    recipient.
//
int main(int argc, char **argv)
{
    WSADATA        wsd;
    SOCKET         s;
    char          *sendbuf = NULL;
    int            ret,
                   i;
    SOCKADDR_IN    recipient;

    // Parse the command line and load Winsock
    //
    ValidateArgs(argc, argv);

    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        printf("WSAStartup failed!\n");
        return 1;
    }
    // Create the socket
    //
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == INVALID_SOCKET)
    {
        printf("socket() failed; %d\n", WSAGetLastError());
        return 1;
    }
    // Resolve the recipient's IP address or hostname
    //
    recipient.sin_family = AF_INET;
    recipient.sin_port = htons((short)iPort);
    if ((recipient.sin_addr.s_addr = inet_addr(szRecipient))
  == INADDR_NONE)
    {
        struct hostent *host=NULL;

        host = gethostbyname(szRecipient);
        if (host)
            CopyMemory(&recipient.sin_addr, host->h_addr_list[0],
                host->h_length);
        else
        {
            printf("gethostbyname() failed: %d\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }
    }
    // Allocate the send buffer
    //
    sendbuf = GlobalAlloc(GMEM_FIXED, dwLength);
    if (!sendbuf)
    {
        printf("GlobalAlloc() failed: %d\n", GetLastError());
        return 1;
    }
    memset(sendbuf, cChar, dwLength);
    //
    // If the connect option is set, "connect" to the recipient
    // and send the data with the send() function
    //
    if (bConnect)
    {
        if (connect(s, (SOCKADDR *)&recipient, 
                sizeof(recipient)) == SOCKET_ERROR)
        {
            printf("connect() failed: %d\n", WSAGetLastError());
            GlobalFree(sendbuf);
            WSACleanup();
            return 1;
        }
        for(i = 0; i < dwCount; i++)
        {
            ret = send(s, sendbuf, dwLength, 0);
            if (ret == SOCKET_ERROR)
            {
                printf("send() failed: %d\n", WSAGetLastError());
                break;
            }
            else if (ret == 0)
                break;
            // send() succeeded!
        }
    }
    else
    {
        // Otherwise, use the sendto() function
        //
        for(i = 0; i < dwCount; i++)
        {
            ret = sendto(s, sendbuf, dwLength, 0, 
                    (SOCKADDR *)&recipient, sizeof(recipient));
            if (ret == SOCKET_ERROR)
            {
                printf("sendto() failed; %d\n", WSAGetLastError());
                break;
            }
            else if (ret == 0)
                break;
            // sendto() succeeded!
        }
    }
    closesocket(s);

    GlobalFree(sendbuf);
    WSACleanup();
    return 0;
}

Here is a basic example of a receiver.c program that complements the sender.c program. This receiver program binds to the specified interface and port number and then blocks on a recvfrom() call to receive UDP datagrams:

// Module Name: Receiver.c
//
// Description:
//    This sample receives UDP datagrams by binding to
//    the specified interface and port number and then blocking on a recvfrom()
//    call
//
// Compile:
//    cl -o Receiver Receiver.c ws2_32.lib
//
// Command line options:
//    receiver [-p:int] [-i:IP] [-b:x]
//           -p:int   Local port
//           -i:IP    Local IP address to listen on
//           -b:x     Size of buffer to receive
//

#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_PORT            5150
#define DEFAULT_BUFFER_LENGTH   64

int   iPort    = DEFAULT_PORT;          // Port to receive data on
DWORD dwLength = DEFAULT_BUFFER_LENGTH; // Length of buffer to receive

//
// Function: usage
//
// Description:
//    Print usage information and exit
//
void usage()
{
    printf("usage: receiver [-p:int] [-i:IP] [-b:x]\n\n");
    printf("       -p:int   Local port\n");
    printf("       -i:IP    Local IP address to listen on\n");
    printf("       -b:x     Size of buffer to receive\n\n");
    ExitProcess(1);
}

//
// Function: ValidateArgs
//
// Description:
//    Parse the command line arguments and set the global variables
//
void ValidateArgs(int argc, char **argv)
{
    int i;

    for (i = 1; i < argc; i++)
    {
        if ((argv[i][0] == '-') || (argv[i][0] == '/'))
        {
            switch (tolower(argv[i][1]))
            {
            case 'p':        // Local port
                if (strlen(argv[i]) > 3)
                    iPort = atoi(&argv[i][3]);
                break;
            case 'i':        // Local IP addr
                if (strlen(argv[i]) > 3)
                    ; // You can add code here to handle the IP address if needed
                break;
            case 'b':        // Buffer size
                if (strlen(argv[i]) > 3)
                    dwLength = atol(&argv[i][3]);
                break;
            default:
                usage();
                break;
            }
        }
    }
}

//
// Function: main
//
// Description:
//    Main thread of execution. Initialize Winsock, parse the command
//    line arguments, create a socket, bind to the local IP and port,
//    and then receive datagram messages.
//
int main(int argc, char **argv)
{
    WSADATA        wsd;
    SOCKET         s;
    char          *recvbuf = NULL;
    int            ret;
    SOCKADDR_IN    local;
    SOCKADDR_IN    sender;
    int            senderSize;

    // Parse the command line and load Winsock
    ValidateArgs(argc, argv);

    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        printf("WSAStartup failed!\n");
        return 1;
    }

    // Create the socket
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == INVALID_SOCKET)
    {
        printf("socket() failed; %d\n", WSAGetLastError());
        return 1;
    }

    // Bind to the local IP and port
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons((short)iPort);

    if (bind(s, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR)
    {
        printf("bind() failed: %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    // Allocate the receive buffer
    recvbuf = (char *)malloc(dwLength);
    if (!recvbuf)
    {
        printf("malloc() failed!\n");
        closesocket(s);
        WSACleanup();
        return 1;
    }

    while (1)
    {
        senderSize = sizeof(sender);

        // Receive data
        ret = recvfrom(s, recvbuf, dwLength, 0, (SOCKADDR *)&sender, &senderSize);
        if (ret == SOCKET_ERROR)
        {
            printf("recvfrom() failed; %d\n", WSAGetLastError());
            break;
        }

        printf("Received %d bytes from %s:%d\n", ret, inet_ntoa(sender.sin_addr), ntohs(sender.sin_port));
        // You can process the received data here

    }

    closesocket(s);
    free(recvbuf);
    WSACleanup();
    return 0;
}
M. Saqib: Saqib is Master-level Senior Software Engineer with over 14 years of experience in designing and developing large-scale software and web applications. He has more than eight years experience of leading software development teams. Saqib provides consultancy to develop software systems and web services for Fortune 500 companies. He has hands-on experience in C/C++ Java, JavaScript, PHP and .NET Technologies. Saqib owns and write contents on mycplus.com since 2004.
Related Post