UDP Server Example

Introduction

This example demonstrates how to create a basic User Datagram Protocol (UDP) server application using Windows Sockets (Winsock). UDP is a connectionless, unreliable transport layer protocol. It is often used for applications where speed is more important than guaranteed delivery, such as streaming media or online gaming.

This server will listen on a specified port, receive UDP datagrams, and echo the received data back to the sender.

Prerequisites

  • A development environment capable of C/C++ development on Windows (e.g., Visual Studio).
  • Basic understanding of C/C++ programming and Windows Sockets API.

Server Code (udp_server.c)

Below is the C code for the UDP server. You can copy this code into a new C file (e.g., udp_server.c) and compile it using your C++ compiler.

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

#pragma comment(lib, "ws2_32.lib")

#define DEFAULT_PORT "8080"
#define BUFFER_SIZE 512

int main() {
    WSADATA wsaData;
    SOCKET listenSocket = INVALID_SOCKET;
    SOCKET clientSocket = INVALID_SOCKET; // Not strictly needed for UDP, but good practice to understand
    struct sockaddr_in serverAddr, clientAddr;
    int clientAddrSize = sizeof(clientAddr);
    char recvBuf[BUFFER_SIZE];
    int bytesReceived;

    // Initialize Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        fprintf(stderr, "WSAStartup failed with error: %d\n", WSAGetLastError());
        return 1;
    }

    // Create a SOCKET for listening
    listenSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (listenSocket == INVALID_SOCKET) {
        fprintf(stderr, "socket failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    // Setup the server address structure
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY; // Listen on all available interfaces
    serverAddr.sin_port = htons(atoi(DEFAULT_PORT)); // Port to listen on

    // Bind the socket to the server address
    if (bind(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        fprintf(stderr, "bind failed with error: %d\n", WSAGetLastError());
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    printf("UDP Server listening on port %s...\n", DEFAULT_PORT);

    // Main server loop
    while (1) {
        // Clear the receive buffer
        memset(recvBuf, 0, BUFFER_SIZE);

        // Receive data from a client
        bytesReceived = recvfrom(listenSocket, recvBuf, BUFFER_SIZE, 0, (struct sockaddr *)&clientAddr, &clientAddrSize);
        if (bytesReceived == SOCKET_ERROR) {
            fprintf(stderr, "recvfrom failed with error: %d\n", WSAGetLastError());
            // Continue to next iteration rather than exiting on a single error
            continue;
        }

        // Convert client address to string for printing
        char clientIpStr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &clientAddr.sin_addr, clientIpStr, INET_ADDRSTRLEN);
        printf("Received %d bytes from %s:%d\n", bytesReceived, clientIpStr, ntohs(clientAddr.sin_port));
        printf("Data: %.*s\n", bytesReceived, recvBuf);

        // Echo the data back to the client
        int bytesSent = sendto(listenSocket, recvBuf, bytesReceived, 0, (struct sockaddr *)&clientAddr, clientAddrSize);
        if (bytesSent == SOCKET_ERROR) {
            fprintf(stderr, "sendto failed with error: %d\n", WSAGetLastError());
            continue;
        }
        printf("Sent %d bytes back to %s:%d\n", bytesSent, clientIpStr, ntohs(clientAddr.sin_port));
    }

    // Cleanup (though this loop is infinite, this is for completeness)
    closesocket(listenSocket);
    WSACleanup();

    return 0;
}

Compilation and Linking:

If you are using Visual Studio, create a new C++ console application project and add this code. The compiler should automatically link the Winsock library. If compiling from the command line (e.g., using MinGW or Visual Studio's developer command prompt), you might need to explicitly link:

cl udp_server.c /link ws2_32.lib

or

gcc udp_server.c -lws2_32 -o udp_server.exe

How to Run

  1. Compile the C code using your compiler.
  2. Run the compiled executable (e.g., udp_server.exe) on your Windows machine.
  3. The server will start listening on port 8080.

Testing the Server

You can test the UDP server using a UDP client application. A simple UDP client example is also available.

UDP Client Example

Alternatively, you can use tools like:

  • Netcat (nc): If you have Netcat installed, you can send data to the server:
echo "Hello UDP Server" | nc -u 127.0.0.1 8080

The server should print the received message and echo it back. Netcat might show the echoed response depending on its configuration.

Key Winsock Functions Used

  • WSAStartup(): Initializes the Winsock DLL.
  • socket(): Creates a socket. For UDP, we use AF_INET (IPv4) and SOCK_DGRAM (UDP).
  • bind(): Assigns a local address and port to a socket.
  • recvfrom(): Receives data from a UDP socket and gets the source address.
  • sendto(): Sends data to a specific address on a UDP socket.
  • closesocket(): Closes a socket.
  • WSACleanup(): Terminates the use of the Winsock DLL.

Important Considerations for UDP

Connectionless: UDP does not establish a connection before sending data. Each datagram is sent independently.
Unreliable: Datagrams may be lost, duplicated, or arrive out of order. The application layer must handle these issues if they are critical.
No Flow Control: UDP does not have built-in mechanisms to prevent a fast sender from overwhelming a slow receiver.
Buffer Management: Ensure your receive buffer is large enough to accommodate incoming datagrams. Larger datagrams might be truncated or cause errors if the buffer is too small.