我试图让网卡硬件接收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, ×tamping_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
从套接字读取。