我正在研究一个相当复杂的C程序,该程序设计为在Linux下运行,该程序在很大程度上依赖于套接字提供的辅助数据。

我实际上正在经历一种奇怪的行为,顺便说一句,这似乎仅发生在我的程序上(其他示例代码,如this one,都在正常工作)。
我通过定义以下内容来使用辅助数据:

struct msghdr mhdr;
struct iovec iov;
struct cmsghdr *cmsg = NULL;
struct sockaddr_ll addrll;
socklen_t addrllLen=sizeof(addrll);
unsigned char packet[PACKET_SIZE_MAX];
char ctrlBuf[CMSG_SPACE(sizeof(struct scm_timestamping))]; // For example, in order to retrieve timestamps

然后,通过设置:
    memset(&mhdr,0,sizeof(mhdr));

    iov.iov_base=packet;
    iov.iov_len=sizeof(packet);

    mhdr.msg_name=&(addrll); // This is a raw socket
    mhdr.msg_namelen=addrllLen; // This is a raw socket

    mhdr.msg_control=ctrlBuf;
    mhdr.msg_controllen=sizeof(ctrlBuf);

    mhdr.msg_iov=&iov;
    mhdr.msg_iovlen=1;

    mhdr.msg_flags=0;

然后,在设置一些套接字选项以检索有用的辅助数据(例如,通过设置sendto()来获取软件或硬件时间戳)后,我便使用recvmsg()SO_TIMESTAMPING发送和接收数据。我能够验证我打开的套接字实际上支持这些选项。

当我尝试使用类似以下代码的代码来提取辅助数据时,有时会出现问题,例如,使用发送/接收时间戳来提取struct scm_timestamping:
for(cmsg=CMSG_FIRSTHDR(&mhdr);cmsg!=NULL;cmsg=CMSG_NXTHDR(&mhdr, cmsg)) {
    if(cmsg->cmsg_level==SOL_SOCKET && cmsg->cmsg_type==SO_TIMESTAMPING) {
        hw_ts=*((struct scm_timestamping *)CMSG_DATA(cmsg));
    }
}

有时候,CMSG_FIRSTHDR(&mhdr)返回一个空指针,而返回的mhdr.msg_controllen0,以至于我无法提取任何时间戳。
有时,在同一台PC上并使用相同的NIC,一切都很好。

所以,我的问题是:一般情况下,返回的mhdr.msg_controllen可以是0吗?这可能是由于struct msghdr的定义中的问题引起的吗?还是这是由于某种内核问题引起的?

最佳答案

控制消息缓冲区的大小可能很小,因为它没有对齐,并且第一个对齐边界以下的部分将不可用。我不确定设置缓冲区的正确惯用方式是什么(可能使用malloc是避免代码的唯一方法,严格来说,由于对齐和有效类型/“别名”冲突,避免使用UB),而是添加了_Alignof(max_align_t) (或sizeof(max_align_t))到缓冲区大小可能是一种解决方案。不过,我想知道什么是公认的“正确”方法。

cmsg(3) man page似乎建议使用统一的对齐方式:

10-08 09:25
查看更多