ソケットの待ち受け時に別の処理を実行する


私はソケットを用いてシンプルなサーバーとクライアントを作成しました。

[code]

    use std::env;
    #[macro_use]
    extern crate log;
    
    mod client;
    mod server;
    
    fn main() {
        env::set_var("RUST_LOG", "debug");
        env_logger::init();
        let args: Vec<String> = env::args().collect();
        if args.len() != 3 {
            error!("Please specify [server|client] [addr:port].");
            std::process::exit(1);
        }
        let role: &str = &args[1];
        let address = &args[2];
    
        match role {
            "server" => {
                server::serve(address).unwrap_or_else(|e| error!("{}", e));
            }
            "client" => {
                client::connect(address).unwrap_or_else(|e| error!("{}", e));
            }
            _ => {
                missing_role();
            }
        }
    }
    
    fn missing_role() {
        error!("Please specify server or client on the 1st argument.");
        std::process::exit(1);
    }
    
[/code]

[code]

    use std::io::{Read, Write};
    use std::net::{TcpListener, TcpStream};
    use std::{str, thread};
    
    pub fn serve(address: &str) -> Result<(), failure::Error> {
        let listener = TcpListener::bind(address)?;
        loop {
            let (stream, _) = listener.accept()?;
            thread::spawn(move || {
                handler(stream).unwrap_or_else(|error| error!("{:?}", error));
            });
        }
    }
    
    fn handler(mut stream: TcpStream) -> Result<(), failure::Error> {
        debug!("Handling data from {}", stream.peer_addr()?);
        let mut buffer = [0u8; 1024];
        loop {
            let nbytes = stream.read(&mut buffer)?;
            if nbytes == 0 {
                debug!("Connection closed.");
                return Ok(());
            }
            print!("{}", str::from_utf8(&buffer[..nbytes])?);
            stream.write_all(&buffer[..nbytes])?;
        }
    }
    
[/code]

[code]

    use std::io::{self, Write};
    use std::net::TcpStream;
    use std::str;
    
    pub fn connect(address: &str) -> Result<(), failure::Error> {
        let mut stream = TcpStream::connect(address)?;
    
        loop {
            let mut input = String::new();
            io::stdin().read_line(&mut input)?;
            stream.write_all(input.as_bytes())?;
        }
    }
    
[/code]

私は`main.rs`でserverのソケット待ち受け時に別の処理を実行したいと考えています。

しかし、以下のように書いても`server::serve(address).unwrap_or_else(|e| error!("{}",
e));`以降は実行されません。

[code]

    use std::env;
    #[macro_use]
    extern crate log;
    
    mod client;
    mod server;
    
    fn main() {
        env::set_var("RUST_LOG", "debug");
        env_logger::init();
        let args: Vec<String> = env::args().collect();
        if args.len() != 3 {
            error!("Please specify [server|client] [addr:port].");
            std::process::exit(1);
        }
        let role: &str = &args[1];
        let address = &args[2];
    
        match role {
            "server" => {
                server::serve(address).unwrap_or_else(|e| error!("{}", e));
                println!("hogehoge");
            }
            "client" => {
                client::connect(address).unwrap_or_else(|e| error!("{}", e));
            }
            _ => {
                missing_role();
            }
        }
    }
    
    fn missing_role() {
        error!("Please specify server or client on the 1st argument.");
        std::process::exit(1);
    }
    
[/code]

これは、ソケットの待ち受け状態に入っていて、関数が終了していないためだと考えています。

どうすれば、ソケットの待ち受け時に別の処理を実行できるでしょうか？




スレッドを使う方法のほかにノンブロックIOを使う方法もあります。

[code]

    pub fn serve(address: &str) -> Result<(), failure::Error> {
        let listener = TcpListener::bind(address)?;
        listener.set_nonblocking(true)?;
        loop {
            match listener.accept() {
                Ok((stream, _)) => {
                    println!("accept");
                    stream.set_nonblocking(false)?;
                    thread::spawn(move || {
                        handler(stream).unwrap_or_else(|error| error!("{:?}", error));
                    });
                }
                Err(e) => {
                    //error!("{:?}", e);
                    println!("hogehoge");
                }
            }
        }
    }
    
[/code]




`serve`関数と同じく、スレッドを使って並列処理を実現してみます。`"server"`の分岐を次のように書き換えてみてください。

[code]

            "server" => {
                let address = address.to_owned();
    
                let handle = thread::spawn(move || {
                    server::serve(&address).unwrap_or_else(|e| error!("{}", e));
                });
    
                println!("hogehoge");
    
                handle.join().unwrap();
            }
    
[/code]

メインスレッドが終了するとプログラム自体が終了してしまうので、分岐したスレッドの終了を待つために`join`しています。

