目录
概述
readv、recv和recvmsg三个函数都是用于从文件或套接字接收数据的函数,但它们在功能和使用场景上存在一些区别。
-
readv函数:
readv函数主要用于从文件描述符读取数据到多个缓冲区中。它允许在一次系统调用中读取多个缓冲区的数据,这有助于减少多次系统调用的开销,提高读操作的性能。readv函数通常用于文件I/O操作。 -
recv函数:
recv函数用于从套接字接收数据。它提供了类似于read函数的功能,但还额外提供了一个flags参数,用于控制一些特殊的行为。例如,通过设置MSG_PEEK标志,recv可以预览数据而不将其从缓冲区中移除;通过设置MSG_WAITALL标志,recv可以阻止在接收的数据少于预期时返回。recv函数通常用于网络编程中的TCP和UDP套接字。 -
recvmsg函数:
recvmsg函数是另一个用于从套接字接收数据的函数,它提供了更为复杂和灵活的功能。与recv函数相比,recvmsg函数提供了更多的控制和选项,例如可以接收辅助数据(如带外数据)和更详细的控制消息标志。recvmsg函数还支持分散/聚集I/O,即可以将接收到的数据分散到多个缓冲区中,或将来自多个源的数据聚集到一个缓冲区中。recvmsg函数通常用于更高级的网络编程场景。
总结来说,readv函数主要用于文件I/O操作,从文件描述符读取数据到多个缓冲区中;recv函数用于从套接字接收数据,并提供了额外的Flags参数来控制特殊行为;recvmsg函数则提供了更为复杂和灵活的功能,支持分散/聚集I/O和更多的控制选项。根据具体的使用场景和需求,可以选择适合的函数来进行数据接收操作。
readv 示例 (文件I/O)
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
char buf1[10];
char buf2[10];
struct iovec iov[2];
iov[0].iov_base = buf1;
iov[0].iov_len = sizeof(buf1);
iov[1].iov_base = buf2;
iov[1].iov_len = sizeof(buf2);
ssize_t bytes_read = readv(fd, iov, 2);
if (bytes_read == -1) {
perror("readv");
return 1;
}
printf("Read %zd bytes:\n", bytes_read);
printf("Buffer 1: %s\n", buf1);
printf("Buffer 2: %s\n", buf2);
close(fd);
return 0;
}
recv 示例 (套接字通信)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
char buffer[1024];
ssize_t bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (bytes_received == -1) {
perror("recv");
exit(EXIT_FAILURE);
}
buffer[bytes_received] = '\0'; // Null-terminate the received string
printf("Received: %s\n", buffer);
close(sockfd);
return 0;
}
recvmsg 示例 (套接字通信)
这个示例展示了如何使用recvmsg
来从一个套接字接收数据,并且处理可能存在的辅助数据(例如控制消息)。
请注意,这个示例假设您已经建立了一个TCP连接,并且服务器正在发送数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
int main() {
int sockfd;
struct sockaddr_in server_addr;
char buffer[1024];
struct msghdr msg;
struct iovec iov;
char control_buffer[256];
struct cmsghdr *cmsg;
int flags;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 配置服务器地址信息
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 连接到服务器
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
// 初始化消息结构
memset(&msg, 0, sizeof(msg));
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);
// 接收数据
ssize_t bytes_received = recvmsg(sockfd, &msg, 0);
if (bytes_received == -1) {
perror("recvmsg");
exit(EXIT_FAILURE);
}
// 处理接收到的数据
buffer[bytes_received] = '\0'; // Null-terminate the received string
printf("Received: %s\n", buffer);
// 检查是否有辅助数据
if (msg.msg_flags & MSG_CTRUNC) {
fprintf(stderr, "Control message was truncated\n");
exit(EXIT_FAILURE);
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fds = (int *) CMSG_DATA(cmsg);
int num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
// 处理文件描述符
for (int i = 0; i < num_fds; i++) {
printf("Received file descriptor: %d\n", fds[i]);
}
}
}
// 关闭套接字
close(sockfd);
return 0;
}
在这个示例中,我们定义了一个msghdr
结构体来配置接收操作,包括数据缓冲区、辅助数据缓冲区以及它们的大小。recvmsg
函数接收这些参数,并返回接收到的数据量和可能存在的控制消息。
我们还检查了MSG_CTRUNC
标志,它表示控制消息被截断。如果设置了此标志,我们需要处理错误。然后,我们遍历了所有接收到的控制消息,并检查是否有SCM_RIGHTS
类型的消息,它通常用于传递文件描述符。
这只是一个基本的示例,实际应用中可能需要对错误进行更详细的处理,并且可能需要根据具体的应用协议来解析接收到的数据。此外,对于非阻塞套接字,您可能还需要检查EAGAIN
或EINTR
错误,并相应地重试接收操作。