TcpStream does not Respond

TL;DR

Why does the code snippet in the next section not respond to the client?

Introduction

I'm trying out Rust, creating and removing some toy projects. I've just wanted to create a TCP server. The overall logic I am trying to achieve on the server is:

  1. Read the received bytes as string.
  2. If String is ps.cpu:
    1. Spawn thread (A).
    2. Respond to client with "Requested ps.cpu." in (A).
    3. Close the connection in (A).
  3. Else (especially for a case if String is empty, I mean):
    1. Spawn thread (B).
    2. Respond to client with "Hello world." in (A).
    3. Close the connection in (B).

I have also used log and simple_logger in order to get some useful info about what's going on inside.

Implementation

So I've come up with something like this below:

#[macro_use]
extern crate log;

#[macro_use]
extern crate serde;

mod ps;

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

const ADDRESS: &str = "0.0.0.0:10000";

fn main() -> io::Result<()> {
    simple_logger::init_with_level(log::Level::Trace).unwrap();

    info!("Binding the address: {}", ADDRESS);
    let listener = net::TcpListener::bind(ADDRESS)?;

    info!("Waiting for connections on {}", ADDRESS);

    for stream_result in listener.incoming() {
        let mut stream: net::TcpStream = stream_result?;
        let addr = stream.peer_addr()?;

        info!("Spawned a connection from: {}.", addr);

        let mut message = String::with_capacity(256 as usize);
        match stream.read_to_string(&mut message) {
            Err(err) => error!("An error occured while reading stream into string: {}", err),
            _ => {}
        }

        match message.as_str() {
            "ps.cpu" => {
                thread::spawn(move || {
                    debug!("Sending ps.cpu data to {}...", addr);
                    match stream.write("Requested ps.cpu.".as_bytes()) {
                        Err(err) => error!("An error occured while writing ps.cpu stream: {}", err),
                        _ => {}
                    };
                    match stream.flush() {
                        Err(err) => error!("An error occured while flushing ps.cpu stream: {}", err),
                        _ => {}
                    };
                });
            },
            _ => {
                thread::spawn(move || {
                    debug!("Sending data to {}...", addr);
                    match stream.write("Hello world.".as_bytes()) {
                        Err(err) => error!("An error occured while writing root stream: {}", err),
                        _ => {}
                    };
                    match stream.flush() {
                        Err(err) => error!("An error occured while flushing ps.cpu stream: {}", err),
                        _ => {}
                    };
                });
            }
        }
    }

    Ok(())
}

Testing

I have tested this server using Packet Sender. See the requests:


Newest packet is at the very top. Please mind the from and to.

Edit: A strange thing I have noticed is that it responds twice. I don't know why that is.

And my server is logging out as below:

2019-10-02 22:49:52,664 INFO  [yoghurt] Binding the address: 0.0.0.0:10000
2019-10-02 22:49:52,664 INFO  [yoghurt] Waiting for connections on 0.0.0.0:10000
2019-10-02 22:50:03,884 INFO  [yoghurt] Spawned a connection from: 127.0.0.1:58685.
2019-10-02 22:50:04,386 DEBUG [yoghurt] Sending ps.cpu data to 127.0.0.1:58685...
2019-10-02 22:50:09,666 INFO  [yoghurt] Spawned a connection from: 127.0.0.1:58613.
2019-10-02 22:50:10,167 DEBUG [yoghurt] Sending data to 127.0.0.1:58613...

(ye, btw, i named my thing yoghurt, don't ask me why)

Troubleshooting

The problem is it does not respond what I think it will respond. I have covered these situations with match in the code above.

  • If the received message is ps.cpu, respond with Requested ps.cpu. to the client.
  • Else respond with Hello world. to the client.

While it does not respond, the logs clearly show that the program does successfully tell ps.cpu from another. Then why does it not respond to the client?

Maybe the problem is with the statement below:

match stream.write("Requested ps.cpu.".as_bytes()) {
    Err(err) => error!("An error occured while writing ps.cpu stream: {}", err),
    _ => {}
};

Is my way of thinking wrong here? I mean, if the code snippet below is correct (in case I am only interested in the error):

let result = a_function_that_returns_result();

match result {
    Err(err) => {},
    _ => {}
}

Then this should be the same:

match a_function_that_returns_result() {
    Err(err) => {},
    _ => {}
}

Am I mistaken?


Thanks in advance. And also, since I'm new, please do not hesitate to mock my code if you see another problem and show me the right/convenient way to do things in Rust.


Environment

  • Toolchain: stable-x86_64-unknown-linux-gnu

I tried running your code locally (minus the external library usage). I think your problem is that when you call stream.read_to_string(&mut message) the required EOF is not detected in the request data so this call doesn't return.

I'd guess that you're still seeing the subsequent server log messages since, when the client ends the connection (probably due to timeout), the call to stream.read_to_string(&mut message) eventually returns. I'm not familiar with PacketSender and used netcat to test locally, so I might be a little off here.

I would suggest following the example in the book here: https://doc.rust-lang.org/book/ch20-01-single-threaded.html#reading-the-request and read your request data into a [u8] buffer. You can then convert this to a string using String::from_utf8_lossy(...) or similar after reading the data.

You might also need to use a buffer with a slightly bigger capacity than the 256 you're setting the String capacity to at present. I had a similar issue that somebody helped me with here: Data Loss on TcpStream, in that instance I wasn't reading the whole request and so my responses were garbled.

Hopefully that helps a little :slight_smile:

2 Likes