将期货连接拆分为接收器和流,并在两个不同的任务中使用它们

时间:2017-10-03 05:00:03

标签: rust

我正在使用websocket库试验期货API。我有这段代码:

use futures::future::Future;
use futures::future;
use futures::sink::Sink;
use futures::stream::Stream;
use futures::sync::mpsc::channel;
use futures::sync::mpsc::{Sender, Receiver};
use tokio_core::reactor::Core;

use websocket::{ClientBuilder, OwnedMessage};

pub fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let handle_clone = handle.clone();

    let (send, recv): (Sender<String>, Receiver<String>) = channel(100);

    let f = ClientBuilder::new("wss://...")
        .unwrap()
        .async_connect(None, &handle_clone)
        .map_err(|e| println!("error: {:?}", e))

        .map(|(duplex, _)| duplex.split())
        .and_then(move |(sink, stream)| {

            // this task consumes the channel, writes messages to the websocket
            handle_clone.spawn(future::loop_fn(recv, |recv: Receiver<String>| {
                sink.send(OwnedMessage::Close(None))
                    .and_then(|_| future::ok(future::Loop::Break(())))
                    .map_err(|_| ())
            }));

            // the main tasks listens the socket
            future::loop_fn(stream, |stream| {
                stream
                    .into_future()
                    .and_then(|_| future::ok(future::Loop::Break(())))
                    .map_err(|_| ())
            })
        });

    loop {
        core.turn(None)
    }
}

连接到服务器后,我想运行“监听器”和“发送者”任务,而不会阻止另一个。问题是我无法在新任务中使用sink,它失败了:

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> src/slack_conn.rs:29:17
   |
25 |         .and_then(move |(sink, stream)| {
   |                          ---- captured outer variable
...
29 |                 sink.send(OwnedMessage::Close(None))
   |                 ^^^^ cannot move out of captured outer variable in an `FnMut` closure

我可以直接使用duplex发送和接收,但这会导致更糟糕的错误。

有关如何使这项工作的任何想法?实际上,我对任何允许我非阻塞地连接到服务器并产生两个异步任务的futures代码感到满意:

  • 从连接中读取并执行某些操作(打印到屏幕等)的人。
  • 从mpsc频道读取并写入连接的

如果我必须用不同的风格写它,那就好了。

1 个答案:

答案 0 :(得分:2)

SplitSink实施Sink,定义send取得所有权:

fn send(self, item: Self::SinkItem) -> Send<Self>
where
    Self: Sized,

另一方面,loop_fn要求可以多次调用闭包。这两件事基本上是不相容的 - 你怎么能多次调用一些需要消耗价值的东西?

这是一段完全未经测试的代码编译 - 我没有流氓WebSocket服务器。

#[macro_use]
extern crate quick_error;

extern crate futures;
extern crate tokio_core;
extern crate websocket;

use futures::{Future, Stream, Sink};
use futures::sync::mpsc::channel;
use tokio_core::reactor::Core;

use websocket::ClientBuilder;

pub fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let (send, recv) = channel(100);

    let f = ClientBuilder::new("wss://...")
        .unwrap()
        .async_connect(None, &handle)
        .from_err::<Error>()
        .map(|(duplex, _)| duplex.split())
        .and_then(|(sink, stream)| {
            let reader = stream
                .for_each(|i| {
                    println!("Read a {:?}", i);
                    Ok(())
                })
                .from_err();

            let writer = sink
               .sink_from_err()
               .send_all(recv.map_err(Error::Receiver))
               .map(|_| ());

            reader.join(writer)
        });

    drop(send); // Close the sending channel manually

    core.run(f).expect("Unable to run");
}

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        WebSocket(err: websocket::WebSocketError) {
            from()
            description("websocket error")
            display("WebSocket error: {}", err)
            cause(err)
        }
        Receiver(err: ()) {
            description("receiver error")
            display("Receiver error")
        }
    }
}

在实施过程中突出的要点是:

  1. 所有必须最终成为Future
  2. 方式更容易定义错误类型并转换为
  3. 了解ItemError相关类型是否为&#34;对&#34;很棘手我最终做了很多&#34;类型的断言&#34; ({ let x: &Future<Item = (), Error = ()> = &reader; })。