Getting Started with UDP Network Programming
1. Basic Theories of Network Programming
1.1 Network Models: OSI Seven-Layer and TCP/IP Four-Layer
There are two core architectures for network communication. The OSI model is a general reference model, while the TCP/IP model is the practical application model for the Internet. Their corresponding relationships are as follows:
| OSI Seven-Layer Model | Core Functions | TCP/IP Four-Layer Model | Core Protocols/Technologies |
|---|---|---|---|
| Application Layer | Provides network services for users (e-mail, file transfer, etc.) | Application Layer | HTTP, FTP, TFTP, DNS, SNMP |
| Presentation Layer | Unifies data representation and completes data encryption/decryption | Application Layer | (Merged into the Application Layer in the TCP/IP model) |
| Session Layer | Manages process sessions and coordinates communication processes | Application Layer | (Merged into the Application Layer in the TCP/IP model) |
| Transport Layer | Manages end-to-end data transmission and provides reliable/unreliable services | Transport Layer | TCP (Reliable Transmission), UDP (Real-Time Transmission) |
| Network Layer | Routing selection, internet interconnection, and locating target hosts | Network Layer | IP, ICMP (ping command), RIP, OSPF, IGMP |
| Data Link Layer | Physical address addressing, data frame encapsulation, and error control | Link Layer | ARP (IP to MAC), RARP |
| Physical Layer | Converts data into electrical signals and transmits them through network media | Link Layer | Network cards, twisted pair, optical fibers, wireless channels |
1.2 Explanation of Core Concepts
(1) IP Address and Port
- IP Address : Composed of network bits and host bits, it is used to identify hosts in the network. The mainstream version is IPv4 (32-bit, e.g.,
192.168.14.128), and IPv6 will be gradually adopted in the future. - Port Number: Used to identify applications on a host, with a range of 1 to 65535. Ports 1 to 1023 are reserved for the system, and ports 1024 to 65535 are user-defined.
- IP + Port: Uniquely identifies a specific application in the network and serves as the basic address identifier for network communication.
(2) Socket
A Socket is an interface between an application and the kernel network protocol stack, essentially a file descriptor. Through it, network data transmission and reception can be realized, which is the core entry point for network programming.
(3) Byte Order
- Host Byte Order: Mainstream CPUs (Intel, AMD, ARM) adopt little-endian storage (low bytes are stored at low addresses).
- Network Byte Order: Network devices uniformly adopt big-endian storage (high bytes are stored at low addresses).
- Conversion Functions:
htons()(Host to Network Short),inet_addr()(Converts dotted decimal IP to network byte order IP).
(4) Network Configuration Related Commands
| Command | Function |
|---|---|
ifconfig |
View/temporarily configure network information (e.g., ifconfig ens33 192.168.0.13/24 up to temporarily set the IP address) |
sudo vim /etc/network/interfaces |
Edit the Ubuntu permanent network configuration file |
sudo /etc/init.d/networking restart |
Restart the network service to load permanent configurations |
ping www.baidu.com |
Test network connectivity |
netstat -anp |
View all network communication connections and process associations on the local machine |
sudo reboot -f |
Force restart the system to make network configurations take effect |
1.3 DHCP and DNS
- DHCP (Dynamic Host Configuration Protocol): Automatically assigns network parameters such as IP address, subnet mask, and gateway to the host without manual configuration.
- DNS (Domain Name System) : Translates human-readable domain names (e.g.,
www.baidu.com) into machine-recognizable IP addresses, realizing the mapping from domain names to IP addresses.
2. Detailed Explanation of UDP Protocol
2.1 Core Features of UDP
UDP (User Datagram Protocol) is a connectionless transport layer protocol with the following core features:
- Connectionless: Communication parties do not need to establish a connection and can send data directly, saving the overhead of connection establishment.
- Low Latency: Without mechanisms such as confirmation and retransmission, data transmission is fast, suitable for real-time scenarios.
- Low Network Resource Utilization: The protocol header is simple (only 8 bytes), with low overhead.
- Unreliable Transmission: Does not guarantee the orderly arrival and integrity of data, and packet loss or out-of-order may occur.
2.2 Characteristics of UDP Datagrams
- There is a clear boundary between data packets, and the receiver can distinguish different data packets.
- The number of sending and receiving operations must correspond; if data is sent 10 times, the receiver also needs to call the receiving function 10 times to obtain the complete data.
- No write blocking: When the sending speed is too fast, the kernel buffer will be full, leading to data loss, and the sending rate needs to be controlled manually.
- With read blocking: If there is no data for the receiver to read, it will block and wait until data arrives.
2.3 UDP Communication Roles and Processes
(1) Communication Roles
- Server: The end that provides services, usually only one. It is responsible for binding a fixed IP and port, and waiting for and processing client requests.
- Client: The end that uses services, which can be multiple. It does not need to bind a fixed port and directly sends requests to the server.
(2) Function Call Process
| Role | Core Process | Key Functions |
|---|---|---|
| Server | Create Socket → Bind IP and Port → Cyclically Receive Client Data → Reply Data → Close Socket | socket() → bind() → recvfrom() → sendto() → close() |
| Client | Create Socket → Send Data to Server → Receive Server Reply → Close Socket | socket() → sendto() → recvfrom() → close() |
3. UDP Practice: Timestamp Echo Service (Complete Code)
This section implements a simple UDP server that receives messages from the client, appends the current timestamp, and replies to the client. The client cyclically sends messages and receives replies, demonstrating the complete process of UDP communication.
3.1 Server Code (udp_server.c)
c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
// Define an alias for the socket address structure pointer to simplify code
typedef struct sockaddr *(SA);
int main(int argc, char **argv)
{
// 1. Create UDP socket
// AF_INET: IPv4 address family, SOCK_DGRAM: UDP datagram socket, 0: Automatically adapt to the protocol
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
perror("socket create failed");
return 1;
}
printf("UDP socket created successfully, socket descriptor: %d\n", sockfd);
// 2. Initialize server address structure
struct sockaddr_in ser_addr, cli_addr;
bzero(&ser_addr, sizeof(ser_addr)); // Clear the structure memory
bzero(&cli_addr, sizeof(cli_addr));
ser_addr.sin_family = AF_INET; // Specify the IPv4 address family
ser_addr.sin_port = htons(50000); // Convert port number to network byte order (50000 is a custom port)
// Bind to the local IP (192.168.14.128), or use INADDR_ANY to bind all network card IPs
ser_addr.sin_addr.s_addr = inet_addr("192.168.14.128");
// 3. Bind the socket to the server IP + port
int ret = bind(sockfd, (SA)&ser_addr, sizeof(ser_addr));
if (-1 == ret)
{
perror("bind failed");
close(sockfd);
return 1;
}
printf("Socket bound successfully, IP: 192.168.14.128, Port: 50000\n");
// 4. Cyclically receive client data and reply
socklen_t cli_addr_len = sizeof(cli_addr); // Client address length
while (1)
{
char buf[512] = {0}; // Receive buffer
// Receive client data (blocking wait)
// (SA)&cli_addr: Used to store client address information, &cli_addr_len: Client address length
ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli_addr, &cli_addr_len);
if (recv_len < 0)
{
perror("recvfrom failed");
continue;
}
// Print client information and received data
printf("Received message from client [%s:%d]: %s\n",
inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);
// Get the current system time and append it to the received message
time_t current_time;
time(¤t_time);
struct tm *time_info = localtime(¤t_time);
sprintf(buf, "%s %02d:%02d:%02d", buf,
time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
// Reply to the client
ssize_t send_len = sendto(sockfd, buf, strlen(buf) + 1, 0, (SA)&cli_addr, cli_addr_len);
if (send_len < 0)
{
perror("sendto failed");
continue;
}
printf("Replied to client [%s:%d]: %s\n",
inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);
}
// 5. Close the socket (this will not be executed in practice due to the while(1) loop; terminate with Ctrl+C)
close(sockfd);
return 0;
}
Key Analysis of Server Code
socket(AF_INET, SOCK_DGRAM, 0): Creates an IPv4 UDP socket and returns a file descriptor.bind(): Binds the socket to a fixed IP and port. The server must bind, otherwise the client cannot locate the server.recvfrom(): Receives client data and obtains client address information for replying.sendto(): Replies to the specified client address, which requires passing the client address structure.htons()/inet_addr(): Converts host byte order to network byte order to ensure cross-device communication compatibility.
3.2 Client Code (udp_client.c)
c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
typedef struct sockaddr *(SA);
int main(int argc, char **argv)
{
// 1. Create UDP socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
perror("socket create failed");
return 1;
}
printf("UDP client socket created successfully\n");
// 2. Initialize server address structure (specify server IP and port)
struct sockaddr_in ser_addr;
bzero(&ser_addr, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(50000); // Consistent with the server port
ser_addr.sin_addr.s_addr = inet_addr("192.168.14.128"); // Server IP
// 3. Cyclically send messages to the server and receive replies
int send_count = 10; // Send messages 10 times cyclically
while (send_count--)
{
char buf[512] = "hello"; // Message to be sent
// Send data to the server
ssize_t send_len = sendto(sockfd, buf, strlen(buf), 0, (SA)&ser_addr, sizeof(ser_addr));
if (send_len < 0)
{
perror("sendto failed");
close(sockfd);
return 1;
}
printf("Sent message to server: %s (Remaining sending times: %d)\n", buf, send_count);
// Receive server reply
bzero(buf, sizeof(buf)); // Clear the receive buffer
ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (recv_len < 0)
{
perror("recvfrom failed");
close(sockfd);
return 1;
}
printf("Received reply from server: %s\n", buf);
sleep(1); // Send once every 1 second
}
// 4. Close the socket
close(sockfd);
printf("Client exited normally\n");
return 0;
}
Key Analysis of Client Code
- The client does not need to call
bind(): The kernel will automatically assign a random port and local IP to the client, which does not affect communication with the server. sendto(): Sends data directly to the server address, which requires specifying the server's IP and port.recvfrom(NULL, NULL): The client does not need to care about the server address (already clear), so pass NULL to ignore the server address information.sleep(1): Controls the sending rate to avoid packet loss caused by too fast sending.
4. Compilation and Running Steps
4.1 Environment Preparation
-
Install Compilation Environment : Install the gcc compiler in the Ubuntu system
bashsudo apt update && sudo apt install gcc -y -
Configure Network : Ensure the server and client are in the same local area network, or achieve communication through bridge mode
- Temporarily set the server IP:
ifconfig ens33 192.168.14.128/24 up - Test network connectivity:
ping 192.168.14.128(The client pings the server to ensure smooth network)
- Temporarily set the server IP:
4.2 Compile Code
bash
# Compile the server
gcc udp_server.c -o udp_server
# Compile the client
gcc udp_client.c -o udp_client
4.3 Run the Program
Step 1: Start the Server (Run First)
bash
./udp_server
Server Output:
UDP socket created successfully, socket descriptor: 3
Socket bound successfully, IP: 192.168.14.128, Port: 50000
Step 2: Start the Client (Open a New Terminal and Run Later)
bash
./udp_client
Client Output (Example):
UDP client socket created successfully
Sent message to server: hello (Remaining sending times: 9)
Received reply from server: hello 15:30:25
Sent message to server: hello (Remaining sending times: 8)
Received reply from server: hello 15:30:26
...
Client exited normally
Step 3: View Server Output (Example)
Received message from client [192.168.14.129:41235]: hello
Replied to client [192.168.14.129:41235]: hello 15:30:25
Received message from client [192.168.14.129:41235]: hello
Replied to client [192.168.14.129:41235]: hello 15:30:26
...
5. Troubleshooting Common Issues
-
bind() Failed: Address already in use
- Cause: Port 50000 is already occupied by another process.
- Solution: Use
netstat -anp | grep 50000to find the occupying process, kill the process, or replace the port.
-
Unable to Receive/Send Data
- Check if the IP addresses of the server and client are correct and ensure they are in the same network segment.
- Check if the firewall is blocking the UDP port; you can disable the firewall (
sudo ufw disable). - Confirm that the network mode is bridge mode to avoid network segment isolation caused by NAT mode.
-
Data Loss
- Cause: UDP has no flow control, and the kernel buffer overflows due to too fast sending rate.
- Solution: Add
sleep()in the client to control the sending rate, or implement flow control at the application layer.
6. Summary and Expansion
6.1 Article Summary
- Network Models: The OSI seven-layer is a reference model, and the TCP/IP four-layer is a practical application model; their corresponding relationships need to be kept in mind.
- UDP Features: Connectionless, low latency, unreliable, suitable for real-time scenarios; datagrams have clear boundaries, and the number of sending and receiving operations must correspond.
- UDP Programming Process: Server (Create Socket → Bind → Receive/Send Data), Client (Create Socket → Send/Receive Data).
- Core Functions:
socket(),bind(),recvfrom(), andsendto()are the basics of UDP programming.
6.2 Expansion Directions
- Multi-Client Communication : The server can obtain addresses of different clients through
recvfrom()to achieve one-to-many communication. - Data Verification: UDP does not guarantee reliability; you can add checksum and retransmission mechanisms at the application layer to improve data transmission reliability.
- Comparative Learning of TCP and UDP: TCP is connection-oriented reliable transmission, suitable for scenarios such as file transfer; you can further learn TCP programming.
- Advanced Network Programming: Learn advanced technologies such as socket option settings, non-blocking IO, and multiplexing (select/poll/epoll).