wspipe
Linuxのプロセス間通信であるpipeをweb socketにつないで、インターネットをまたいで使えるようなコマンドをRustで書いてみました。
ソースコードはgithub に上げました。
https://github.com/tetsu-koba/wspipe-rs
使用方法
stdin2ws
と ws2stdout
という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の処理が完了していないためと思われるのですが、ちょっと解せません。バグを踏んでいるのか、やり方が間違っているのか。