
UDP(User Datagram Protocol,用户数据报协议)协议是一种基于IP协议的不可靠网络传输协议,它是TCP/IP协议栈中传输层的一部分。与TCP协议相比,UDP协议具有轻量级、无需建立连接、资源消耗少、通信效率高等特点。
UDP是一种无连接的传输层协议,与 TCP 不同,它不保证数据的可靠传输、不保证数据的顺序性,也没有拥塞控制机制。UDP 协议具有开销小、传输速度快的特点,适用于对实时性要求较高、对少量数据丢失不太敏感的应用场景。在嵌入式 Linux 应用开发中,UDP 常用于实时音视频传输、游戏、传感器数据采集等领域。

UDP协议的字段格式包括以下几个部分:
UDP协议的数据传输过程包括封包和解封包两个步骤:
①封包:在发送方,用户发送的数据在传输层增加UDP头部,封装在UDP的数据部分。然后,在IP层增加IP头部数据,将UDP的数据和头部都封装在IP层的数据部分。最后,IP层将数据传输给网络设备的驱动程序,以太网增加头部和尾部后,发送到以太网上。
②解封包:在接收方,驱动程序从以太网上接收到数据后,去除头部和尾部并进行CRC校验。然后,将正确的数据传递给IP层,IP层剥去IP头后进行校验,并将数据发送给其上层UDP层。UDP层将UDP的包头剥去后,根据应用程序的标识符判断是否发送给此应用程序。
#include <sys/socket.h>
// 创建socket
int socket(int domain, int type, int protocol); // SOCK_DGRAM
// 绑定地址
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// 接收数据(带来源地址)
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
// 发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);在嵌入式Linux应用开发中,可以使用socket编程接口来实现UDP协议的数据传输。具体步骤如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int main() {
int sockfd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
// 创建 UDP 套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
// 填充服务器地址信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 绑定套接字到指定地址和端口
if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("UDP server listening on port %d...\n", PORT);
while (1) {
// 接收客户端数据
ssize_t recv_len = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL,
(struct sockaddr *)&client_addr, &client_addr_len);
if (recv_len < 0) {
perror("recvfrom failed");
continue;
}
buffer[recv_len] = '\0';
printf("Client: %s\n", buffer);
// 发送响应数据给客户端
const char *response = "Message received";
sendto(sockfd, (const char *)response, strlen(response), MSG_CONFIRM,
(const struct sockaddr *)&client_addr, client_addr_len);
printf("Response sent to client\n");
}
// 关闭套接字
close(sockfd);
return 0;
}首先创建一个 UDP 套接字,然后将其绑定到指定的 IP 地址和端口。使用 recvfrom 函数接收客户端发送的数据,并使用 sendto 函数发送响应数据给客户端。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_IP "127.0.0.1"
#define PORT 8888
#define BUFFER_SIZE 1024
int main() {
int sockfd;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE];
// 创建 UDP 套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&server_addr, 0, sizeof(server_addr));
// 填充服务器地址信息
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
perror("Invalid address/ Address not supported");
exit(EXIT_FAILURE);
}
const char *message = "Hello, server!";
// 发送数据到服务器
sendto(sockfd, (const char *)message, strlen(message), MSG_CONFIRM,
(const struct sockaddr *)&server_addr, sizeof(server_addr));
printf("Message sent to server\n");
socklen_t server_addr_len = sizeof(server_addr);
// 接收服务器响应数据
ssize_t recv_len = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL,
(struct sockaddr *)&server_addr, &server_addr_len);
if (recv_len < 0) {
perror("recvfrom failed");
exit(EXIT_FAILURE);
}
buffer[recv_len] = '\0';
printf("Server: %s\n", buffer);
// 关闭套接字
close(sockfd);
return 0;
}创建一个 UDP 套接字,填充服务器的地址信息,使用 sendto 函数发送数据到服务器,然后使用 recvfrom 函数接收服务器的响应数据。
SO_RCVBUF/SO_SNDBUF调优socket缓冲区
// 检查无效socket描述符
if(fcntl(sockfd, F_GETFL) < 0 && errno == EBADF) {
// 处理socket失效
}struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));struct timeval tv;
tv.tv_sec = 2; // 2秒超时
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));使用netcat测试:
# 接收测试
nc -ul 8888
# 发送测试
echo "test" | nc -u 192.168.1.100 8888抓包分析:
tcpdump -i eth0 udp port 8888 -vv -X查看socket状态:
netstat -anu | grep 8888使用多线程处理I/O:
pthread_create(&recv_thread, NULL, udp_recv_handler, &sockfd);零拷贝技术(Linux 4.1+):
setsockopt(sockfd, SOL_SOCKET, SO_ZEROCOPY, &enable, sizeof(enable));批量数据发送:
struct mmsghdr msgs[10];
sendmmsg(sockfd, msgs, 10, 0);掌握这些UDP编程技术后,开发者可以在嵌入式Linux系统中实现高效的网络通信,特别适合需要快速响应、可接受少量数据丢失的物联网应用场景。实际开发中需根据具体硬件资源和应用需求进行参数调优和可靠性增强设计。