Reading Lines with TcpStream

I am attempting to create a multithreaded server in Rust. At present, all I do is ask for your name, then wait for you to type "quit," at which time your connection should be closed. In addition, the server will print disconnections to stdout if the client does not type "quit," but simply drops the connection. To test it I am using telnet, but this is causing problems.
Naturally, I am attempting to process the input once the user presses enter in their telnet client of choice. TO this end, I assumed it would be possible to wrap a TcpStream in a BufReader and then call read_line on it. But this does not work correctly: the program panics because, apparently, the stream does not contain UTF-8. Obviously, read_to_string or read_to_end are unappealing alternatives, and I have no idea how to proceed from here. Any help would be appreciated.

Do you have any example code? It might be easier to suggest feedback if you're able to post a link to the playground, for example.

Certainly. This is the code, as it stands, for reading in a name:

let mut name = String::new();
let mut reader = BufReader::new(stream.try_clone().unwrap());
stream.write(b"Welcome. Please input your name. When you are ready to disconnect, type quit.").unwrap();
stream.flush().unwrap();
if reader.read_line(&mut name).unwrap() > 0 {
    println!("Acquired name: {}. Waiting for disconnect string.", name);
}

You can and should format your code with triple backticks, like this:

```rust
let mut name = String::new();
```

let mut name = String::new();
let mut reader = BufReader::new(stream.try_clone().unwrap());
stream
    .write(b"Welcome. Please input your name. When you are ready to disconnect, type quit.")
    .unwrap()
;
stream.flush().unwrap();
let count =
    reader
        .read_line(&mut name)
        .unwrap()
;
if count > 0 {
    println!("Acquired name: {}. Waiting for disconnect string.", name);
}

By the way, you should not use .write() but .write_all() since .write() may write truncated content (.write() is intended to be called several times, which .write_all() does for you).

But for even greater ergonomics (e.g., string formatting), you can use the write! macro:

write!(&mut stream,
    "Welcome. Please input your name. When you are ready to disconnect, type quit.",
).unwrap();

If you are unsure about the given buffer being valid UTF-8, you can just work with plain bytes:

let mut name: Vec<u8> = vec![];
let mut reader = BufReader::new(stream.try_clone().unwrap());
stream
    .write_all(b"Welcome. Please input your name. When you are ready to disconnect, type quit.")
    .unwrap()
;
stream.flush().unwrap();
let count =
    reader
        .read_until(b'\n', &mut name)
        .unwrap()
;
let name = String::from_utf8_lossy(&name);
if count > 0 {
    println!("Acquired name: {}. Waiting for disconnect string.", name);
}
3 Likes

Thanks for this. It worked, but I now have a very different issue.
For reasons too lengthy to explain here, I am going to be writing the client in a language which only handles UDP connections. This leads to a few questions:

  1. How do I check if a UdpSocket had actually connected to a client?
  2. I notice two sets of methods, and they appear to be closely related but not identical. On the one hand, there are bind, send_to, and rec_from; on the other, there are connect, send, and recv. What is the difference between the first and second set?
  3. Is it safe to assme that, if a recv method returns 0 bytes, the remote end of the socket has disconnected?
    Thanks for the assistance.

There are no connections in the land of udp. The connect method is just a convenience that allows leaving out the address on every send, and the only difference is when you set the address. As for receiving, one receives from anyone while the other one receives from only one sender.

Regarding disconnection, since there are no connections, all you can do is send your own disconnect packet or add a timeout.

Note that messages may not be received, and the same message may be received several times. You will not be notified of this, and will have to implement your own handling of this.

2 Likes