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");
  }
}

こんな感じで。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
21
Help us understand the problem. What are the problem?