如何在期货流中为每个项目发出TcpClient请求?

时间:2018-02-03 04:37:59

标签: asynchronous rust rust-tokio

我有一个概念项目,客户端向服务器发送一个数字( @Injectable() export class UserService { url = 'http://localhost:3000/api/'; constructor(private http: HttpClient) {} // register user register(user: User) { const userString = JSON.stringify(user); const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' // auth: 'my-token' }) }; return this.http.post(this.url + 'register', userString, httpOptions); } } ),服务器计算该值是否为素数,并返回响应(PrimeClientRequest)。我希望客户端是一个简单的CLI,它会提示用户输入一个号码,将请求发送到服务器,并显示响应。理想情况下,我想使用来自Tokio的PrimeClientResponse和来自Futures-Rs的Streams来实现这一点。

我已经使用服务编写了一个Tokio服务器,我想为客户端重复使用相同的TcpClientcodec

客户端的一部分是名为proto的函数,它返回read_prompt。本质上,它是一个无限循环,每次迭代读取Stream的某些输入。

以下是相关代码:

main.rs

stdin

cli.rs

use futures::{Future, Stream};
use std::env;
use std::net::SocketAddr;
use tokio_core::reactor::Core;
use tokio_prime::protocol::PrimeClientProto;
use tokio_prime::request::PrimeRequest;
use tokio_proto::TcpClient;
use tokio_service::Service;

mod cli;

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

    let addr_string = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let remote_addr = addr_string.parse::<SocketAddr>().unwrap();

    println!("Connecting on {}", remote_addr);
    let tcp_client = TcpClient::new(PrimeClientProto).connect(&remote_addr, &handle);

    core.run(tcp_client.and_then(|client| {
        client
            .call(PrimeRequest { number: Ok(0) })
            .and_then(|response| {
                println!("RESP = {:?}", response);
                Ok(())
            })
    })).unwrap();
}

使用上面的代码,客户端在客户端终止之前向服务器发送单个请求。我希望能够使用从use futures::{Future, Sink, Stream}; use futures::sync::mpsc; use std::{io, thread}; use std::io::{Stdin, Stdout}; use std::io::prelude::*; pub fn read_prompt() -> impl Stream<Item = u64, Error = ()> { let (tx, rx) = mpsc::channel(1); thread::spawn(move || loop { let thread_tx = tx.clone(); let input = prompt(io::stdout(), io::stdin()).unwrap(); let parsed_input = input .parse::<u64>() .map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid u64")); thread_tx.send(parsed_input.unwrap()).wait().unwrap(); }); rx } fn prompt(stdout: Stdout, stdin: Stdin) -> io::Result<String> { let mut stdout_handle = stdout.lock(); stdout_handle.write(b"> ")?; stdout_handle.flush()?; let mut buf = String::new(); let mut stdin_handle = stdin.lock(); stdin_handle.read_line(&mut buf)?; Ok(buf.trim().to_string()) } 生成的流向read_prompt提供输入,并在流中为每个项目发出请求。我该怎么做呢?

完整代码可在joshleeb/tokio-prime找到。

1 个答案:

答案 0 :(得分:0)

我提出的解决方案(到目前为止)一直使用Future-Rs箱中的LoopFn。它不理想,因为仍然需要建立新的连接,但它至少是朝着正确方向迈出的一步。

<强> main.rs

use futures::{future, Future};
use std::{env, io};
use std::net::SocketAddr;
use tokio_core::reactor::{Core, Handle};
use tokio_prime::protocol::PrimeClientProto;
use tokio_prime::request::PrimeRequest;
use tokio_proto::TcpClient;
use tokio_service::Service;

mod cli;

fn handler<'a>(
    handle: &'a Handle, addr: &'a SocketAddr
) -> impl Future<Item = (), Error = ()> + 'a {
    cli::prompt(io::stdin(), io::stdout())
        .and_then(move |number| {
            TcpClient::new(PrimeClientProto)
                .connect(addr, handle)
                .and_then(move |client| Ok((client, number)))
        })
        .and_then(|(client, number)| {
            client
                .call(PrimeRequest { number: Ok(number) })
                .and_then(|response| {
                    println!("{:?}", response);
                    Ok(())
                })
        })
        .or_else(|err| {
            println!("! {}", err);
            Ok(())
        })
}

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

    let addr_string = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let remote_addr = addr_string.parse::<SocketAddr>().unwrap();

    println!("Connecting on {}", remote_addr);

    let client = future::loop_fn((), |_| {
        handler(&handle, &remote_addr)
            .map(|_| -> future::Loop<(), ()> { future::Loop::Continue(()) })
    });

    core.run(client).ok();
}

<强> cli.rs

use futures::prelude::*;
use std::io;
use std::io::{Stdin, Stdout};
use std::io::prelude::*;

#[async]
pub fn prompt(stdin: Stdin, stdout: Stdout) -> io::Result<u64> {
    let mut stdout_handle = stdout.lock();
    stdout_handle.write(b"> ")?;
    stdout_handle.flush()?;

    let mut buf = String::new();
    let mut stdin_handle = stdin.lock();
    stdin_handle.read_line(&mut buf)?;

    parse_input(buf.trim().to_string())
}

fn parse_input(s: String) -> io::Result<u64> {
    s.parse::<u64>()
        .map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid u64"))
}