Edited at

pipeをweb socketにつなぐコマンドをRustで書いてみた


wspipe

Linuxのプロセス間通信であるpipeをweb socketにつないで、インターネットをまたいで使えるようなコマンドをRustで書いてみました。

ソースコードはgithub に上げました。

https://github.com/tetsu-koba/wspipe-rs


使用方法

stdin2wsws2stdout という2つのコマンドがあります。

ws2stdout が受信側でありweb socketを開いてlistenします。

そこにstdin2wsで送信することができます。

例として、受信側(ipアドレスは192.168.10.120)では以下のようにしてポート8001にweb socketを作って待ち受けます。

./ws2stdout 0.0.0.0:8001 |tee kern.log

それに対して、以下のように/var/log/kern.log をwebsocket経由で流すことができます。

sudo tail -f /var/log/kern.log | ./stdin2ws ws://192.168.10.120:8001

これはある実験のために作ったのですが、それに関しては次の記事で。

(2018.10.10 書きました。Websocketで簡単に音声のストリーミングをしてみる)


つまずいたところ

websocketにはws-rs のクレートを使用しています。

https://crates.io/crates/ws

pub fn stdin2websocket(url: &str) {

let (sender, receiver) = channel::<u32>();
if let Err(error) = connect(url, |out| {
let s2 = sender.clone();
thread::spawn(move || {
send_binary(&out);
thread::sleep(time::Duration::from_millis(100));
out.close(CloseCode::Normal).unwrap();
s2.send(1).unwrap();
});

msg_handler
})
{
println!("Failed to create WebSocket due to: {:?}", error);
}
receiver.recv().unwrap();
}

ws::connect() の引数で渡すクロージャの中でスレッドを起こしてstdinからwebsocketに転送をするのですが、このスレッドの終了を待つ方法で苦労しました。

このクロージャが FnMut であり、複数回実行される可能性のあるものであるため、単純に所有権をmove させることができないのです。コンパイルエラーになります。

channelのsenderをcloneして渡せばよいということに気がつくまでかなりかかりました。

channelはreceiverはcloneできませんが、senderはclone可能です。

(パッケージ名のmpscはMulti-producer, single-consumer の意味)

また、転送終了後にcloseする前に100msのwaitを入れましたが、これが無いとcloseがエラーになります。まだwebsocketの処理が完了していないためと思われるのですが、ちょっと解せません。バグを踏んでいるのか、やり方が間違っているのか。