21
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Rust で TCP/IP ソケット通信をする際のモデル

大学の課題でソケット通信でオセロ対戦をするプログラムを書いていたのですが、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");
}

あとは、readerwriter を 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");
  }
}

こんな感じで。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
21
Help us understand the problem. What are the problem?