9日目です。昨日作ったTCPサーバに対するクライアントを作ります。
ソースコード
use std::net::TcpStream; use std::str; use std::time::Duration; use std::io::{self, BufRead, BufReader, Write}; fn main() { let remote = "127.0.0.1:33333".parse().unwrap(); let mut stream = TcpStream::connect_timeout(&remote, Duration::from_secs(1)).expect("Could not connect."); stream.set_read_timeout(Some(Duration::from_secs(2))).unwrap(); loop { let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); stream.write(input.as_bytes()).expect("failed to write"); let mut reader = BufReader::new(&stream); let mut buffer = Vec::new(); reader.read_until(b'\n', &mut buffer).expect("failed to read from socket"); print!("{}", str::from_utf8(&buffer).expect("failed to convert to String")); } }
解説
コネクションのセットアップ
let remote = "127.0.0.1:33333".parse().unwrap(); let mut stream = TcpStream::connect_timeout(&remote, Duration::from_secs(1)).expect("Could not connect."); stream.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
サーバーへの接続と読み込みに対してタイムアウトを設定しています。タイムアウトに指定した時間を経過するとパニックを起こして終了します。
また3行目にあるSome()
はOption型の値であり、以下のように定義されています。Option
enum Option<T> { None, Some(T) }
ループ
loop { let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); stream.write(input.as_bytes()).expect("failed to write"); let mut reader = BufReader::new(&stream); let mut buffer = Vec::new(); reader.read_until(b'\n', &mut buffer).expect("failed to read from socket"); print!("{}", str::from_utf8(&buffer).expect("failed to convert to String")); }
大したことはしていません。stdinから文字列を受け取り、バイト列に変換してからソケットに書き込んでいます。逆にサーバーからの応答を受け取るときは、バイト列から文字列に変換してstdoutに書き込んでいます。
rustはバイト列をchar型ではなくu8型の配列やベクタで表現します。つまりbufferは型を明示すると以下のようになります。
let mut buffer: Vec<u8> = Vec::new();
実行
昨日のTCPサーバを立てた後にTCPクライアントを実行します。
$ rustc src/tcp_client.rs && ./tcp_client qqq //入力 qqq // 応答
参考
Network Programming with Rust (Abhishek Chanda, 2018)