MIO Lossing connections


#1

Hi Folks,

I have some issues with mio.

I try to write a very simple echo server over TCP. All I want to do is to receive data and send back exactly the same thing.

The code is very simple and works:

extern crate mio;

use mio::tcp::TcpListener;
use mio::Token;
use mio::Poll;
use mio::Ready;
use mio::PollOpt;
use mio::Events;

use std::io::{Read, Write};
use std::str;
use std::io::ErrorKind;

const SERVER: Token = Token(0);

fn main() {
    println!("Hello, world!");

    let addr = "0.0.0.0:1883".parse().unwrap();
    let server = TcpListener::bind(&addr).unwrap();

    let poll = Poll::new().unwrap();

    poll.register(&server, SERVER, Ready::readable(), PollOpt::level()).unwrap();

    let mut events = Events::with_capacity(1024);

    let mut buffer: [u8; 1024] = [0; 1024];

    loop {

        poll.poll(&mut events, None).unwrap();

        println!("Number of events: {}", events.len());

        for event in events.iter() {
            if event.readiness().is_readable() && event.token() == SERVER {
                match server.accept() {
                    Ok((mut tcp_stream, socketAddr)) => {
                        match tcp_stream.read(&mut buffer) {
                            Ok(n) => {
                                println!("Read {} bytes", n);
                                println!("Value: {}", str::from_utf8(&buffer).unwrap());
                                tcp_stream.write(&buffer);
                                buffer = [0; 1024];
                            }
                            Err(e) => {
                                if e.kind() == std::io::ErrorKind::WouldBlock {
                                    println!("Would Block");
                                }
                                println!("Error: {:?}", e);
                            }
                        }
                    }
                    Err(e) => {
                        println!("Error: {:?}", e);
                    }
                }
            }
        }
    }
}

However sometime some packet get lost.

If I run the code and I start the following bash script:

$ for i in `seq 1 10`; 
    do 
        echo -e $i | sudo nc localhost 1883 -D; 
    done
1 # we miss the 2
3
4
5 # we miss the 6
7
8
9
10

And the log of the code are coherent:

Number of events: 1
Read 2 bytes
Value: 1

Number of events: 1 # this should be the 2
Would Block
Error: Error { repr: Os { code: 11, message: "Resource temporarily unavailable" } }
Number of events: 1
Read 2 bytes
Value: 3

Number of events: 1
Read 2 bytes
Value: 4

Number of events: 1
Read 2 bytes
Value: 5

Number of events: 1 # this should be the 6
Would Block
Error: Error { repr: Os { code: 11, message: "Resource temporarily unavailable" } }
Number of events: 1
Read 2 bytes
Value: 7

Number of events: 1
Read 2 bytes
Value: 8

Number of events: 1
Read 2 bytes
Value: 9

Number of events: 1
Read 3 bytes
Value: 10

I open an issue on mio itself (https://github.com/carllerche/mio/issues/634), Alex was extremely nice but still I haven’t quite understood his point.
Also, he seems quite busy with more valuable work that babysitting me, so I believe that asking the community would be better for everybody.

I can accept spurious wake up. However I do not understand how it is possible that the whole message get lost.
How can I be sure to read and process all the packets that arives to me?


#2

You have a couple of issues here. 1) your assuming that you will read all bytes in one go. 2) you’re write is also non-blocking, but you’re writing without regard to the blocking or non-blocking call. A general strategy is to wrap the functions in some form of state machine, and then changing the socket interest based on current state.

There’s a lot of complexity to get right with MIO. I would suggest checking out Tokio, which handles all of this state for you.


#3

Hi :slight_smile:

the whole goal of the project I am approaching is to learn more about network programming, so the lower the library I use the better. In my understanding MIO is a thin (???) wrapper around epoll. The lower the level the better :slight_smile:

  1. You are completely right, however, I read up to 1024 bytes while I send only 3; in this toy example it should not be an issue.
  2. Definitely, however, still, I do not understand why I am missing some packets. A possible issue would be that I wait to write and more messages will pipe up into the socket, but the only drawback should be that the next poll receives multiple events.

Neither of these issues explain the behaviour I am seeing.

However I may understand what I am doing wrong: https://github.com/carllerche/mio/issues/634#issuecomment-316405388

I believe that the real issue is that I am not polling the TCPStream but only the TCPListener.

Is that possible?


#4

Yeah, I think that’s the issue. What might be happening is the socket connects, you get the event notification, but no data is present and you get a WouldBlock error on read. Once the data is there, nothing polls for it since you’re only checking for connections (due to polling the listener).

Have you seen https://github.com/carllerche/mio-examples/blob/master/ping_pong/src/main.rs?