【问题标题】:iov and msg_control in sendmsg and recvmsgsendmsg 和 recvmsg 中的 iov 和 msg_control
【发布时间】:2021-01-09 18:10:53
【问题描述】:

iov.iov_base 和 msg.msg_control 有什么区别? 我在看一些代码示例(ipuitls 开源 ping) 使用 sendmsg 发送数据时,数据包在 iov.iov_base 中设置 使用 recvmsg 读取数据时,数据包直接从 msg->msg_control 读取。

struct iovec 和 struct msghdr 是什么关系?读取/发送数据有区别吗?

抱歉这个愚蠢的问题。到目前为止我还没有找到答案,我很困惑。 谢谢!

【问题讨论】:

    标签: c linux sockets recvmsg sendmsg


    【解决方案1】:

    辅助数据或控制消息(.msg_control 处的.msg_controllen 字节)是由内核提供或验证的数据,而正常的有效负载(在iovecs 中)只是从另一个端点接收的数据,未经验证和未经检查由内核(如果协议有校验和,则校验和除外)。

    对于 IP 套接字(请参阅man 7 ip),有几个套接字选项会导致内核在收到的消息上提供辅助数据。例如:

    • IP_RECVORIGDSTADDR 套接字选项告诉内核提供IP_ORIGDSTADDR 类型的辅助消息(以struct sockaddr_in 作为数据),标识接收到的数据报的原始目标地址

    • IP_RECVOPTS 套接字选项告诉内核提供一个IP_OPTIONS 类型的辅助消息,其中包含用于传入数据报的所有 IP 选项标头(对于 IPv4 最多 40 个字节)

    Ping 和 traceroute 使用 IP 上的 ICMP 消息;详情请参阅man 7 icmp(和man 7 raw)。

    由于大多数 ICMP 响应不包含发送者填写的有用数据,iovecs 通常不包含任何有趣的内容。相反,有趣的数据在 IP 消息头和选项中。

    例如,ICMP Echo reply 数据包仅包含 8 个字节(64 位):8 位类型 (0)、8 位代码 (0)、16 位校验和、16 位 id 和 16 位序列号。要获取包含感兴趣字段的 IP 标头,您需要内核将它们作为辅助数据控制消息提供。


    背景:

    sendmsg() 和相关手册页中所述,我们有

    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
    
    struct msghdr {
        void         *msg_name;       /* Optional address */
        socklen_t     msg_namelen;    /* Size of address */
        struct iovec *msg_iov;        /* Scatter/gather array */
        size_t        msg_iovlen;     /* # elements in msg_iov */
        void         *msg_control;    /* Ancillary data */
        size_t        msg_controllen; /* Ancillary data buffer len */
        int           msg_flags;      /* Flags (unused) */
    };
    
    struct iovec {
        void         *iov_base;       /* Starting address */
        size_t        iov_len;        /* Number of bytes to transfer */
    };
    

    man 3 cmsg 描述如何构造和访问这些辅助数据,

    struct cmsghdr {
        size_t        cmsg_len;    /* Data byte count, including header
                                      (type is socklen_t in POSIX) */
        int           cmsg_level;  /* Originating protocol */
        int           cmsg_type;   /* Protocol-specific type */
        unsigned char cmsg_data[]; /* Data itself */
    };
    
    struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
    struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
    size_t          CMSG_ALIGN(size_t length);
    size_t          CMSG_SPACE(size_t length);
    size_t          CMSG_LEN(size_t length);
    unsigned char  *CMSG_DATA(struct cmsghdr *cmsg);
    

    这些辅助数据消息始终与当前架构充分对齐(以便可以直接访问数据项),因此要构造适当的辅助消息(SCM_CREDENTIALS 以通过 Unix 域传递用户、组和进程 ID 信息socket 或 SCM_RIGHTS 来传递文件描述符),必须使用这些宏。 man 3 cmsg 手册页包含这些示例代码。

    可以这么说,要遍历给定消息 (struct msghdr msg) 中的每个辅助数据部分,您使用的东西可以归结为

    char *const  end = (char *)msg.msg_control + msg.msg_controllen;
    char        *ptr = (char *)msg.msg_control;
    
    for (char *ptr = (char *)msg.msg_control;  ptr < end;
               ptr += ((struct cmsghdr *)ptr)->cmsg_len) {
        struct cmsghdr *const cmsg = (struct cmsghdr *)ptr;
    
        /* level is cmsg->cmsg_level and type is cmsg->cmsg_type, and
           cmsg->cmsg_data is sufficiently aligned for the level and type,
           so you can use ((datatype *)(cmsg->cmsg_data)) to obtain a pointer
           to the type corresponding to this level and type ancillary payload.
           The exact size of the payload is
               (cmsg->cmsg_len - sizeof (struct cmsghdr))
           so e.g. an SCM_RIGHTS ancillary message, with
               cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS
           has exactly
               (cmsg->cmsg_len - sizeof (struct cmsghrd)) / sizeof (int)
           new file descriptors as a payload.
        */
    }
    

    【讨论】:

      猜你喜欢
      • 2013-11-02
      • 2016-11-20
      • 2018-07-29
      • 1970-01-01
      • 2014-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      相关资源
      最近更新 更多