如何使用Tokio从TcpStream读取单个数据包?

时间:2018-06-12 20:26:29

标签: rust rust-tokio

我正在尝试使用tokio接收单个数据包:

extern crate tokio;
extern crate tokio_io;

use tokio::net::{TcpListener};
use tokio::prelude::*;

use std::net::SocketAddr;
fn main() {
    let addr = "0.0.0.0:8080".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 |mut socket| {
            let mut bytes = vec![];
            bytes.reserve(1024);
            let processor = socket.read_buf(&mut bytes).into_future()
                .and_then(move |_size| {
                    println!("bytes: {:?}", bytes);
                    Ok(())
                })
                .map_err(|_| ());;
            tokio::spawn(processor)
        });
    tokio::run(done);
}

此代码打印一个空数据包。如何更改此代码以打印带有数据的接收数据包?

2 个答案:

答案 0 :(得分:1)

对于我自己,我几乎找到了答案。非常有帮助Similar question

struct AsWeGetIt<R>(R);

impl<R> Stream for AsWeGetIt<R>
    where
        R: AsyncRead,
{
    type Item = BytesMut;
    type Error = std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        let mut buf = BytesMut::with_capacity(1000);

        self.0
            .read_buf(&mut buf)
            .map(|async| async.map(|_| Some(buf)))
    }
}
....
let processor = AsWeGetIt(socket).into_future()
.and_then(|(bytes,_)|  {
    println!("bytes: {:?}", bytes);
    Ok(())
}).map_err(|_| ());

但为了更好地理解如何在没有单独结构的情况下做... 为什么以及使用什么地图?

答案 1 :(得分:0)

如果您的目标是真正接收一个数据包,我认为您已经成功了!

我已经对该程序进行了几次测试,并得到了回复。我正在测试:

nc 127.0.0.1 8080 <<< hello

运行几次之后,我得到以下输出:

Listening on: 0.0.0.0:8080
bytes: [104, 101, 108, 108, 111, 10]
bytes: []
bytes: [104, 101, 108, 108, 111, 10]
bytes: []
bytes: [104, 101, 108, 108, 111, 10]
bytes: []
bytes: [104, 101, 108, 108, 111, 10]
bytes: [104, 101, 108, 108, 111, 10]

如您所见,有时我们已准备好数据,有时我们却没有。我认为在你的测试中你只是运气不好而且只有在发送任何数据之前才得到TCP响应?

我大约90%确定TCP流可以包含空数据包,这就是我们所看到的。 (如果有人在这里有更多知识,请随时编辑答案或评论。)

要修复您的计划,您可能需要重新考虑目标。

读取一个TCP数据包似乎很有帮助。相反,通常,您希望读取一些字节数并在数据到来时处理数据。我对TCP的理解是作为字节流,而不是真正的数据包流。数据包只是从一个地方到另一个地方获取字节的一种方式,它们可以是任何长度而不会破坏兼容性。 “一包”是一个相当模糊的概念。

以下是使用tokio::io::read_exact函数读取流的前16个字节的示例:

extern crate tokio;

use tokio::net::TcpListener;
use tokio::prelude::*;

use std::net::SocketAddr;

fn main() {
    let addr = "0.0.0.0:8080".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 |mut socket| {
            // this function deals with bytes a bit differently and will just fill the
            // buffer exactly rather than adding onto the end.
            let mut bytes = vec![0; 16];
            let processor = tokio::io::read_exact(socket, bytes)
                .and_then(move |(socket, bytes)| {
                    println!("bytes: {:?}", bytes);
                    Ok(())
                })
                .map_err(|_| ());
            tokio::spawn(processor)
        });
    tokio::run(done);
}