Asynchronous 如何使用基于Tokio的echo服务器异步检索和修改数据?

Asynchronous 如何使用基于Tokio的echo服务器异步检索和修改数据?,asynchronous,rust,rust-tokio,Asynchronous,Rust,Rust Tokio,我正在一个echo服务器上工作,该服务器从TCP获取数据,并对该数据应用一些逻辑。例如,如果客户机数据以hello的形式输入,我希望从服务器以hello的形式响应它 我可以使用copy函数转发输入数据,但这在我的情况下并不有用 以下是我正在编写的起始代码: extern crate futures; extern crate tokio_core; extern crate tokio_io; use futures::stream::Stream; use futures::Future;

我正在一个echo服务器上工作,该服务器从TCP获取数据,并对该数据应用一些逻辑。例如,如果客户机数据以hello的形式输入,我希望从服务器以hello的形式响应它

我可以使用copy函数转发输入数据,但这在我的情况下并不有用

以下是我正在编写的起始代码:

extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

use futures::stream::Stream;
use futures::Future;
use std::net::SocketAddr;
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;
use tokio_io::io::copy;
use tokio_io::AsyncRead;

fn main() {
    let addr = "127.0.0.1:15000".parse::<SocketAddr>().unwrap();
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let socket = TcpListener::bind(&addr, &handle).unwrap();
    println!("Listening on: {}", addr);

    let done = socket.incoming().for_each(move |(socket, addr)| {
        let (reader, writer) = socket.split();
        let amt = copy(reader, writer);

        let msg = amt.then(move |result| {
            match result {
                Ok((amt, _, _)) => println!("wrote {} bytes to {}", amt, addr),
                Err(e) => println!("error on {}: {}", addr, e),
            }

            Ok(())
        });

        handle.spawn(msg);

        Ok(())
    });

    core.run(done).unwrap();
}

从某种意义上说,echo服务器是一种特殊的服务器,客户端的一个请求后面跟着服务器的一个响应。这样一个用例的一个很好的例子是tokio的

然而,需要考虑的一件事是,虽然UDP是基于数据包的,而数据包以您发送数据包的确切形式到达另一端,但TCP不是。TCP是一种流协议,它有强有力的保证,保证数据包由另一方接收,并且发送的数据完全按照发送顺序接收。但是,不能保证的是,一方发送的一个呼叫会导致另一方接收到一个呼叫,返回与发送的数据完全相同的数据块。当发送很长的数据块时,这尤其令人感兴趣,其中一个发送映射到多个接收。因此,在尝试向客户机发送响应之前,您应该接受服务器可以等待的分隔符。在Telnet中,该分隔符将是\r\n。 这就是东京的解码器/编码器基础设施发挥作用的地方。下面是这种编解码器的示例实现。如果你想 Telnet,这正是你想要的。它将一次只给您发送一条消息,并允许您一次只发送一条此类消息作为响应:

extern crate tokio;

use tokio::codec::Decoder;
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio::codec::LinesCodec;
use std::net::SocketAddr;

fn main() {
    let addr = "127.0.0.1:15000".parse::<SocketAddr>().unwrap();

    let socket = TcpListener::bind(&addr).unwrap();
    println!("Listening on: {}", addr);

    let done = socket.incoming()
        .map_err(|e| println!("failed to accept socket; error = {:?}", e))
        .for_each(move |socket| {
            // Fit the line-based codec on top of the socket. This will take on the task of
            // parsing incomming messages, as well as formatting outgoing ones (appending \r\n).
            let (lines_tx, lines_rx) = LinesCodec::new().framed(socket).split();

            // This takes every incomming message and allows to create one outgoing message for it,
            // essentially generating a stream of responses.
            let responses = lines_rx.map(|incomming_message| {
                // Implement whatever transform rules here
                if incomming_message == "hello" {
                    return String::from("hello from server");
                }
                return incomming_message;
            });

            // At this point `responses` is a stream of `Response` types which we
            // now want to write back out to the client. To do that we use
            // `Stream::fold` to perform a loop here, serializing each response and
            // then writing it out to the client.
            let writes = responses.fold(lines_tx, |writer, response| {
                //Return the future that handles to send the response to the socket
                writer.send(response)
            });

            // Run this request/response loop until the client closes the connection
            // Then return Ok(()), ignoring all eventual errors.
            tokio::spawn(
                writes.then(move |_| Ok(()))
            );

            return Ok(());
        });

    tokio::run(done);
}

我对Tokio不太熟悉,因此我没有写完整的答案,但我认为您需要将\u to \u端从套接字读取到缓冲区,将额外的数据附加到缓冲区,然后将\u全部从缓冲区写入套接字。Tokio文档中有使用读/写帮助程序的方法,您可以根据您的用例进行调整。我不清楚您的问题到底是什么,以及您尝试了什么。您显示的代码只是Tokio echo服务器示例的一个副本,没有任何修改,因此看起来您到目前为止根本没有尝试过任何东西。@SvenMarnach我使用这些代码来解释我正在处理的上下文。打开插座和通过tokio核心运行它们是一样的。我目前正在尝试从套接字获取数据,但我不确定在我更改数据后如何将其发送回客户端。听起来您需要一个答案来为您编写代码。我觉得这不符合堆栈溢出的精神。@AkinerAlkan只是为了进一步扩展它,关于堆栈溢出的问题应该是关于一个特定的问题,所以答案可以让很多人受益。出于这个原因,我们需要了解你到底有什么问题。一个简单的问题是,我复制了这个示例,并希望以此方式对其进行更改,但我真的不知道如何与此方法不匹配,因此您最好在Reddit或Rust用户论坛上提问。这些站点的范围比堆栈溢出更广。
extern crate tokio;

use tokio::codec::Decoder;
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio::codec::LinesCodec;
use std::net::SocketAddr;

fn main() {
    let addr = "127.0.0.1:15000".parse::<SocketAddr>().unwrap();

    let socket = TcpListener::bind(&addr).unwrap();
    println!("Listening on: {}", addr);

    let done = socket.incoming()
        .map_err(|e| println!("failed to accept socket; error = {:?}", e))
        .for_each(move |socket| {
            // Fit the line-based codec on top of the socket. This will take on the task of
            // parsing incomming messages, as well as formatting outgoing ones (appending \r\n).
            let (lines_tx, lines_rx) = LinesCodec::new().framed(socket).split();

            // This takes every incomming message and allows to create one outgoing message for it,
            // essentially generating a stream of responses.
            let responses = lines_rx.map(|incomming_message| {
                // Implement whatever transform rules here
                if incomming_message == "hello" {
                    return String::from("hello from server");
                }
                return incomming_message;
            });

            // At this point `responses` is a stream of `Response` types which we
            // now want to write back out to the client. To do that we use
            // `Stream::fold` to perform a loop here, serializing each response and
            // then writing it out to the client.
            let writes = responses.fold(lines_tx, |writer, response| {
                //Return the future that handles to send the response to the socket
                writer.send(response)
            });

            // Run this request/response loop until the client closes the connection
            // Then return Ok(()), ignoring all eventual errors.
            tokio::spawn(
                writes.then(move |_| Ok(()))
            );

            return Ok(());
        });

    tokio::run(done);
}