我正在研究一个简单的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还是很陌生,非常感谢您的帮助!
答案 0 :(得分:1)
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());
}
您还可以看看如何将作用域线程与crossbeam一起使用
答案 1 :(得分:0)
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");
}