大学の課題でソケット通信でオセロ対戦をするプログラムを書いていたのですが、Rust におけるソケット通信の仕様を理解するのに色々と苦労したので、メモしておきます。
ホスト名とポート番号を指定して、相手とデータのやり取りをする場合を考えます。
// for example
let host = "localhost";
let port = 3000;
まず、IPアドレスに変換してくれる .to_socket_addrs()
というメソッドがありますが、これによって変換されるアドレスは IPv4 だけでなく IPv6 とかの候補まで出してしまいます。相手側の実装にもよりますが、多分求めているのは IPv4 なので、.is_ipv4()
というメソッドできちんと絞っておきましょう。
let host_and_port = format!("{}:{}", host, port);
let mut addrs = host_and_port.to_socket_addrs().unwrap();
if let Some(addr) = addrs.find(|x| (*x).is_ipv4()) {
// TODO
} else {
eprintln!("Invalid Host:Port Number");
}
アドレスが取れたら connect
します。成功すると stream
が得られます。
let host_and_port = format!("{}:{}", host, port);
let mut addrs = host_and_port.to_socket_addrs().unwrap();
if let Some(addr) = addrs.find(|x| (*x).is_ipv4()) {
match TcpStream::connect(addr) {
Err(_) => {
println!("Connection NG.");
}
Ok(stream) => {
println!("Connection Ok.");
// TODO
}
}
} else {
eprintln!("Invalid Host:Port Number");
}
stream
が取れたらバッファBufReader
, BufWriter
を作ってしまいましょう。直接 stream
から read
, write
すると遅いので。それに、バッファを作っておけば使い回せます。
引数のstream
は参照で取るのがミソ。
let host_and_port = format!("{}:{}", host, port);
let mut addrs = host_and_port.to_socket_addrs().unwrap();
if let Some(addr) = addrs.find(|x| (*x).is_ipv4()) {
match TcpStream::connect(addr) {
Err(_) => {
println!("Connection NG.");
}
Ok(stream) => {
println!("Connection Ok.");
let mut reader = BufReader::new(&stream);
let mut writer = BufWriter::new(&stream);
// TODO
}
}
} else {
eprintln!("Invalid Host:Port Number");
}
あとは、reader
や writer
を mutable な参照で引数に取る関数をよしなに作っていけば良いです。例えば、
fn read_something (reader: &mut BufReader<&TcpStream>) {
let mut msg = String::new();
reader.read_line(&mut msg).expect("RECEIVE FAILURE!!!");
// read_line は改行文字まで読む。
// 他のread系のメソッドもある (https://doc.rust-lang.org/std/io/trait.BufRead.html)
println!("{}", msg);
}
fn write_something (writer: &mut BufWriter<&TcpStream>, comment: &str) {
let msg = format!("MESSAGE: {}\n", comment);
// 送る側もたぶん改行文字を付けたほうがよいでしょう。
writer.write(msg.as_bytes()).expect("SEND FAILURE!!!");
writer.flush().unwrap();
}
fn main() {
let host_and_port = format!("{}:{}", host, port);
let mut addrs = host_and_port.to_socket_addrs().unwrap();
if let Some(addr) = addrs.find(|x| (*x).is_ipv4()) {
match TcpStream::connect(addr) {
Err(_) => {
println!("Connection NG.");
}
Ok(stream) => {
println!("Connection Ok.");
let mut reader = BufReader::new(&stream);
let mut writer = BufWriter::new(&stream);
read_something(&mut reader);
write_something(&mut writer, "hoge");
}
}
} else {
eprintln!("Invalid Host:Port Number");
}
}
こんな感じで。