在Rust中访问unix socket时间戳



我试图让网卡硬件接收TcpStream消息的时间戳。我在C中找到了一些代码示例(https://stackoverflow.com/a/42210708和https://stackoverflow.com/a/47329376/9518712),但我正在努力在Rust中实现它们。

我也想知道是否有一个更好的抽象通过socket辅助数据https://doc.rust-lang.org/std/os/unix/net/struct.SocketAncillary.html

我将非常感谢任何简单的工作代码示例。

let (mut socket, response) = connect(Url::parse("wss://myurl.com/ws")?)?;
let stream = socket.get_mut();
let tcp_stream = match stream {
    MaybeTlsStream::Rustls(ref s) => &s.sock,
    _ => panic!(),
};
let fd = tcp_stream.as_raw_fd();
let msg = socket.read_message().expect("Error reading message");
println!("Received: {}", msg);
// ??? code to get socket timestamp ???

要使用链接答案中描述的SO_TIMESTAMPING,您可能需要使用nix crate。

碰巧包含以下示例:

// Set up
let message = "Ohayō!".as_bytes();
let in_socket = socket(
    AddressFamily::Inet,
    SockType::Datagram,
    SockFlag::empty(),
    None).unwrap();
setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
bind(in_socket, &localhost).unwrap();
let address: SockaddrIn = getsockname(in_socket).unwrap();
// Get initial time
let time0 = SystemTime::now();
// Send the message
let iov = [IoSlice::new(message)];
let flags = MsgFlags::empty();
let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
assert_eq!(message.len(), l);
// Receive the message
let mut buffer = vec![0u8; message.len()];
let mut cmsgspace = cmsg_space!(TimeVal);
let mut iov = [IoSliceMut::new(&mut buffer)];
let r = recvmsg::<SockaddrIn>(in_socket, &mut iov, Some(&mut cmsgspace), flags)
    .unwrap();
let rtime = match r.cmsgs().next() {
    Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime,
    Some(_) => panic!("Unexpected control message"),
    None => panic!("No control message")
};
// Check the final time
let time1 = SystemTime::now();
// the packet's received timestamp should lie in-between the two system
// times, unless the system clock was adjusted in the meantime.
let rduration = Duration::new(rtime.tv_sec() as u64,
                              rtime.tv_usec() as u32 * 1000);
assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
// Close socket
nix::unistd::close(in_socket).unwrap();

要使其适应使用标准库的代码框架:

use std::io::IoSliceMut;
use nix::sys::socket::{setsockopt, TimestampingFlag};
use nix::sys::socket::sockopt::Timestamping;
let fd = tcp_stream.as_raw_fd();
// get both software and hardware timing data for received packets
let timestamp_options = 
    TimestampingFlag::SOF_TIMESTAMPING_RX_HARDWARE 
    | TimestampingFlag::SOF_TIMESTAMPING_RX_SOFTWARE;
setsockopt(fd, Timestamping, &timestamping_options)?;
// assuming your packet can be as large as 32 bytes, update if needed
let mut buffer = vec![0u8; 32];
let mut cmsgspace = nix::cmsg_space!(TimeVal);
let mut iov = [IoSliceMut::new(&mut buffer)];
let r = recvmsg::<SockaddrStorage>(
    tcp_stream.as_raw_fd(), 
    &mut iov, 
    Some(&mut cmsgspace), 
    MsgFlags::empty())?;
if let Some(ControlMessageOwned::ScmTimestamp(rtime)) = r.cmsgs().next() {
    // use rtime as you like
};

基本上,你必须注意的主要事情是你必须使用系统调用recvmsg从套接字读取。

最新更新