Rustで簡単なネットワークプログラムをつくる
少し前にRustに関する記事を書いたと思います。
どうしても気になってしまいまして、何かしたいと思いました。
そこで今回はRustで簡単なサーバプログラムとクライアントプログラムを作ってみたいと思います。
前にも書きましたがRustは新しい概念が多いと思います。
ですからただ覚えるのではなく、何かを作りながらではないと身に付けるのが難しいと思いました。
サーバーの方
まずサーバーのほうから始めていきたいと思います。
どうせc言語とかpythonとかと同じだろうと思ったら全然違ってました。
これは多分すぐ忘れる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
use std::net::{TcpListener,TcpStream}; use std::thread; use std::io::{Read,Write,Error}; fn sendRecv(mut stream: TcpStream) -> Result<(), Error> { println!("Connection from: {}",stream.peer_addr()?); let mut buf = [0; 512]; loop { let byte_read = stream.read(&mut buf)?; if byte_read == 0 {return Ok(());} stream.write(&buf[..byte_read])?; } } fn main() { let args: Vec<String> = std::env::args().collect(); let coninfo = args[1].to_string() + ":" + &args[2]; let listener = TcpListener::bind(coninfo).expect("COuld not bind"); for stream in listener.incoming() { match stream { Err(e) => {eprintln!("failed: {}",e)} Ok(stream) => { thread::spawn(move ||{ sendRecv(stream).unwrap_or_else(|error| eprintln!("{:?}",error)); }); } } } } |
まずコマンド引数のとり方からちょっと馴染みがないです。
bind自体は一行ですむのですが、IPアドレスとポート番号をコロンで繋いで文字列として関数に渡しております。
listener.incomingというのはc言語でいうacceptのことでしょうか。
接続が来たらそれをmatchに渡して正当性の確認をしています。
問題ないようであれば、スレッドを生成してその中で関数を呼び出しています。
実際のデータのやり取りはsendRecv関数がやってます。
データ格納用の変数を定義してます。
let mut buf = [0; 512];
これはc言語で言うところの
char buf[512];
なんでしょうか。
そしてstream.readで相手からデータを受信してます。
受信バイトを確認しつつそのまま受信したものを送信してます。
streamに関してところどころ ? が付いている箇所があるかと思います。
これがどうやらエラーハンドリングをしてくれているようです。
もしエラーがあれば呼び出し元に帰りunwrap_or_elseが実行されるのかと。
ソケットやデータの送受信部分については特に問題ないですね
クライアントの方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
use std::net::{TcpStream}; use std::str; use std::io::{self,BufRead,BufReader,Write}; fn main() { let args: Vec<String> = std::env::args().collect(); let coninfo = args[1].to_string() + ":" + &args[2]; let mut stream = TcpStream::connect(coninfo).expect("Could not connect to Server"); loop { let mut input = String::new(); let mut buffer: Vec<u8> = Vec::new(); io::stdin().read_line(&mut input).expect("Failed to read from stdin"); stream.write(input.as_bytes()).expect("Failed to write to server"); let mut reader = BufReader::new(&stream); reader.read_until(b'\n',&mut buffer).expect("Could not read intop buffer"); print!("{}",str::from_utf8(&buffer).expect("Could not write buffer as string")); } } |
クライアントの方もコマンド引数をとりそれをコロンでつないで関数に渡してます。
TcpStream::connect(coninfo)がそうです。
そしてあとはループの中で標準入力から文字列をもらいそれをバイト型に変換してストリームに渡してます。
ここがサーバーと違うところなのですが、なぜかstream.readができませんでした。
TcpStreamの場合、BufReaderをつかってデータを受信するようです。
BufReader::new(&stream);がそれの前処理的なもので、
reader.read_until(b’\n’,&mut buffer)が実際に読み込む処理なのでしょうか。
今回は単なるエコーサーバーなので送った文字列が返ってきます。
サーバー側もですがc言語よりかは圧倒的に記述量は少ないと思います。
これから
今回は簡単なサーバープログラムとクライアントプログラムを作りました。
これからどうしましょう。
今まで他の言語作ったものをすべてRustで書き直すみたいなことすれば身につきますかね。
Rustで何かを作ってる方は何かおすすめを教えていただければと思います。
他にもRustについて記事があります。