Resumindo, preciso ler uma string de uma conexão TCP enviada por um cliente C#. O cliente usa o BinaryWriter
, que prefixa a string real com um comprimento no formato leb128. Estou usando tokio::net::TcpStream
em Rust e procurei por uma caixa para me ajudar a recuperar esse prefixo de comprimento do fluxo, mas não consegui encontrar nada adequado. A maioria das soluções requer que a fonte da qual você está lendo implemente o io::Read
trait, mas tokio::net::TcpStream
não o implementa.
Consegui fazê-lo funcionar com esse código feio, mas desconfiei dele desde o começo. Recentemente, descobri que às vezes ele leva a algum tipo de condição de corrida. Não tenho certeza, mas acho que ele é bloqueado no let file_name_len = leb128::read::unsigned(&mut socket)?;
, o que de alguma forma faz com que meu ouvinte TCP pare de aceitar novas conexões, o que é ainda mais estranho.
let mut socket = socket.into_std()?;
socket.set_nonblocking(false)?;
let file_name_len = leb128::read::unsigned(&mut socket)?;
let mut socket = tokio::net::TcpStream::from_std(socket)?;
Alguém sabe o caminho certo para fazer isso?
O código acima está bloqueando :
set_nonblocking(false)
).leb128::read::unsigned(&mut socket)?;
.Isso bloqueará todo o tópico tokio.
Ele não deve bloquear o ouvinte TCP se o ouvinte TCP for executado em uma tarefa separada e você estiver usando o tempo de execução multithread (padrão) do Tokio... a menos que, é claro, você tenha várias tarefas LEB bloqueando cada thread do Tokio.
Infelizmente, não há uma API padrão para leituras assíncronas, e o
leb128
pacote não fornece nenhuma integração com o Tokio, então vai dar um pouco de trabalho.Não muito, porém, porque
&[u8]
implementaRead
, e depoisRead
o slice terá sido atualizado para apontar para os bytes não lidos.Como você está usando TCP, presumo que já tenha algum tipo de buffer para os bytes recebidos — para passá-los ao decodificador — então você deve usar apenas esse buffer .
Não acho a estrutura de código acima muito... legal, no entanto. Misturar E/S assíncrona e decodificação significa que você não pode testar a decodificação sozinha, doloroso, eu realmente aconselho preferir o design Sans IO quando possível.
Em vez disso, eu o encorajaria a escrever um
Framer
ouDecoder
que cuidará de parte (ou toda) da lógica de decodificação e apenas separará claramente a E/S do enquadramento/decodificação.A ideia é relativamente simples: inserir bytes nele e obter bytes enquadrados ou mensagens decodificadas.
Como não tenho seu decodificador, usarei um framer, cuja função é isolar um único quadro (mensagem codificada) no fluxo.
Depois que você tiver um emoldurador, na verdade é relativamente simples:
E o mais importante: é muito fácil testar se o criador da mensagem consegue lidar com todos os tipos de mensagens.
O código do framer real é relativamente simples: