Loading... ### Linux 网络编程——`recvfrom` 和 `sendto` 函数详解 在 Linux 网络编程中,`recvfrom` 和 `sendto` 是用于处理基于数据报(如 UDP)的网络通信的重要函数。这些函数主要用于在没有建立连接的情况下进行数据发送和接收操作。与 TCP 的 `recv` 和 `send` 函数不同,`recvfrom` 和 `sendto` 能够处理包含源或目标地址的信息,因此它们非常适合用于无连接的网络通信场景。 本文将详细介绍 `recvfrom` 和 `sendto` 函数的用法、参数解释,以及它们在网络编程中的实际应用。 ### 一、`recvfrom` 函数 #### 1.1 函数原型 ```c ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ``` **参数说明**: - `sockfd`:一个表示套接字的文件描述符。 - `buf`:指向接收缓冲区的指针,用于存储接收到的数据。 - `len`:指定接收缓冲区的大小,通常为 `buf` 的长度。 - `flags`:标志位,通常为 0,或者使用特定的标志位(如 `MSG_DONTWAIT`,表示非阻塞接收)。 - `src_addr`:指向 `sockaddr` 结构体的指针,用于存储发送方的地址信息。如果不需要获取发送方的地址信息,可以传入 `NULL`。 - `addrlen`:一个指向 `socklen_t` 类型的指针,表示 `src_addr` 的大小。如果 `src_addr` 为 `NULL`,则可以忽略该参数。 **返回值**: - 成功时,返回接收到的字节数。 - 失败时,返回 -1,并设置 `errno` 来指示错误类型。 #### 1.2 使用示例 以下是使用 `recvfrom` 函数接收 UDP 数据报的示例代码: ```c #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { int sockfd; struct sockaddr_in server_addr, client_addr; char buffer[1024]; socklen_t addr_len = sizeof(client_addr); // 创建UDP套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket creation failed"); return -1; } // 绑定本地地址和端口 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(8080); if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); close(sockfd); return -1; } // 接收数据报 ssize_t recv_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &addr_len); if (recv_len < 0) { perror("recvfrom failed"); } else { buffer[recv_len] = '\0'; printf("Received message: %s\n", buffer); printf("From IP: %s and Port: %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); } close(sockfd); return 0; } ``` **解释**: - 创建一个 UDP 套接字,并将其绑定到本地端口 8080。 - 使用 `recvfrom` 函数接收来自客户端的数据,并将数据存储在 `buffer` 中,同时记录发送方的 IP 地址和端口信息。 - 成功接收后,打印收到的数据及客户端的 IP 和端口。 ### 二、`sendto` 函数 #### 2.1 函数原型 ```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,或者使用特定的标志位(如 `MSG_CONFIRM`,确认数据报传输)。 - `dest_addr`:指向目标地址的 `sockaddr` 结构体的指针,表示要发送数据的目的地址。 - `addrlen`:表示 `dest_addr` 结构体的大小。 **返回值**: - 成功时,返回发送的字节数。 - 失败时,返回 -1,并设置 `errno` 来指示错误类型。 #### 2.2 使用示例 以下是使用 `sendto` 函数发送 UDP 数据报的示例代码: ```c #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { int sockfd; struct sockaddr_in server_addr; const char *message = "Hello, UDP Server!"; // 创建UDP套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket creation failed"); return -1; } // 设置目标服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 发送数据报 ssize_t sent_len = sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (sent_len < 0) { perror("sendto failed"); } else { printf("Message sent to server\n"); } close(sockfd); return 0; } ``` **解释**: - 创建一个 UDP 套接字,并设置目标服务器的 IP 地址和端口号。 - 使用 `sendto` 函数将字符串消息发送到指定的服务器地址。 - 成功发送后,打印发送成功的消息。 ### 三、`recvfrom` 和 `sendto` 的典型应用场景 #### 3.1 无连接的网络通信 `recvfrom` 和 `sendto` 通常用于 UDP 协议的网络通信。UDP 是无连接的协议,因此每个数据报都必须包含目标或源地址信息。通过 `recvfrom` 函数,服务器能够接收到客户端发送的数据并获取其地址信息;通过 `sendto` 函数,客户端可以将数据发送到指定的服务器地址,而不需要预先建立连接。 #### 3.2 广播和多播通信 在网络编程中,广播和多播通信是一种特殊的无连接通信方式,适用于发送给多个接收方的场景。使用 `sendto` 函数,可以将数据报发送到广播地址或多播地址,从而实现一对多的通信。接收方则使用 `recvfrom` 函数接收来自这些地址的消息。 #### 3.3 实时通信 由于 UDP 协议的低延迟特性,`recvfrom` 和 `sendto` 函数广泛应用于实时通信场景,例如在线游戏、视频会议、实时监控等。在这些应用中,数据的及时性比可靠性更重要,因此使用无连接的 UDP 协议能够有效减少通信延迟。 ### 四、常见问题与处理方法 #### 4.1 数据报丢失 UDP 协议不保证数据报的可靠传输,因此在网络拥塞或其他异常情况下,数据报可能会丢失。开发者应在应用层处理重传机制或超时处理,以提高通信的可靠性。 #### 4.2 数据报截断 `recvfrom` 函数接收到的数据如果超过了缓冲区的长度,可能会被截断。因此,确保分配足够大的缓冲区以容纳完整的数据报是非常重要的。同时,可以通过设置标志位 `MSG_PEEK` 来查看缓冲区的数据,而不实际读取它,从而判断是否有足够的空间。 #### 4.3 多客户端处理 在处理多个客户端的通信时,服务器需要通过 `recvfrom` 函数不断检查并区分不同客户端的请求。可以结合 `select` 或 `poll` 等多路复用机制来处理多个套接字的并发请求。 ### 五、总结 在 Linux 网络编程中,`recvfrom` 和 `sendto` 函数为开发者提供了基于 UDP 协议的无连接通信接口。这些函数通过处理源地址和目标地址信息,实现了灵活的数据报发送和接收功能。无论是在实时 通信、广播、多播,还是其他无连接网络场景中,这两个函数都是构建可靠、高效网络应用的重要基础。 最后修改:2024 年 08 月 23 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏