The following code should accept tcp connection, read from them, and close them on hangup.
extern crate mio;
use mio::{EventLoop,Token,ReadHint};
use mio::tcp::{TcpListener, TcpStream};
use std::io::Read;
const L_CLIENT: Token = Token(0);
const C_CLIENT: Token = Token(1);
fn sa(port:u16) -> std::net::SocketAddr {
let ipa : std::net::Ipv4Addr = std::net::Ipv4Addr::new(127,0,0,1);
std::net::SocketAddr::V4(std::net::SocketAddrV4::new(ipa,port))
}
fn main(){
let mut event_loop = EventLoop::new().unwrap();
let lclient = TcpListener::bind(&sa(4000)).unwrap();
event_loop.register(&lclient, L_CLIENT).unwrap();
println!("running loop...");
event_loop.run(&mut MyHandler{lclient:lclient,cclient:None}).unwrap();
println!("done.");
}
struct MyHandler{
lclient:TcpListener,
cclient:Option<TcpStream>,
}
impl mio::Handler for MyHandler {
type Timeout = ();
type Message = ();
fn readable(&mut self, event_loop: &mut EventLoop<Self>, token: Token, hint: ReadHint){
match token {
L_CLIENT => {
let s=self.lclient.accept().unwrap().expect("no client??");
match self.cclient{
None => {event_loop.register(&s,C_CLIENT);self.cclient=Some(s);}
Some(_) =>{println!("ignore second client");} // s should be closed as it goes out of scope
}
},
C_CLIENT => {
let mut client=self.cclient.expect("connection is gone"); // what's the problem here?
if hint.is_hup() {
event_loop.deregister(&client);
self.cclient=None; // how to close connection?
} else {
let mut buf: [u8; 500] = [0; 500];
let l=client.read(&mut buf).unwrap();
println!("read {} bytes.",l);
}
},
_ =>{println!("other Token");}
}
}
}
This almost works. In the C_CLIENT case I want to handle a client hangup. In that case I guess I need to move ownership for the connection from my handler to the method in order to let it go out of scope to close it.
Unfortunately rust will not allow me to move the connection:
error: cannot move out of borrowed content
As far as I understand the problem is that &self
is borrowed in the readable
method and therefore is not able to access its fields.
How do I access the connection? (avoiding the above error.)
How do I close that connection? (i.e. transfer ownership.)
I assume the error in the original code is from this line:
This isn't allowed because
Option::expect
takes ownership of its argument, butreadable
only has a borrowed reference. Only an owner can move a value to a new owner.You could solve this by using
Option:::take
. Instead of moving the original option, this just borrows and mutates it, changing it toNone
and returning its original value:However, it's not necessary to move the value at all. You already have a mutable reference, so you can just mutate it in place:
When you write
self.cclient = None;
, the old value of self.cclient is dropped immediately, closing the connection. In Rust, a value is dropped when it is no longer owned. This can happen when its owner goes out of scope or when its owner is reassigned. The Rust compiler statically inserts drop calls when a variable goes out of scope or is reassigned.