我有一个永远无法完成的Tokio未来(rx
是Receiver
,sock
是Tokio UdpSocket
)。它基本上是从数据包队列中读取数据包并通过套接字传输它们:
poll_fn(move || {
match try_ready!(rx
.poll()
.map_err(|_e| tokio::io::Error::new(tokio::io::ErrorKind::Other, "Poll error")))
{
Some((packet, to)) => {
println!(
"Rx: Received {} bytes for {}: {:?}",
packet.len(),
to,
packet.as_slice(),
);
try_ready!(sock.poll_send_to(packet.as_slice(), &to));
println!("Sent");
}
None => println!("Rx end"),
}
Ok(futures::Async::Ready(()))
})
.map_err(|e: tokio::io::Error| println!("Error: {:?}", e))
它执行到poll_send_to
行(println!
之前的poll_send_to
行,之后的println!
行),然后一直等待直到不发送数据包。
我用以下内容替换了上面的内容,以确保它不是套接字问题(以前我认为是不稳定的通知有一些问题):
poll_fn(move || {
let packet = vec![0;10];
let to = SocketAddr::from_str("127.0.0.1:8001").expect("Parse error");
try_ready!(sock.poll_send_to(packet.as_slice(), &to));
Ok(futures::Async::Ready(()))
})
.map_err(|e: tokio::io::Error| println!("Error: {:?}", e))
这个未来很好,它按预期方式发送了数据包并退出了程序。
鉴于rx
可以成功poll
并打印println
消息,因此我认为消息通道没有问题。考虑到第二个未来的工作,我也不认为套接字有问题。我直接通过Wireshark观察数据包,因此我的观察结果也不是问题。
我对Rust和Tokio还是很陌生,所以我有可能忽略了一些基本事实(例如,同一将来不能两次try_ready
,将来就不会从以前的停滞状态恢复过来)等)。
您能帮我找出第一个未来的问题吗?
use futures::future::lazy;
use futures::stream::Stream;
use futures::try_ready;
use std::net::SocketAddr;
use std::str::FromStr;
use tokio;
use tokio::net::UdpSocket;
use tokio::prelude::future::poll_fn;
use tokio::prelude::Future;
fn main() {
let mut sock = UdpSocket::bind(&SocketAddr::from_str("127.0.0.1:8000").expect("Parse error"))
.expect("Bind error");
let (mut tx, mut rx) = tokio::sync::mpsc::channel::<(Vec<u8>, SocketAddr)>(2000);
tokio::run(lazy(move || {
//----------------- This future works ----------------//
// tokio::spawn(
// poll_fn(move || {
// let packet = vec![70; 10];
// let to = SocketAddr::from_str("127.0.0.1:8001").expect("Parse error");
// try_ready!(sock.poll_send_to(packet.as_slice(), &to));
// Ok(futures::Async::Ready(()))
// })
// .map_err(|e: tokio::io::Error| println!("Error: {:?}", e)),
// );
//----------------- This future doesn't ----------------//
tokio::spawn(
poll_fn(move || {
match try_ready!(rx
.poll()
.map_err(|_e| tokio::io::Error::new(tokio::io::ErrorKind::Other, "Poll error")))
{
Some((packet, to)) => {
// This is printed
println!(
"Rx: Received {} bytes for {}: {:?}",
packet.len(),
to,
packet.as_slice(),
);
try_ready!(sock.poll_send_to(packet.as_slice(), &to));
// This is never printed
println!("Sent");
}
None => println!("Rx end"),
}
Ok(futures::Async::Ready(()))
})
.map_err(|e: tokio::io::Error| println!("Error: {:?}", e)),
);
//----------------- This future queues a packet ----------------//
tokio::spawn(
poll_fn(move || {
try_ready!(tx.poll_ready());
tx.try_send((
vec![70; 10],
SocketAddr::from_str("127.0.0.1:8001").expect("Parse error"),
))
.expect("Send error");
// Wait permanently so message channel doesn't get disconnected
// Achieved differently in production
Ok(futures::Async::NotReady)
})
.map_err(|e: tokio::sync::mpsc::error::SendError| println!("Error: {:?}", e)),
);
Ok(())
}));
}
答案 0 :(得分:1)
使用此版本的将来会显示此问题:
tokio::spawn(
future::poll_fn(move || {
eprintln!("Starting poll_fn");
let from_channel = rx
.poll()
.map_err(|_e| tokio::io::Error::new(tokio::io::ErrorKind::Other, "Poll error"));
if let Some((packet, to)) = futures::try_ready!(dbg!(from_channel)) {
futures::try_ready!(dbg!(sock.poll_send_to(packet.as_slice(), &to)));
}
Ok(futures::Async::Ready(()))
})
.map_err(|e: tokio::io::Error| println!("Error: {:?}", e)),
);
这是稍作清理的输出:
Starting poll_fn
[src/main.rs:21] from_channel = Ok(NotReady)
Starting poll_fn
[src/main.rs:21] from_channel = Ok(Ready(Some(/* ... */)))
[src/main.rs:22] sock.poll_send_to(packet.as_slice(), &to) = Ok(NotReady)
Starting poll_fn
[src/main.rs:21] from_channel = Ok(NotReady)
换句话说:
简而言之,您在未来中无法正确维护状态机。您需要知道最后一次运行的距离是多少,然后在下次运行的时候开始。
async
/ await
语法被人们高度期待是有原因的:它将为您编写这些状态机。
我不知道为什么为什么您选择使用较低级别的基于poll
的界面。我会使用基于Future
的更高级别的
tokio::spawn({
rx.fold(sock, |sock, (packet, to)| {
sock.send_dgram(packet, &to)
.inspect(|_| println!("Sent it!"))
.map(|(sock, _)| sock)
.map_err(|e| panic!("Error: {:?}", e))
})
.map(drop)
.map_err(|e| panic!("Error: {:?}", e))
});
基于
Future
的接口[...]错误地破坏了套接字(和缓冲区)
这是使用基于poll
的界面的一个很好的理由,但是我仍然会花足够长的时间来实现自己的未来。像这样:
struct X(UdpSocket);
struct XSendGram<D> {
sock: Option<UdpSocket>,
data: D,
addr: SocketAddr,
}
impl X {
fn send_dgram<D>(self, data: D, addr: SocketAddr) -> XSendGram<D> {
XSendGram {
sock: Some(self.0),
data,
addr,
}
}
}
impl<D> Future for XSendGram<D>
where
D: AsRef<[u8]>,
{
type Item = (X, usize);
type Error = (X, std::io::Error);
fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
let mut sock = self.sock.take().expect("Future called after success or failure");
match sock.poll_send_to(self.data.as_ref(), &self.addr) {
Ok(Async::Ready(bytes)) => Ok(Async::Ready((X(sock), bytes))),
Ok(Async::NotReady) => {
self.sock = Some(sock); // Restore it for the next call
Ok(Async::NotReady)
}
Err(e) => Err((X(sock), e)),
}
}
}
tokio::spawn({
rx.fold(X(sock), |sock, (packet, to)| {
sock.send_dgram(packet, to)
.inspect(|(_, n)| println!("Sent {} bytes", n))
.then(|r| match r {
Ok((sock, _)) | Err((sock, _)) => future::ok(sock),
})
})
.map(drop)
.map_err(|e| panic!("Error: {:?}", e))
});