Вечный поток Tokio TCP (клиент) - PullRequest
0 голосов
/ 18 апреля 2020

Предисловие, вы можете перейти к следующему разделу

Поэтому я решил попробовать Rust для моего нового относительно небольшого проекта, потому что мне нравится, что он создает один исполняемый файл, который легко развернуть на моей цели на основе ARM с относительно небольшими ресурсами с точки зрения оперативной памяти и дискового пространства. У меня нет предыдущего опыта работы с Rust, но большой опыт работы с другими языками, и до сих пор я немного разочарован. Похоже, что для многих библиотек Rust и, возможно, для самого Rust API-интерфейсы меняются так быстро, что 90% образца кода, найденного в сети, не будут компилироваться с последними версиями библиотек, такими как tokio, tokio-util et c. Кроме того, документация часто вводит в заблуждение. Например, если вы Google для LinesCodec, он будет отображаться в tokio_io::codec::LinesCodec, tokio::codec::LinesCodec, tokio_codec::LinesCodec и tokio_util::codec::LinesCodec, который в конечном итоге, похоже, будет использоваться на сегодняшний день. То же самое происходит с другими вещами, такими как FramedRead, в некоторых из которых были функции-члены and_then и map, но в последней версии их, похоже, не существует. Наконец, количество вопросов и ответов, связанных с Rust на SO, намного меньше, чем для других языков, которые я использовал, что затрудняет начало использования Rust. То, что я пытаюсь сделать в течение последних двух дней, относительно легко решается на большинстве языков программирования, и я считаю, что и в Rust должно быть простое решение, но пока я не добился успеха.

Сам вопрос

Мне нужно подключить TCP-клиент к удаленному серверу и бесконечно читать и обрабатывать данные построчно, как указано в . Это нужно делать асинхронно, потому что тот же процесс также действует как HTTP-сервер, поэтому я использую tokio.

Насколько я понимаю, довольно распространенным способом является использование TcpStream, slit это к частям RX / TX, затем я пытаюсь подключить LinesCodecFramedRead), но я не могу соединить все это вместе без ошибок компиляции.

[dependencies]
futures = "*"
hyper = "*"
tokio = { version = "*", features = ["full"] }
tokio-util = "0.2.0"
tokio-modbus = { version = "*", features = ["tcp", "server", "tcp-server-unstable"], git = "https://github.com/slowtec/tokio-modbus" }
let stream = TcpStream::connect("172.16.100.10:1001").await.unwrap();
let transport = FramedRead::new(stream, LinesCodec::new()); // need to split?
/* ... what to do next to process incoming data line-by-line ...? */

Пока что я пришел с этим решением, хотя не уверен, насколько оно хорошо

tokio::spawn(async {
    let connection = TcpStream::connect("172.16.100.10:1001").await.unwrap();
    let mut reader = BufReader::new(connection);

    loop {
        let mut line = String::new();
        reader.read_line(&mut line).await.unwrap();
        println!("{}", line);
    }
});

1 Ответ

0 голосов
/ 20 апреля 2020

Пример использования LineCode c в программе chat * . Соответствующим разделом является функция «process».

Меньший пример (сервер, но принцип тот же для клиента), который переворачивает каждую полученную строку и выводит ее обратно, с максимальным размером буфера, равным 5000:

use tokio::net::TcpListener;
use tokio::stream::StreamExt;

use tokio_util::codec::{Framed, LinesCodec};

extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;

use futures::SinkExt;

async fn talk(sock: tokio::net::TcpStream) {
    let mut lines = Framed::new(sock, LinesCodec::LinesCodec::new_with_max_length(5000));

    while let Some(Ok(line)) = lines.next().await {
        let rev = line.graphemes(true).rev().collect::<String>();
        if let Err(_e) = lines.send(rev).await {
            break;
        }
    }
}

#[tokio::main]
async fn main() {
    let addr = "127.0.0.1:6200";
    let mut listener = TcpListener::bind(addr).await.unwrap();

    let mut incoming = listener.incoming();
    while let Some(conn) = incoming.next().await {
        match conn {
            Err(e) => eprintln!("accept failed = {:?}", e),
            Ok(sock) => {
                tokio::spawn(talk(sock));
            }
        }
    }
}

Проблема с обходным решением read_line заключается в том, что read_line не дает вам возможности ограничить длину строки, поэтому, если у вас ненадежный ввод, это может привести к тому, что ваша программа потреблять произвольное количество памяти. LineCode c дает возможность ограничить длину строки.

...