Linux 下 UDP 编程入门 – wiki基地

Linux 下 UDP 编程入门指南

UDP(用户数据报协议)是一种无连接的传输层协议,它提供了一种简单、快速但不可靠的数据传输方式。与 TCP 不同,UDP 不建立连接,也不保证数据包的顺序和可靠性,但它具有较低的开销和更高的传输速度,适用于对实时性要求较高但对数据丢失容忍度较高的应用场景,例如流媒体、在线游戏和 DNS 查询等。

本文将详细介绍 Linux 下 UDP 编程的基本概念、API 函数以及一个完整的示例程序,帮助读者快速入门 UDP 编程。

1. UDP 编程基础

UDP 编程的核心在于使用套接字(socket)进行数据传输。套接字是一种抽象的通信端点,它允许进程之间通过网络进行通信。在 UDP 编程中,我们需要创建一个 UDP 套接字,然后使用该套接字发送和接收数据。

1.1 地址结构

UDP 使用 IP 地址和端口号来标识通信的双方。IP 地址用于标识网络上的主机,而端口号用于标识主机上的特定应用程序。在 Linux 下,使用 struct sockaddr_in 结构体来表示 IPv4 地址,其定义如下:

“`c
struct sockaddr_in {
sa_family_t sin_family; / 地址族,AF_INET 表示 IPv4 /
in_port_t sin_port; / 端口号,需要转换为网络字节序 /
struct in_addr sin_addr; / IP 地址 /
};

struct in_addr {
in_addr_t s_addr; / IP 地址,需要转换为网络字节序 /
};
“`

1.2 网络字节序

由于不同主机可能使用不同的字节序(大端序或小端序),因此在网络通信中需要使用统一的字节序,即网络字节序(大端序)。我们需要使用 htons()htonl() 函数将主机字节序转换为网络字节序,使用 ntohs()ntohl() 函数将网络字节序转换为主机字节序。

2. UDP 编程 API 函数

Linux 下 UDP 编程主要使用以下 API 函数:

  • socket(): 创建套接字。
  • bind(): 绑定套接字到本地地址和端口。
  • sendto(): 发送数据。
  • recvfrom(): 接收数据。
  • close(): 关闭套接字。

2.1 socket()

c
int socket(int domain, int type, int protocol);

  • domain: 地址族,AF_INET 表示 IPv4。
  • type: 套接字类型,SOCK_DGRAM 表示 UDP。
  • protocol: 协议,通常为 0。

2.2 bind()

c
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • sockfd: 套接字描述符。
  • addr: 指向 sockaddr 结构体的指针,包含要绑定的地址和端口。
  • addrlen: sockaddr 结构体的长度。

2.3 sendto()

c
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);

  • sockfd: 套接字描述符。
  • buf: 要发送的数据缓冲区。
  • len: 要发送的数据长度。
  • flags: 通常为 0。
  • dest_addr: 指向 sockaddr 结构体的指针,包含目标地址和端口。
  • addrlen: sockaddr 结构体的长度。

2.4 recvfrom()

c
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);

  • sockfd: 套接字描述符。
  • buf: 接收数据缓冲区。
  • len: 缓冲区大小。
  • flags: 通常为 0。
  • src_addr: 指向 sockaddr 结构体的指针,用于存储发送方的地址和端口。
  • addrlen: sockaddr 结构体的长度指针。

3. UDP 编程示例

以下是一个简单的 UDP 服务器和客户端示例程序:

3.1 UDP 服务器 (server.c)

“`c

include

include

include

include

include

include

include

define SERVER_PORT 8888

int main() {
int sockfd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
char buffer[1024];

// 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    perror("socket");
    exit(1);
}

// 绑定地址和端口
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听所有接口
server_addr.sin_port = htons(SERVER_PORT);

if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
    perror("bind");
    exit(1);
}

printf("UDP server listening on port %d...\n", SERVER_PORT);


while (1) {
    client_addr_len = sizeof(client_addr);
    int n = recvfrom(sockfd, buffer, sizeof(buffer), 0,
                     (struct sockaddr *)&client_addr, &client_addr_len);
    if (n < 0) {
        perror("recvfrom");
        exit(1);
    }

    printf("Received from %s:%d: %s\n",
           inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer);

    // 发送回复
    sendto(sockfd, buffer, n, 0, (struct sockaddr *)&client_addr, client_addr_len);
}

close(sockfd);
return 0;

}
“`

3.2 UDP 客户端 (client.c)

“`c

include

include

include

include

include

include

include

define SERVER_IP “127.0.0.1”

define SERVER_PORT 8888

int main() {
int sockfd;
struct sockaddr_in server_addr;
char buffer[1024];

// 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    perror("socket");
    exit(1);
}


// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
    perror("inet_pton");
    exit(1);
}

while (1) {
    printf("Enter message: ");
    fgets(buffer, sizeof(buffer), stdin);

    // 发送数据
    sendto(sockfd, buffer, strlen(buffer), 0,
           (struct sockaddr *)&server_addr, sizeof(server_addr));

    // 接收回复
    int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
    if (n < 0) {
        perror("recvfrom");
        exit(1);
    }

    printf("Server replied: %s", buffer);
}

close(sockfd);
return 0;

}

“`

4. 编译和运行

使用以下命令编译服务器和客户端程序:

bash
gcc server.c -o server
gcc client.c -o client

首先运行服务器程序:

bash
./server

然后在另一个终端运行客户端程序:

bash
./client

现在,客户端可以向服务器发送消息,服务器将接收消息并将其回显给客户端。

5. 总结

本文介绍了 Linux 下 UDP 编程的基础知识和 API 函数,并提供了一个简单的服务器和客户端示例程序。UDP 编程相对简单,但需要注意字节序转换和错误处理。 通过理解本文的内容,读者可以开始编写自己的 UDP 应用程序。 希望本文能帮助读者快速入门 Linux 下 UDP 编程。 更深入的学习可以参考 Linux 系统编程相关的书籍和文档,以及探索更高级的主题,例如多播和广播。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部