【发布时间】:2012-07-31 00:12:52
【问题描述】:
我正在尝试获取传出数据包的准确时间戳(使用原始套接字发送)。根据Linux/Documentation/networking/timestamping.txt,“对于发送时间戳,传出的数据包将循环回套接字的错误队列,并附上发送时间戳。可以使用 recvmsg(flags=MSG_ERRQUEUE) 接收它。”。
不幸的是,recvmsg 在原始套接字上调用时总是返回-1(使用socket(PF_INET, SOCK_RAW, IPPROTO_RAW) 创建并且SO_TIMESTAMP 设置为1 和setsockopt)。我究竟做错了什么?有没有更好的方法来获取传出数据包的准确时间戳?
附录(信息):
我还尝试从通过 UDP 套接字发送的数据包中获取时间戳(源代码如下),recvmsg 返回-1:错误是“资源暂时不可用”(EAGAIN)。
附录(源代码):
#include <arpa/inet.h>
#include <linux/net_tstamp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
void die(char* s)
{
perror(s);
exit(1);
}
int main(int argc, char* argv[])
{
char* destination_ip = "10.0.0.1";
int destination_port = 1234;
int sock;
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
die("socket()");
}
int timestamp_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, ×tamp_flags, sizeof(timestamp_flags)) < 0) {
die("setsockopt()");
}
struct sockaddr_in si_server;
memset(&si_server, 0, sizeof(si_server));
si_server.sin_family = AF_INET;
si_server.sin_port = htons(destination_port);
if (inet_aton(destination_ip, &si_server.sin_addr) == 0) {
die("inet_aton()");
}
const int buffer_len = 256;
char buffer[buffer_len];
const int n_packets = 10;
for (int i = 0; i < n_packets; ++i) {
sprintf(buffer, "Packet %d", i);
if (sendto(sock, buffer, buffer_len, 0, (const sockaddr*) &si_server, sizeof(si_server)) < 0) {
die("sendto()");
}
// Obtain the sent packet timestamp.
char data[256];
struct msghdr msg;
struct iovec entry;
struct sockaddr_in from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
int res;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = data;
entry.iov_len = sizeof(data);
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
if (recvmsg(sock, &msg, MSG_ERRQUEUE) < 0) {
die("recvmsg()");
}
}
return 0;
}
【问题讨论】:
-
如果你使用 SOCK_STREAM 你会得到不同的时间?
-
顺便说一句,我使用 setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));禁用 Nagel 算法。我认为你也应该这样做。但我想知道你是否可以连续使用两个 setsockopt?
-
显然你也可以在你想要的任何级别添加 sockopt,以获得真正的时间我猜你会在 IPPROTO_RAW 级别设置 SO_TIMESTAMP
-
从错误队列中读取不是非阻塞的吗?我猜你会得到 EAGAIN,因为当你调用 recvmsg() 时时间戳还没有准备好。也许尝试使用 poll() 并等待 POLLERR。
标签: c linux timestamp raw-sockets