Передача пакетов Rust pnet между потоками по каналам - PullRequest
0 голосов
/ 07 декабря 2018

Я работаю над простой программой Rust, которая читает и анализирует сетевые пакеты.Для чтения сетевых пакетов я использую pnet libary .Поскольку синтаксический анализ может занять некоторое время, я использую два отдельных потока для чтения и анализа пакетов.

Теперь моя идея состояла в том, чтобы передать пакеты чтения из первого потока во второй поток через передачу сообщений (используя mpsc::channel()).Вот упрощенная версия моего кода, который я написал на основе примера, приведенного в документе pnet :

extern crate pnet;

use std::sync::mpsc;
use std::thread;

use pnet::datalink;
use pnet::datalink::Channel::Ethernet;

fn main() {
    let (sender, receiver) = mpsc::channel();
    thread::spawn(move || {
        for packet in receiver.recv() {
            println!("{:?}", packet)
        }
    });

    let interface = datalink::interfaces().into_iter()
        .find(|interface| interface.name == "enp5s0")
        .unwrap();
    let (_, mut package_receiver) =
        match datalink::channel(&interface, Default::default()) {
            Ok(Ethernet(tx, rx)) => (tx, rx),
            _ => panic!()
        };

    loop {
        match package_receiver.next() {
            Ok(packet) => {
                // sender.send("foo"); // this works fine
                sender.send(packet);
            }
            _ => panic!()
        }
    }
}

Это прекрасно работает для отправки примитивных типов или строк по каналу, но недля сетевых пакетов.Когда я пытаюсь отправить пакет потоку парсера через канал, я получаю следующую ошибку компилятора:

error[E0597]: `*package_receiver` does not live long enough                                                                   
--> src/main.rs:28:15                                                                                                       
|                                                                                                                          
28 |         match package_receiver.next() {                                                                                  
|               ^^^^^^^^^^^^^^^^ borrowed value does not live long enough                                                  
...                                                                                                                           
36 | }                                                                                                                        
| - borrowed value only lives until here                                                                                   
|                                                                                                                          
= note: borrowed value must be valid for the static lifetime...

Я довольно новичок в Rust и буду очень признателен за помощь!

Ответы [ 2 ]

0 голосов
/ 08 декабря 2018

packet имеет тип &[u8], с некоторым временем жизни 'a, которое также совпадает с указанием ссылки на package_receiver в вызове next().Определение next() с временами жизни будет выглядеть так:

fn next(&'a mut self) -> Result<&'a [u8]>

Вы send &[u8] для потока.Но поток может пережить ссылки, которые вы отправляете на него, что приводит к висящим ссылкам.В результате компилятор жалуется, что им нужно иметь 'static время жизни. "foo" работает, потому что это &'static str.

Одним из способов было бы получить право собственности на данные и затем отправить их какзначение в другой поток.

Ok(packet) => {
   // sender.send("foo"); // this works fine
   sender.send(packet.to_owned());
}

Вы также можете взглянуть на использование потоков с областью действия поперечина

0 голосов
/ 08 декабря 2018

Вызов package_receiver.next () определяется как:

pub trait DataLinkReceiver: Send {
    fn next(&mut self) -> Result<&[u8]>;
}

И mspc :: Sender определяет send как:

pub fn send(&self, t: T) -> Result<(), SendError<T>>

Итак package_receiver.next () возвращает результат, содержащий ссылку на фрагмент байтов, & [u8] .Поэтому, когда вы вызываете sender.send (package); , это говорит о том, что вы хотите отправить ссылку на другой поток.Однако область соответствия для package_receiver.next () не обещает, что ссылка будет жить дольше, чем конец области.Таким образом, другой поток не может быть гарантирован, что ссылка все еще действительна во время доступа к этим данным.

str работает, потому что это статическая строка времени жизни.Эта память всегда будет действительной для чтения независимо от того, какой поток ее читает.

Если вы измените свой вызов на:

sender.send(Vec::from(packet))

Это создает переменную Vec, копирует фрагмент пакета в новыйпамяти, затем передает владение этой переменной другому потоку.Это гарантирует, что другой принимающий поток всегда будет иметь свободный доступ к этим данным.Поскольку владение явно передается, код в принимающем потоке известен компилятору как место, где заканчивается время жизни полученных переменных Vec.

Также будут некоторые разные ошибки при использовании результата .send () о котором можно позаботиться примерно так:

if sender.send(Vec::from(packet)).is_err() {
    println!("Send error");
}
...