如何使用Tokio实现基于拉的系统?

时间:2019-09-10 19:04:54

标签: rust rust-tokio

我想在服务器和客户端之间实现基于拉式的系统,其中服务器仅在客户端要求时才推送数据。

我与Tokio一起玩,并且能够创建一个基于推式的系统,该系统能够以1ms的间隔推入一个字符串。

let done = listener
    .incoming()
    .for_each(move |socket| {
        let server_queue = _cqueue.clone();
        let (reader, mut writer) = socket.split();
        let sender = Interval::new_interval(std::time::Duration::from_millis(1))
            .for_each(move |_| {
                writer
                    .poll_write(server_queue.pull().borrow())
                    .map_err(|_| {
                        tokio::timer::Error::shutdown();
                    })
                    .unwrap();
                return Ok(());
            })
            .map_err(|e| println!("{}", e));
        ;
        tokio::spawn(sender);
        return Ok(());
    })
    .map_err(|e| println!("Future_error {}", e));

有没有一种方法可以仅在客户要求时发送而不必使用阅读器?

1 个答案:

答案 0 :(得分:1)

让我们回想一下可能导致这种“数据发送”的事件。您可以想到多种方式:

  • 客户端连接到服务器。按照合同,这是“寻求数据”。您已经实现了这种情况
  • 客户端在连接客户端和服务器的套接字/管道上发送带内消息。为此,您需要使用AsyncRead的{​​{1}}部分,已经使用的socket部分,并建立一个双工频道,以便您可以同时阅读和交谈
  • 客户端通常在另一个原型主机端口三元组上使用不同的协议发送带外消息。您当前的服务器可以识别它,并将该数据发送给客户端。为此,您需要另一个三元组的阅读器,并且需要一个消息传递结构以将其中继到可以访问套接字的AsyncWrite部分的一个地方

简短的回答是“否”,您不能真正在不听的事件上采取行动。


  

@Shepmaster我只是想知道是否有一个现有的库可以用来“整洁”地处理

有,然后没有。

大多数库都围绕一个特定的问题。在您的情况下,您选择了通过使用TCP套接字(实现AsyncWrite)来在最低的级别上工作。

要做任何事情,您都需要做出以下决定:

  1. 一种传输格式
  2. 协议

当我需要快速而肮脏的双工流实现时,我倾向于将代码包装到其中:

AsyncRead + AsyncWrite

此结构具有许多优点,但也有一些缺点。主要优点是您可以使用与另一种语言相同的方式来处理同一对象上的读写部分 。对象本身实现use futures::sync::mpsc::{UnboundedSender, unbounded}; use std::sync::{Arc}; use futures::{Sink, Stream, Future, future, stream}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::codec::{Framed, Encoder, Decoder}; use std::io; use std::fmt::Debug; use futures_locks::{RwLock as FutLock}; enum Message<T:Send+Debug+'static> { Content(T), Done } impl<T: Send + Debug + 'static> From<T> for Message<T> { fn from(message:T) -> Message<T> { Message::Content(message) } } struct DuplexStream<T:Send+Debug+'static> { writer: Arc<FutLock<UnboundedSender<Message<T>>>>, handlers: Arc<FutLock<Option<Box<dyn Stream<Item = Message<T>, Error = ()> + Send>>>> } impl<T:Send+Debug+'static> DuplexStream<T> { pub fn from<R,U>(framed_socket: Framed<R, U>) -> Arc<DuplexStream<T>> where U: Send + Encoder<Item = T> + Decoder<Item = T> + 'static, R: Send + AsyncRead + AsyncWrite + 'static { let (tx, rx) = framed_socket.split(); // Assemble the combined upstream stream let (upstream_tx, upstream_rx) = unbounded(); let upstream = upstream_rx.take_while(|item| match item { Message::Done => future::ok(false), _ => future::ok(true) }).fold(tx, |o, m| { o.send(match m { Message::Content(i) => i, _ => unreachable!() }).map_err(|_| { () }) }).map(|e| { Message::Done }).into_stream(); // Assemble the downstream stream let downstream = rx.map_err(|_| ()).map(|r| { Message::Content(r) }).chain(stream::once(Ok(Message::Done))); Arc::new(DuplexStream { writer: Arc::new(FutLock::new(upstream_tx)), handlers: Arc::new(FutLock::new(Some(Box::new(upstream.select(downstream).take_while(|m| match m { Message::Content(_) => { future::ok(true) }, Message::Done => { future::ok(false) } }))))) }) } pub fn start(self: Arc<Self>) -> Box<dyn Stream<Item = T, Error = io::Error> + Send> { Box::new(self.handlers .write() .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "Stream closed")) .map(|mut handler| -> Box<dyn Stream<Item = T, Error = io::Error> + Send> { match handler.take() { Some(e) => Box::new(e.map(|r| match r { Message::Content(i) => i, _ => unreachable!() }).map_err(|_| io::Error::new(io::ErrorKind::NotFound, "Stream closed"))), None => Box::new(stream::once(Err(io::Error::new(io::ErrorKind::AddrInUse, "Handler already taken")))) } }).into_stream().flatten() ) } pub fn close(self: Arc<Self>) -> Box<dyn Future<Item = (), Error = io::Error> + Send> { self.inner_send(Message::Done) } pub fn send(self: Arc<Self>, message: T) -> Box<dyn Future<Item = (), Error = io::Error> + Send> { self.inner_send(message.into()) } pub fn inner_send(self: Arc<Self>, message: Message<T>) -> Box<dyn Future<Item = (), Error = io::Error> + Send> { Box::new(self.writer.write() .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "The mutex has disappeared")).and_then(|guard| { future::result(guard.unbounded_send(message).map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "The sink has gone away"))) })) } } (因为它是Clone),每个方法都可以在任何地方使用(特别是对旧Arc代码有用),只要您保留它的副本 ,并且不要调用futures它将继续运行(只要底层的close()实现仍然存在)即可。

这不能使您脱离第1点和第2点,但是您可以(并且应该)将AsyncRead + AsyncWrite用于第1点,并将第2点实现为业务逻辑。

用法示例(实际上是一个测试;-))

tokio::codec::Framed