How to close TCPConnection with a function?

Hello. I'm new to Rust.
I'm making a TCP server with Rust.

I coukd

mod errors;
mod result;

#[cfg(test)]
mod tests;

use std::io::{Error, Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;

pub use self::errors::ServerSessionError;
pub use self::result::ServerSessionResult;

pub struct Server {
        addr: Option<String>,
        listener: TcpListener,
    }

impl Server {
    pub fn new() -> Result<(Server, Vec<ServerSessionResult>), ServerSessionError> {
        let server = Server {
            addr: Some("somewhere".to_string()),
            listener: TcpListener::bind("127.0.0.1:1935").unwrap(),
        };

        let mut results = Vec::with_capacity(4);

        Ok((server, results))
    }
    
    pub fn listen_and_serve(&self) {
        // Build a server
        println!("Listening...");
        for streams in self.listener.incoming() {
            match streams {
                Err(e) => { eprintln!("error: {}", e) },
                Ok(stream) => {
                    thread::spawn(move || {
                        handler(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
                    });
                }
            }
        }
    }

    pub fn terminate(&self) {
        // Terminate a server
        drop(self.listener);
    }
}

fn handler(mut stream: TcpStream) -> Result<(), Error> {
    println!("Connection from {}", stream.peer_addr()?);
    let mut buffer = [0; 1024];
    loop {
        let nbytes = stream.read(&mut buffer)?;
        if nbytes == 0 {
            return Ok(());
        }

        stream.write(&buffer[..nbytes])?;
        stream.flush()?;
    }
}

Running it, an error occurs

   Compiling inferno-rtmp-engine v0.1.0 (/Users/kunosouichirou/Documents/GitHub/Inferno/inferno/inferno-rtmp-engine)
warning: trait objects without an explicit `dyn` are deprecated
  --> inferno-rtmp-engine/src/servers/errors.rs:24:32
   |
24 |     fn cause(&self) -> Option<&Fail> {
   |                                ^^^^ help: use `dyn`: `dyn Fail`
   |
   = note: `#[warn(bare_trait_objects)]` on by default

warning: variable does not need to be mutable
  --> inferno-rtmp-engine/src/servers/mod.rs:26:13
   |
26 |         let mut results = Vec::with_capacity(4);
   |             ----^^^^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0507]: cannot move out of `self.listener` which is behind a shared reference
  --> inferno-rtmp-engine/src/servers/mod.rs:48:14
   |
48 |         drop(self.listener);
   |              ^^^^^^^^^^^^^ move occurs because `self.listener` has type `std::net::TcpListener`, which does not implement the `Copy` trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0507`.
error: could not compile `inferno-rtmp-engine`.

To learn more, run the command again with --verbose.

Without my terminate() function, it works properly.
But I want to stop the TCP server with a function for unit testing.

How can I close the server?

It will close on drop (at the end of the scope), so you don't need to call anything. In this case, that will likely mean at the end of the test at latest.

1 Like

Thank you for answering.

use super::*;

#[test]
fn bridge_tcp_server() {
    let (server, _result) = Server::new().unwrap();
    server.listen_and_serve();
}

This is my test code which works.
But it does not stop on

    Finished test [unoptimized + debuginfo] target(s) in 1.36s
     Running target/debug/deps/amf0-652cd166ce1912be

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/inferno_rtmp_engine-d3654c5273df6f45

running 1 test

Then I stop the test with Ctrl-c.
How do you stop the server?

Thank you.

Looping over incoming will never end.

One way to get around this is by putting listen_and_serve on a different thread so that it doesn't block the tests. I don't think that there is a way to foribly close a listener.

You can also make the listener non-blocking and figure out a way to efficiently sleep between accepts. Using async await is one way to do this. And a library like tokio or async_std makes this approach more maintainable.

1 Like

Thank you!
The test worked properly!

    Finished test [unoptimized + debuginfo] target(s) in 1.44s
     Running target/debug/deps/amf0-652cd166ce1912be

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/inferno_rtmp_engine-d3654c5273df6f45

running 1 test
Listening...
test servers::tests::bridge_tcp_server ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests amf0

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.