我有一个永远不会完成的东京未来(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(())
}));
}
使用此版本的未来会显示问题:
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))
});