【问题标题】:Communicate Rust pnet packets between threads using channels使用通道在线程之间通信 Rust pnet 数据包
【发布时间】:2022-03-01 16:22:12
【问题描述】:

我正在开发一个读取和解析网络数据包的简单 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 的新手,非常感谢您的帮助!

【问题讨论】:

    标签: multithreading rust pcap


    【解决方案1】:

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

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

    send&amp;[u8]到一个线程。但是该线程可以比您发送给它的引用寿命更长,从而导致悬空引用。结果编译器抱怨他们需要有'staticlifetime."foo" 工作,因为它是&amp;'static str

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

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

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

    【讨论】:

      【解决方案2】:

      package_receiver.next() 调用定义为:

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

      mspc::Sender 将发送定义为:

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

      所以 package_receiver.next() 返回一个包含对字节切片的引用的结果,&[u8]。因此,当您随后调用 sender.send(packet); 时,这表示您要将引用发送到另一个线程。但是, package_receiver.next() 的匹配范围并不保证引用的寿命比范围的结尾长。因此,不能保证其他线程在访问该数据时引用仍然有效。

      str 有效,因为它是一个静态生命周期字符串。无论哪个线程读取它,该内存将始终有效。

      如果您将呼叫更改为:

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

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

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

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

      【讨论】:

        【解决方案3】:

        这里的其他答案很好地解释了为什么会发生这种情况,似乎EthernetPacket(派生数据包宏)只是字节引用的包装器。要从字节引用中创建一个“新”EthernetPacket 变量,可以执行以下操作:

        let packet = EthernetPacket::owned(packet.to_owned()).unwrap();
        sender.send(packet).unwrap();
        

        这将克隆数据包字节并通过通道发送它们。 EthernetPacket::owned 的文档可以在 here 找到。

        【讨论】:

          猜你喜欢
          • 2020-09-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多