通过 ioctl 调用套接字文件描述符获取数据包时间戳

Posted

技术标签:

【中文标题】通过 ioctl 调用套接字文件描述符获取数据包时间戳【英文标题】:Obtain packet timestamp through ioctl call on socket file descriptor 【发布时间】:2016-07-02 16:34:42 【问题描述】:

我正在开发一个运行嵌入式 linux 的系统。我正在尝试从我在套接字上接收的流中获取数据包时间戳

创建套接字后,我执行以下操作:

if (fd != -1) 
    int enabled = 1;
    setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enabled, sizeof(enabled);

绑定套接字之后,套接字的类型为 SOCK_STREAM。通过调用函数recv(fd, buf, size, 0),我成功地在套接字上接收到数据。现在为了获取接收数据的时间戳,我目前正在尝试以下操作:

ret = recv(fd, buf, size, 0);
if (ret > 0) 
    struct timeval tv_ioctl;
    tv_ioctl.tv_sec = 0;
    tv_ioctl.tv_usec = 0;
    int error = ioctl(fd, SO_TIMESTAMP, &tv_ioctl);
    printf("%ld.%ld - error = %d", (long int)tv_ioctl.tv_sec, 
            (long int)tv_ioctl.tv_usec, error);

printf 语句的输出总是如下:

0.0 误差 = -1

error = -1 表示 ioctl 调用失败。我已经使用getsockopt 进行了测试,以检查是否设置了 SO_TIMESTAMP 选项,getsockopt 为选项 SO_TIMESTAMP 返回 0,因此它似乎设置正确。我在这里有点迷茫,我该如何进一步调查为什么 ioctl 调用似乎失败了?

【问题讨论】:

看THIS,好像SO_TIMESTAMP是用来设置选项的,不是用来取数据的。您必须使用recvmsg 获取信息。 当您从系统调用中收到错误时,您应该打印errnostrerror() 或使用perror() 为您执行此操作。正如您所发现的,-1 并不能告诉您任何有用的信息。 【参考方案1】:

在套接字上检索最新时间戳的 ioctl 是SIOCGSTAMPSO_TIMESTAMP 是一个套接字选项,而不是一个 ioctl。您的代码应为:

int error = ioctl(fd, SIOCGSTAMP, &tv_ioctl);
                      ^^^^^^^^^^

检索时间戳的替代方法是将recv 更改为recvmmsg 并从辅助数据中提取时间戳。这更有效,因为它涉及更少的系统调用 (Socket reading and timestamps);不过,ioctl 更简单。

请注意,SIOCGSTAMP 和 SO_TIMESTAMP 是互斥的 - 如果您要使用 SIOCGSTAMP,您应该禁用 SO_TIMESTAMP(使用enabled = 0)。这是因为 SO_TIMESTAMP 指示内核通过 recvmmsg 辅助数据而不是通过 SIOCGSTAMP 提供时间戳。

【讨论】:

我现在正在尝试使用 SIOCGSTAMP,我已使用 setsockopt 将 SO_TIMESTAMP 选项设置为 0。但是 ioctl 调用仍然返回 -1。我必须启用 SIOCGSTAMP 还是设备驱动程序不支持它? @Gio errno 设置为什么?我知道 SIOCGSTAMP 会失败的唯一原因是如果启用了 SO_TIMESTAMP,在这种情况下它将被设置为 ENOENT。 @Gio 表示该套接字上没有可用的时间戳。您正在做的事情并没有明显的问题 - 也许尝试运行内核文档 kernel.org/doc/Documentation/networking/timestamping/… 中的时间戳示例,看看您是否可以让其中任何一个工作。 我发现了它为什么不起作用。 SIOCGSTAMP 仅适用于 UDP 数据包或 RAW 套接字,但不适用于 TCP。 @kirbo 要求 TCP 的时间戳是没有意义的,因为到达的数据包和可用的数据之间没有直接的关联。如果你真的想要 TCP 的时间戳,你必须使用 RAW 套接字并实现你自己的 TCP 堆栈(或使用用户空间 TCP 库)。

以上是关于通过 ioctl 调用套接字文件描述符获取数据包时间戳的主要内容,如果未能解决你的问题,请参考以下文章

linux网卡信息获取

(10)Linux 网络编程之ioctl函数

为啥 ioctl I_SENDFD 不返回任何权限(EPERM)? [复制]

linux ioctl 方法

Mac 沙盒和文件 ioctl

第3章 文件I/O_dupdup2fcntl和ioctl函数