Чтение пакетов данных в структуры в Rust - PullRequest
1 голос
/ 18 апреля 2020

1. Краткое изложение проблемы.

  • Я пытаюсь прочитать данные пакетов UDP из сокета в Struct in Rust.
  • Большая часть моего поиска в Google указала мне направление внешнего ящики, чтобы сделать анализ. Это кажется чрезмерным, и я думаю, что должен быть способ использовать стандартные возможности языка, чтобы «легко» читать двоичные данные в структуру, не используя ничего небезопасного. Я также хотел бы использовать это позже для встроенных платформ, так что размер кода на самом деле имеет для меня значение. (В приведенном ниже минимальном воспроизводимом примере будет использоваться библиотека std, поэтому все мы можем начать с высокой точки.)

2. Я попробовал следующее ...

Прежде всего, это скрипт php, который я использую для отправки пакетов UDP в мою программу для тестирования.

<?php

$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$pack = pack('C*', 4, 3, 1, 0);

socket_sendto($sock, $pack, strlen($pack), 0, '127.0.0.1', 1337);
socket_close($sock);

Супер просто, упакуйте некоторый двоичный файл и отправьте его по проводам в мою программу. Все является известным уникальным значением, поэтому мы можем гарантировать, что информация читается в правильном месте в структуре.

#![allow(non_snake_case)]

use std::net::UdpSocket;
use std::mem::size_of;

#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct ISP_TINY {
    Size: u8, // Always 4
    Type: u8, // Always ISP_TINY = 3
    ReqI: u8, // 0 Unless return of a request for information.
    SubT: u8, // Sub Type from the TINY_ enumeration.
}

fn main() -> std::io::Result<()>
{
    // Some output so that we know the program has actally started.
    println!("Running ...");

    // Here we bind to the UDP socket, but the question mark allows us to return early if there is an error and end the program then and there.
    let socket = UdpSocket::bind("0.0.0.0:1337")?;

    // We'll read into this buffer space, but it has to be as big as or bigger than the largest packet your program can receive, otherwise it will cut off the rest of the data.
    let mut buf = [0; 256];
    loop
    {
        // recv_from blocks, and will wait (in sleep) until we get a packet to our bound socket.
        let (bytes, socketAddress) = socket.recv_from(&mut buf)?;

        // Once we get a packet, read from our buffer upto the packet length in bytes.
        let packet = &mut buf[..bytes];

        // Check that it's of the size we understand and want.
        if bytes != size_of::<ISP_TINY>()
        {
            println!("Got packet of size {} need packet of size {}", bytes, size_of::<ISP_TINY>());
            // Here we implicitly discard a packet we don't like and contiune the loop.
            continue;
        }

        // When we get a packet we want, we print it's contents, it's size and where it came from.
        println!("Packet Recv {:#?} of size {} from {}", packet, bytes, socketAddress);
    }
}

3. Покажите некоторый код.

Я использовал огромное количество реализаций, чтобы сделать ржавчину счастливой. Я думаю, что за последние 24 часа я сделал около 10 различных дизайнов. Кажется, никто из них не порадовал предложенными мною решениями.

Не похоже, что это будет трудной проблемой. У меня есть данные, я могу просто нарезать их? Не так ли?

        let tiny = ISP_TINY {
            Size: packet[0 .. 1],
            Type: packet[1 .. 2],
            ReqI: packet[2 .. 3],
            SubT: packet[3 .. 4]
        };

Неправильно

error[E0308]: mismatched types
  --> src/main.rs:42:19
   |
42 |             Size: packet[0 .. 1],
   |                   ^^^^^^^^^^^^^^ expected `u8`, found slice `[u8]`

error[E0308]: mismatched types
  --> src/main.rs:43:19
   |
43 |             Type: packet[1 .. 2],
   |                   ^^^^^^^^^^^^^^ expected `u8`, found slice `[u8]`

error[E0308]: mismatched types
  --> src/main.rs:44:19
   |
44 |             ReqI: packet[2 .. 3],
   |                   ^^^^^^^^^^^^^^ expected `u8`, found slice `[u8]`

error[E0308]: mismatched types
  --> src/main.rs:45:19
   |
45 |             SubT: packet[3 .. 4]
   |                   ^^^^^^^^^^^^^^ expected `u8`, found slice `[u8]`

error: aborting due to 4 previous errors

Итак, я сказал себе, может быть, мне нужно создать реализацию ...

impl ISP_TINY
{
    fn from(&self, packet: &[u8])
    {
        self.Size: packet[0 .. 1],
        self.Type: packet[1 .. 2],
        self.ReqI: packet[2 .. 3],
        self.SubT: packet[3 .. 4]
    }
}

Это это тоже не нравится.

error: expected one of `!`, `(`, `::`, `;`, `<`, or `}`, found `[`
  --> src/main.rs:19:26
   |
19 |         self.Size: packet[0 .. 1],
   |                  -       ^ expected one of `!`, `(`, `::`, `;`, `<`, or `}`
   |                  |
   |                  tried to parse a type due to this

error: aborting due to previous error

Это те два из десяти, которые я считал разумными решениями. Но они не работают, и я не уверен, как обойти эту проблему на языке Rust. Как вы читаете необработанные двоичные данные из пакетов в структуры!?

1 Ответ

3 голосов
/ 18 апреля 2020

Просто индексируйте по целому числу, чтобы получить один байт, например packet[0] вместо packet[0..1]. Это даст вам u8 вместо среза длиной 1.

Для преобразования массива байтов в большее целое число, например, u16, i64, et c., Можно используйте такие функции, как from_le_bytes , from_be_bytes и from_ne_bytes, определенные для собственных целых чисел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...