Introduction
Welcome back! In our previous lesson, we explored the theory behind UDP socket programming. We identified the key API calls—socket(), bind(), sendto(), and recvfrom()—and understood why their connectionless nature makes them different from their TCP counterparts.
Today, we move from theory to practice. Your goal for this lesson is to implement a simple UDP echo client-server program in C. You'll take the concepts from our last session and translate them into working code. This is a fantastic opportunity to leverage your C programming skills to build a functional networking application from the ground up, directly addressing your goal of connecting "book knowledge" with real-world implementation.
The UDP Client-Server Flow
As a quick refresher from last time, let's visualize the sequence of calls we are about to implement.

The server is passive; it sets up shop and waits. The client is active; it initiates the conversation. We will build and analyze the code for each part separately.
Implementing the UDP Echo Server
The server's job is to wait for a datagram on a specific port, and when one arrives, to send it right back to the sender—hence, an "echo" server.
The resource below contains well-commented C code for a UDP server. I'd like you to download or copy this code into a file named server_udp.c. We'll then break it down piece by piece.
This link from the 'C/C++ -> Sockets Tutorial' provides the complete source code for a UDP server. We will use this as our foundation.
Follow the link 'Click here for the server code using a datagram socket.' near the middle of the page. Copy the code into a local file named server_udp.c. Read through the code to get a general sense of its structure before we analyze it.
Now, let's dissect the key parts of server_udp.c.
-
Creating the Socket:
sock=socket(AF_INET, SOCK_DGRAM, 0);This should look familiar. We request a socket for the Internet address family (
AF_INET) but specify the type asSOCK_DGRAM, indicating we want to use the UDP protocol. -
Binding the Socket to a Port:
server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(atoi(argv[1])); if (bind(sock,(struct sockaddr *)&server,sizeof(server)) < 0) error("Binding");The server binds to the port number provided as a command-line argument (
argv[1]). The use ofINADDR_ANYallows the socket to receive packets destined for any of the machine's IP addresses on that port. This is standard practice for servers. Crucially, the port number is converted to network byte order usinghtons()("host to network short"). We'll touch on this again shortly. -
Receiving and Replying in a Loop:
while (1) { n = recvfrom(sock,buf,1024,0,(struct sockaddr *)&from,&fromlen); if (n < 0) error("recvfrom"); // ... code to print what was received ... n = sendto(sock,"Got your message",17,0,(struct sockaddr *)&from,fromlen); if (n < 0) error("sendto"); }This is the core of the server's logic:
recvfrom(): The program blocks here, waiting for a datagram. When one arrives,recvfromdoes two things: it copies the message intobufand it populates thefromstruct with the source IP address and port of the client.sendto(): To send the echo reply, the server callssendto. Critically, it uses thefromaddress structure it just received as the destination. This ensures the reply goes back to the correct client.
Implementing the UDP Echo Client
The client's job is to get a message from the user, send it to the server's address, wait for the echo, and print it.
Similar to the server, let's get the client code. Please copy this into a file named client_udp.c.
This link provides the corresponding UDP client code from the same tutorial.
On the same page as before, find and follow the link 'Click here for the client code using a datagram socket.' Copy this code into a local file named client_udp.c and review its structure.
Let's analyze the key parts of client_udp.c.
-
Creating the Socket and Finding the Server:
sock= socket(AF_INET, SOCK_DGRAM, 0); server.sin_family = AF_INET; hp = gethostbyname(argv[1]); bcopy((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length); server.sin_port = htons(atoi(argv[2]));The client also creates a
SOCK_DGRAMsocket. It then populates asockaddr_instruct (server) with the destination address. It usesgethostbyname()to look up the server's IP from the hostname given inargv[1]and sets the port fromargv[2]. -
Sending and Receiving Data:
// Gets message from user into buffer 'buf' printf("Please enter the message: "); bzero(buf,256); fgets(buf,255,stdin); n=sendto(sock,buf,strlen(buf),0,(const struct sockaddr *)&server,length); if (n < 0) error("Sendto"); n = recvfrom(sock,buf,256,0,(struct sockaddr *)&from, &length); if (n < 0) error("recvfrom");sendto(): The client sends the user's message usingsendto, specifying theserveraddress structure as the destination. Notice the client does not callbind(). The OS will automatically assign an ephemeral port to the client's socket, and the server will learn this port viarecvfrom.recvfrom(): The client then blocks onrecvfrom, waiting for the server's echo reply.
A Critical Detail: Network Byte Order
You've seen htons() used in the code. Why is this necessary?
Computers can store multi-byte numbers like integers in two ways: big-endian (most significant byte first) or little-endian (least significant byte first). To ensure that two computers can communicate regardless of their native format, network protocols like IP define a standard order: Network Byte Order, which is big-endian.
The socket API provides functions to convert between your host's byte order and network byte order.
htons(): host to network short (16-bit, for port numbers)htonl(): host to network long (32-bit, for IP addresses)ntohs(): network to host shortntohl(): network to host long
Forgetting to use these functions is a very common bug in network programming, as your code might work when the client and server are on the same machine (which likely has the same byte order) but fail when they are on different ones.
Introduction to Sockets Programming in C using TCP/IP
For a more detailed explanation of this concept, refer to the following slides from the 'Introduction to Sockets Programming' PDF.
Read slides 58-60 (labeled 'Constructing Messages - Byte Ordering' and '...-Solution: Network Byte Ordering'). Focus on the diagram that shows how htonl and ntohl ensure consistency between big-endian and little-endian machines.
Hands-On: Compile and Run Your Echo Service
Now it's time to bring your programs to life.
-
Open two terminal windows. One will be for the server, the other for the client.
-
Compile the code. In each terminal, run the appropriate
gcccommand.# In terminal 1: Compile the server gcc server_udp.c -o server # In terminal 2: Compile the client gcc client_udp.c -o client -
Run the server. Choose a high port number (e.g., above 2000) to avoid conflicts.
# In terminal 1: ./server 9999The server will start and then appear to hang. It's actually blocking on the
recvfrom()call, waiting for a client. -
Run the client. In the other terminal, run the client, telling it to connect to your local machine (
localhost) on the port your server is using.# In terminal 2: ./client localhost 9999 -
Test the communication. The client will prompt you for a message. Type something and press Enter. You should see the server receive the message and print it, and the client should receive the echo and print "Got an ack: ..."
Congratulations! You've just implemented a working UDP client-server program.
To see another demonstration of this entire process, you can watch the following video. It builds a similar program from scratch and shows the compilation and execution steps.
UDP Client Server implementation in C | Socket Programming
The video 'UDP Client Server implementation in C' provides a full walkthrough of coding, compiling, and running a UDP echo application. Watching this will reinforce the steps you just took.
Watch from 05:07 to the end. The first part (05:07 - 14:50) covers writing the server and client code. The second part (14:50 onwards) shows the compilation and execution, demonstrating the message exchange, including the echo.
Conclusion
In this lesson, you successfully transitioned from theory to practice by implementing a complete UDP echo client-server application in C. This is a significant milestone in your journey to understanding how the internet works at a fundamental level.
Key Takeaways:
- UDP Server Logic: Create a
SOCK_DGRAMsocket,bind()it to a well-known port, and enter a loop that callsrecvfrom()to get a message and the client's address, thensendto()to send a reply back to that address. - UDP Client Logic: Create a
SOCK_DGRAMsocket, populate asockaddr_instruct with the server's address and port, and usesendto()to send a message, followed byrecvfrom()to await the reply. sendto()andrecvfrom()are the workhorses of UDP, as they handle both data transfer and addressing for each individual datagram.- Network Byte Order is crucial: Always use functions like
htons()andhtonl()to ensure your address and port data are in the standardized network format.
Preview of the Next Lesson:
Your client program needed to know the server's address (localhost or an IP address) to function. But in the real world, you don't type 216.58.204.78 into your browser; you type google.com. How does your computer translate that human-readable name into a machine-usable IP address? That is the job of the Domain Name System (DNS). In our next lesson, we will explain the purpose of DNS in translating domain names to IP addresses.