UDP Programming with Winsock
UDP (User Datagram Protocol) provides a connectionless, low‑overhead transport mechanism suitable for time‑critical applications such as streaming media, VoIP, and online gaming. The Winsock API offers a set of functions to create, configure, send, and receive UDP datagrams.
Key Concepts
- Connectionless – no handshake, each datagram is independent.
- Unreliable – delivery, ordering, and duplication are not guaranteed.
- Message‑oriented – preserves packet boundaries.
- Maximum payload size is limited by the underlying IP MTU (typically 1500 bytes).
Basic Steps
- Initialize Winsock
- Create UDP Socket
- Bind to Local Port
- Send Datagram
- Receive Datagram
- Cleanup
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udpSocket == INVALID_SOCKET) {
printf("socket failed: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
struct sockaddr_in localAddr;
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = INADDR_ANY; // listen on all interfaces
localAddr.sin_port = htons(5150); // choose a port
if (bind(udpSocket, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) {
printf("bind failed: %ld\n", WSAGetLastError());
closesocket(udpSocket);
WSACleanup();
return 1;
}
struct sockaddr_in destAddr;
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(5151);
InetPton(AF_INET, L"192.168.1.100", &destAddr.sin_addr);
const char* msg = "Hello, UDP!";
int sent = sendto(udpSocket, msg, (int)strlen(msg), 0,
(SOCKADDR*)&destAddr, sizeof(destAddr));
if (sent == SOCKET_ERROR) {
printf("sendto failed: %ld\n", WSAGetLastError());
}
char buffer[1024];
int addrLen = sizeof(destAddr);
int recvLen = recvfrom(udpSocket, buffer, sizeof(buffer)-1, 0,
(SOCKADDR*)&destAddr, &addrLen);
if (recvLen == SOCKET_ERROR) {
printf("recvfrom failed: %ld\n", WSAGetLastError());
} else {
buffer[recvLen] = '\0';
printf("Received: %s\n", buffer);
}
closesocket(udpSocket);
WSACleanup();
Full Example
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
printf("WSAStartup failed\\n");
return 1;
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s == INVALID_SOCKET) {
printf("socket failed\\n");
WSACleanup();
return 1;
}
struct sockaddr_in local = {0};
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(5150);
if (bind(s, (SOCKADDR*)&local, sizeof(local)) == SOCKET_ERROR) {
printf("bind failed\\n");
closesocket(s);
WSACleanup();
return 1;
}
struct sockaddr_in remote = {0};
remote.sin_family = AF_INET;
remote.sin_port = htons(5151);
InetPton(AF_INET, L"127.0.0.1", &remote.sin_addr);
const char* txt = "UDP test message";
sendto(s, txt, (int)strlen(txt), 0, (SOCKADDR*)&remote, sizeof(remote));
char recvbuf[512];
int remoteLen = sizeof(remote);
int rc = recvfrom(s, recvbuf, sizeof(recvbuf)-1, 0, (SOCKADDR*)&remote, &remoteLen);
if (rc >= 0) {
recvbuf[rc] = '\\0';
printf("Received: %s\\n", recvbuf);
}
closesocket(s);
WSACleanup();
return 0;
}
Best Practices
- Use non‑blocking sockets or I/O completion ports for high‑throughput servers.
- Validate the size of incoming datagrams before processing.
- Implement your own sequence numbers if ordering is required.
- Consider setting
SO_REUSEADDRwhen binding to well‑known ports. - Handle
WSAEMSGSIZEto detect oversized packets.