Вот подпись UdpSocket::framed
из документации Токио:
pub fn framed<C: UdpCodec>(self, codec: C) -> UdpFramed<C>
Обратите внимание, что требуется self
, а не &self
; то есть вызов этой функции потребляет сокет. Оболочка UdpFramed
владеет базовым сокетом, когда вы вызываете это. Ваша ошибка компиляции говорит вам, что вы перемещаете socket
при вызове этого метода, но вы также пытаетесь заимствовать socket
внутри своего замыкания (для вызова send_to
).
Это, вероятно, не то, что вы хотите для реального кода. Весь смысл использования framed()
состоит в том, чтобы превратить ваш сокет в нечто более высокое, чтобы вы могли отправлять элементы вашего кодека напрямую, вместо того, чтобы собирать дейтаграммы. Использование send
или send_to
непосредственно на сокете, вероятно, нарушит создание протокола вашего сообщения. В этом коде, где вы пытаетесь реализовать простой эхо-сервер, вам вообще не нужно использовать framed
. Но если вы хотите получить свой торт и съесть его и использовать framed
и send_to
, к счастью UdpFramed
по-прежнему позволяет заимствовать базовый UdpSocket
, используя get_ref
. Вы можете решить вашу проблему следующим образом:
let framed = {
let socket = UdpSocket::bind(&addr, &handle).expect(&format!("Couldn't bind socket to address {}", addr));
socket.framed(MyCodec {})
}
let udp_future = framed.for_each(|(addr, data)| {
info!(self.logger, "Udp packet received from {}: length: {}", addr, data.len());
framed.get_ref().send_to(&data, &addr); // Just echo back the data
Ok(())
});
Я не проверял этот код, поскольку (как правильно заметил Шепмастер) у вашего фрагмента кода есть другие проблемы, но он все равно должен дать вам представление. Я повторю свое предупреждение от ранее: если вы сделаете это в реальном коде, это нарушит используемый вами сетевой протокол. В документации get_ref
это выглядит так:
Обратите внимание, что следует позаботиться о том, чтобы не вмешиваться в основной поток поступающих данных, так как это может повредить поток кадров, с которыми иначе будет работать.
Чтобы ответить на новую часть вашего вопроса: да, вам нужно самостоятельно выполнить сборку, а это значит, что вашему кодеку действительно нужно выполнить кадрирование байтов, которые вы отправляете. Как правило, это может включать последовательность запуска , которая не может происходить в Vec<u8>
. Начальная последовательность позволяет вам распознать начало следующего сообщения после потери пакета (что часто случается с UDP). Если в Vec<u8>
нет последовательности байтов, которая не может появиться, вам нужно избегать ее, когда это произойдет. Затем вы можете отправить длину сообщения, а затем сами данные; или просто данные, сопровождаемые конечной последовательностью и контрольной суммой, чтобы вы знали, что ни одна не была потеряна. У этих дизайнов есть свои плюсы и минусы, и это большая тема сама по себе.
Вам также нужно, чтобы ваш UdpCodec
содержал данные: карту от SocketAddr
до частично пересобранного сообщения, которое в данный момент выполняется. В decode
, если вам дано начало сообщения, скопируйте его на карту и верните Ok
. Если вам дана середина сообщения, и у вас уже есть начало сообщения на карте (для этого SocketAddr
), добавьте буфер в существующий буфер и верните Ok
. Когда вы доберетесь до конца сообщения, верните все это и очистите буфер. Методы UdpCodec
принимают &mut self
, чтобы включить этот вариант использования. ( NB Теоретически вам также следует иметь дело с пакетами, поступающими не по порядку, но на самом деле это довольно редко в реальном мире.)
encode
намного проще: вам просто нужно добавить ту же рамку и скопировать сообщение в буфер.
Позвольте мне повторить здесь, что вам не нужно и не следует использовать базовый сокет после вызова framed()
для него. UdpFramed
является как источником, так и приемником, поэтому этот объект также используется для отправки ответов. Вы даже можете использовать split()
, чтобы извлечь из него отдельные реализации Stream
и Sink
, если это облегчит владение вашим приложением.
В целом, теперь я видел, с какой проблемой вы боретесь, я бы рекомендовал использовать несколько сокетов TCP вместо UDP. Если вам нужен надежный протокол с установлением соединения, TCP уже существует и делает это за вас. Очень просто потратить много времени на создание «надежного» слоя поверх UDP, который одновременно медленнее и менее надежен, чем TCP.