使用通道在线程之间传递Rust pnet数据包

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用通道在线程之间传递Rust pnet数据包相关的知识,希望对你有一定的参考价值。

我正在研究一个简单的Rust程序,它读取和解析网络数据包。为了读取网络数据包我正在使用pnet libary。因为解析可能需要一些时间,我使用两个单独的线程来读取和解析数据包。

我现在的想法是通过消息传递(使用mpsc::channel())将读取包从第一个线程传递到第二个线程。这是我基于the example given in the pnet doc编写的代码的简化版本:

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的新手,非常感谢他们的帮助!

答案

packet&[u8]类型,有一些生命的'a也与package_receiver调用中next()的参考相同。生命周期的next()定义如下所示:

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

send &[u8]线程。但是线程可以比你发送给它的引用更长,导致悬空引用。结果编译器抱怨他们需要'static lifetime."foo"工作,因为它是&'static str

一种方法是获取数据的所有权,然后将其作为值发送到另一个线程。

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

您还可以查看使用crossbeam的作用域线程

另一答案

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_receiver.next()的匹配范围不保证引用的寿命长于范围的结尾。因此,无法保证其他线程在访问该数据时仍然有效。

str工作,因为它是一个静态生命周期字符串。无论什么线程读取它,该内存总是有效读取。

如果您将通话更改为:

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

这将创建一个Vec变量,将数据包切片复制到新内存中,然后将该变量的所有权传递给另一个线程。这可以保证其他接收线程始终可以清楚地访问该数据。因为明确地传递了所有权,所以编译器知道接收线程中的代码,其中接收的Vec变量的生命周期结束。

关于使用.send()的结果也会有一些杂项错误,可以通过以下方式处理:

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

以上是关于使用通道在线程之间传递Rust pnet数据包的主要内容,如果未能解决你的问题,请参考以下文章

如何使用通道在 go 例程之间传递字节片

如何在线程中使用静态生命周期?

Rust 与 C 之间,传递字符串的 7 种方式!

使用通道时Rust中的内存分配

ThreadLocal父子线程之间的数据传递问题

Rust编程语言入门之无畏并发