首页 > 其他分享 >readv、recv和recvmsg三个函数的区别

readv、recv和recvmsg三个函数的区别

时间:2024-02-07 21:45:51浏览次数:25  
标签:addr iov server recvmsg readv msg include recv

目录


概述

readv、recv和recvmsg三个函数都是用于从文件或套接字接收数据的函数,但它们在功能和使用场景上存在一些区别。

  1. readv函数:
    readv函数主要用于从文件描述符读取数据到多个缓冲区中。它允许在一次系统调用中读取多个缓冲区的数据,这有助于减少多次系统调用的开销,提高读操作的性能。readv函数通常用于文件I/O操作。

  2. recv函数:
    recv函数用于从套接字接收数据。它提供了类似于read函数的功能,但还额外提供了一个flags参数,用于控制一些特殊的行为。例如,通过设置MSG_PEEK标志,recv可以预览数据而不将其从缓冲区中移除;通过设置MSG_WAITALL标志,recv可以阻止在接收的数据少于预期时返回。recv函数通常用于网络编程中的TCP和UDP套接字。

  3. 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类型的消息,它通常用于传递文件描述符。

这只是一个基本的示例,实际应用中可能需要对错误进行更详细的处理,并且可能需要根据具体的应用协议来解析接收到的数据。此外,对于非阻塞套接字,您可能还需要检查EAGAINEINTR错误,并相应地重试接收操作。

标签:addr,iov,server,recvmsg,readv,msg,include,recv
From: https://www.cnblogs.com/yubo-guan/p/18011326

相关文章

  • 两个线程共享一个套接字,其中一个线程使用sendmsg函数不断发送消息到该套接字,另一个线
    以下是使用C语言写的一段代码,实现两个线程共享一个套接字,其中一个线程使用sendmsg函数不断发送消息到该套接字,另一个线程使用recvmsg函数不断接收该套接字的消息,并打印出来的功能点击查看代码#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.......
  • 【MySQL】MVCC机制、ReadView数据结构、匹配规则详解
    (目录)MySQLMVCC机制1.隔离级别在MySQLInnoDB存储引擎下,RC、RR基于MVCC(多版本并发控制)进行并发事务控制MVCC是**基于”数据版本”**对并发事务进行访问2.场景分析UNDO_LOG不是会被删除吗?中间数据万一被删了版本链不就断了?UNDO_LOG版本链不是立即删除,MySQL确保版......
  • Git 克隆错误 error: RPC failed; curl 28 Recv failure: Connection was reset
    在网络情况不稳定下克隆项目时,可能会出现下图中的错误。问题原因:http缓存不够或者网络不稳定等。我也是找了好多博客资料,终于解决了解决方法打开cmd,修改git配置(加大httpBuffer)即可。gitconfig--globalhttp.postBuffer524288000  我在解决这个问题之前clone一......
  • SpringBoot复习(54)用于事务处理的InfrastructureAdvisorAutoProxyCreator BeanPostProc
    从类的继承关系看InfrastructureAdvisorAutoProxyCreator是一个BeanPostProcessor.@EnableTransactionManagement注解导入了TransactionManagementConfigurationSelector类,它的代码如下:这个ImportSelector的selectImports方法返回了一个AutoProxyRegistrar,AutoProxyRegistrar代码......
  • 无涯教程-Perl - recv函数
    描述ThisfunctionreceivesamessageonSOCKETattemptingtoreadLENGTHbytes,placingthedatareadintovariableSCALAR.TheFLAGSargumenttakesthesamevaluesastherecvfrom()systemfunction,onwhichthefunctionisbased.Whencommunicatingwith......
  • socket之send和recv原理剖析
    TCPsocket的发送和接收缓冲区当创建一个TCPsocket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。 send或sendall方法是不是直接把数据发给服务端?不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据......
  • UDP recvfrom error错误10022
    fromlen参数没有初始化from参数没有设置正确,也就是结构问题终于发现原来是bind函数的问题。由于在文件开头使用了usingnamespacestd导致默认的bind变成了functional中的那个,而不是socket的bind,导致绑定一直没有成功。当然,也可能是套接字端口被占用......
  • MVCC并发版本控制之重点ReadView
    MVCC并发版本控制本文大部分来自《MySQL是怎样运行的》,这里只是简单总结,用于各位回忆和复习。版本链对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列(不知道的快去看《MySQL是怎样运行的》)trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把......
  • Linux UDP协议栈中的片段分析 - udp_recvmsg
    udp_recvmsg(struct kiocb *iocb, struct sock *sk, structsize_t len, int noblock, int flags, int调用了:err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,copied);调用了:memcpy_toiovec(to, skb->data + o, copy);......
  • 分散输入和集中输出------readv() 、 writev()
    参考:https://blog.csdn.net/zhizhengguan/article/details/117173049//功能:将数据从文件描述符读到分散的内存块中,即分散读。ssize_treadv(intfd,conststructiovec*iov,intiovcnt);//iovcnt代表选择iov中的几块内存,而不是选择第几块内存//功能:将多块分散的内存数......