TcpStream not returning from Read until connection closed - networking

I'm trying to implement a simple TCP messaging protocol in Rust with the standard TCP Sockets. The protocol is like so:
[C0 FF EE EE] header
[XX XX] type U16LE
[XX XX] size U16LE
... (size - 8) bytes of arbitrary data ...
I have come up with the following code:
const REQUEST_HEADER: [u8; 4] = [0xC0, 0xFF, 0xEE, 0xEE];
pub fn run_server(host: &str, port: &str) {
let listener = TcpListener::bind(format!("{}:{}", host, port))
.expect("Could not bind to the requested host/port combination.");
for stream in listener.incoming() {
thread::spawn(|| {
let stream = stream.unwrap();
handle_client(stream);
});
}
}
fn handle_client(stream: TcpStream) {
stream.set_nodelay(true).expect("set_nodelay call failed");
loop {
match get_packet(stream) {
Ok(deframed) => {
match deframed.msgid {
Some(FrameType::Hello) => say_hello(stream),
Some(FrameType::Text) => {
println!("-- Got text message: {:X?}", deframed.content);
save_text(deframed);
},
Some(FrameType::Goodbye) => {
println!("-- You say goodbye, and I say hello");
break; // end of connection
},
_ => println!("!! I don't know this packet type")
}
},
Err(x) => {
match x {
1 => println!("!! Malformed frame header"),
2 => println!("!! Client gone?"),
_ => println!("!! Unknown error")
}
break;
}
}
}
}
// Read one packet from the stream
fn get_packet(reader: TcpStream) -> Result<MyFrame, u8> {
// read the packet header in here
let mut heading = [0u8; 0x8];
match reader.read_exact(&mut heading) {
Ok(_) => {
// check if header is OK
if heading[..4] == REQUEST_HEADER
{
// extract metadata
let mid: u16 = ((heading[4] as u16) << 0)
+ ((heading[5] as u16) << 8);
let len: u16 = ((heading[6] as u16) << 0)
+ ((heading[7] as u16) << 8);
let remain: u16 = len - 0x8;
println!("-> Pkt: 0x{:X}, length: 0x{:X}, remain: 0x{:X}", mid, len, remain);
let mut frame = vec![0; remain as usize];
reader.read_exact(&mut frame).expect("Read packet failed");
let deframed = MyFrame {
msgid: FromPrimitive::from_u16(mid),
content: frame
};
Ok(deframed)
} else {
println!("!! Expected packet header but got {:X?}", heading.to_vec());
Err(1)
}
},
_ => Err(2)
}
}
This is the Python client:
import socket, sys
sock = socket.socket()
sock.connect(("127.0.0.1", 12345))
f = open("message.bin",'rb')
dat = f.read()
sock.sendall(dat)
while True:
sock.recv(64)
When the program receives a multiple messages in a row, they seem to be processed fine. If the client sends a message and waits for a response, the program gets stuck at reading the frame content until ~16 more bytes are received. As a result, the program cannot send a response to the client.
I can tell it's stuck in there and not somewhere else because the ->Pkt log line appears with the correct metadata but it doesn't continue further processing despite the client having already sent everything.
I've tried replacing read_exact with read but it just keeps being stuck there. Once the client software drops the connection the message is suddenly processed as normal.
Is this a design problem or am I missing out a setting I need to change on the socket?

Related

Read/write problem in async tokio application (beginner)

I'm new to network programming and thread in Rust so I may be missing something obvious here. I've been following along with this trying to build a simple chat application. Only, he does it with the standard library and I'm trying to do it with tokio. The functionality is very simple: Client sends a message to Server, Server acknowledges it and sends it back to the Client. Here's my code for the client and server, stripped down as much as I can:
server.rs
#[tokio::main]
async fn main() {
let server = TcpListener::bind("127.0.0.1:7878").await.unwrap();
let mut clients = vec![];
let (tx, mut rx) = mpsc::channel(32);
loop {
if let Ok((socket, addr)) = server.accept().await {
let tx = tx.clone();
let (mut reader, writer) = split(socket);
clients.push(writer);
tokio::spawn(async move {
loop {
let mut buffer = vec![0; 1024];
reader.read(&mut buffer).await.unwrap();
//get message written by the client and print it
//then transmit it on the channel
let msg = buffer.into_iter().take_while(|&x| x != 0).collect::<Vec<_>>();
let msg = String::from_utf8(msg).expect("Invalid utf8 message");
println!("{}: {:?}", addr, msg);
match tx.send(msg).await {
Ok(_) => { ()}
Err(_) => { println!("Error");}
}
}
});
}
//write each message received back to its client
if let Some(msg) = rx.recv().await {
clients = clients.into_iter().filter_map(|mut x| {
println!("writing: {:?}", &msg);
x.write(&msg.clone().into_bytes());
Some(x)
}).collect::<Vec<_>>();
}
}
}
client.rs
#[tokio::main]
async fn main() {
let client = TcpStream::connect("127.0.0.1:7878").await.unwrap();
let (tx, mut rx) = mpsc::channel::<String>(32);
tokio::spawn(async move {
loop {
let mut buffer = vec![0; 1024];
// get message sent by the server and print it
match client.try_read(&mut buffer) {
Ok(_) => {
let msg = buffer.into_iter().take_while(|&x| x != 0).collect::<Vec<_>>();
println!("Received from server: {:?}", msg);
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
()
}
Err(_) => {
println!("Connection with server was severed");
break;
}
}
// get message transmitted from user input loop
// then write it to the server
match rx.try_recv() {
Ok(message) => {
let mut buffer = message.clone().into_bytes();
buffer.resize(1024, 0);
match client.try_write(&buffer) {
Ok(_) => { println!("Write successful");}
Err(_) => { println!("Write error");}
}
}
Err(TryRecvError::Empty) => (),
_ => break
}
}
} );
// user input loop here
// takes user message and transmits it on the channel
}
Sending to the server works fine, and the server appears to be successfully writing as indicated by its output:
127.0.0.1:55346: "test message"
writing: "test message"
The issue is the client never reads back the message from the server, instead getting WouldBlock errors every time it hits the match client.try_read(&mut buffer) block.
If I stop the server while keeping the client running, the client is suddenly flooded with successful reads of empty messages:
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
...
Can anyone tell me what's going on?
Here's what happens in your server:
Wait for a client to connect.
When the client is connected, spawn a background task to receive from the client.
Try to read from the channel, since it is very unlikely that the client has already sent anything at this point the channel is empty.
Loop → wait for another client to connect.
While waiting for another client, the background task receives the message from the first client and sends it to the channel, but the main task is blocked waiting for another client and never tries to read again from the channel.
Easiest way to get it to work is to get rid of the channel in the server and simply echo the message from the spawned task.
Another solution is to spawn an independent task to process the channel and write to the clients.
As for what happens when you kill the server: once the connection is lost attempting to read from the socket does not return an error but instead returns an empty buffer.

How to efficiently send large files across a single network connection?

I'm sending large objects over a network and noticed that using a single network connection is significantly slower than using multiple.
Server code:
use async_std::{
io::{BufWriter, Write},
net::TcpListener,
prelude::*,
task,
};
use bench_utils::{end_timer, start_timer};
use futures::stream::{FuturesOrdered, StreamExt};
async fn send(buf: &[u8], writer: &mut (impl Write + Unpin)) {
// Send the message length
writer.write_all(&(buf.len() as u64).to_le_bytes()).await.unwrap();
// Send the rest of the message
writer.write_all(&buf).await.unwrap();
writer.flush().await.unwrap();
}
fn main() {
task::block_on(async move {
let listener = TcpListener::bind("0.0.0.0:8000").await.unwrap();
let mut incoming = listener.incoming();
let mut writers = Vec::with_capacity(16);
for _ in 0..16 {
let stream = incoming.next().await.unwrap().unwrap();
writers.push(BufWriter::new(stream))
};
let buf = vec![0u8; 1 << 30];
let send_time = start_timer!(|| "Sending buffer across 1 connection");
send(&buf, &mut writers[0]).await;
end_timer!(send_time);
let send_time = start_timer!(|| "Sending buffer across 16 connections");
writers
.iter_mut()
.zip(buf.chunks(buf.len() / 16))
.map(|(w, chunk)| {
send(chunk, w)
})
.collect::<FuturesOrdered<_>>()
.collect::<Vec<_>>()
.await;
end_timer!(send_time);
});
}
Client code:
use async_std::{
io::{BufReader, Read},
net::TcpStream,
prelude::*,
task,
};
use bench_utils::{end_timer, start_timer};
use futures::stream::{FuturesOrdered, StreamExt};
async fn recv(reader: &mut (impl Read + Unpin)) {
// Read the message length
let mut len_buf = [0u8; 8];
reader.read_exact(&mut len_buf).await.unwrap();
let len: u64 = u64::from_le_bytes(len_buf);
// Read the rest of the message
let mut buf = vec![0u8; usize::try_from(len).unwrap()];
reader.read_exact(&mut buf[..]).await.unwrap();
}
fn main() {
let host = &std::env::args().collect::<Vec<_>>()[1];
task::block_on(async move {
let mut readers = Vec::with_capacity(16);
for _ in 0..16 {
let stream = TcpStream::connect(host).await.unwrap();
readers.push(BufReader::new(stream));
}
let read_time = start_timer!(|| "Reading buffer from 1 connection");
recv(&mut readers[0]).await;
end_timer!(read_time);
let read_time = start_timer!(|| "Reading buffer from 16 connections");
readers
.iter_mut()
.map(|r| recv(r))
.collect::<FuturesOrdered<_>>()
.collect::<Vec<_>>()
.await;
end_timer!(read_time);
});
}
Server result:
Start: Sending buffer across 1 connection
End: Sending buffer across 1 connection....................................55.134s
Start: Sending buffer across 16 connections
End: Sending buffer across 16 connections..................................4.19s
Client result:
Start: Reading buffer from 1 connection
End: Reading buffer from 1 connection......................................55.396s
Start: Reading buffer from 16 connections
End: Reading buffer from 16 connections....................................3.914s
I am assuming that this difference is due to the sending connection having to wait for an ACK when the TCP buffer is filled (both machines have TCP window scaling enabled)? It doesn't appear that Rust provides an API to modify the size of these things.
Is there anyway to achieve similar throughput on a single connection? It seems annoying to have to pass around multiple since all of this is going through a single network interface anyways.

Rust TCP Proxy to pipe http, ssh and other tcp traffic

I am new to rust and as learning project I want to create a tcp proxy. Tokio overwhelmed me and I could not find the proper documentation to understand how it works. So I tried to go with std net modules but I stuck at properly piping the traffic. I bound multiple listeners in their own threads and want to pipe the traffic forth and back like a proper tcp proxy would do. Unfortunately I seem to don't understand how that has to work in Rust. Can someone give me an example without 3rd party dependencies?
Here is my code. handle_connection gets called from accepting the connection.
fn forward(direction: &str, input: &mut BufReader<TcpStream>, output: &mut BufWriter<TcpStream>) {
loop {
let mut buffer = [0;1024];
debug!("{} Reading", direction);
match input.read(&mut buffer) {
Ok(bytes) => {
debug!("Read {} bytes", bytes);
if bytes < 1 {
break;
}
match output.write_all(&mut buffer) {
Ok(_) => {
debug!("Forwarded {:#?} bytes", bytes);
output.flush();
if bytes < 1024 {
break; // abort when everything is sent
}
},
Err(error) => panic!("Could not forward: {}", error)
}
},
Err(error) => panic!("Could not read: {}", error)
}
}
}
fn handle_connection(mapping: ProxyMapping, incoming: TcpStream) -> Result<(), String> {
info!("Incoming connection from {}", incoming.peer_addr().map_err(|e| format!("{}", e))?);
// Try to connect to target
let outgoing = TcpStream::connect_timeout(
&mapping.get_target_addr()?,
Duration::from_secs(1)
)?;
// forward tcp steam
debug!("Start sync");
let input_clone = input.try_clone()
.map_err(|error| format!("Couldn't clone {}", error))?;
let mut input_read = BufReader::new(input);
let mut input_write = BufWriter::new(input_clone);
let output_clone = output.try_clone()
.map_err(|error| format!("Couldn't clone {}", error))?;
let mut output_read = BufReader::new(output);
let mut output_write = BufWriter::new(output_clone);
debug!("spawn sync");
loop {
debug!("Forward");
forward("forward", &mut input_read, &mut output_write);
debug!("Backward");
forward("backward",&mut output_read, &mut input_write);
}
Ok(())
}
So far it proxies some data but for http requests it stops at backward reading as I don't know yet when I should shutdown a connection. After some time it just floods the log with read 0 bytes. I added a loop for the sync as for one tcp connection multiple packets could be sent. Any ideas how to improve this?

Graceful exit TcpListener.incoming()

From the rust std net library:
let listener = TcpListener::bind(("127.0.0.1", port)).unwrap();
info!("Opened socket on localhost port {}", port);
// accept connections and process them serially
for stream in listener.incoming() {
break;
}
info!("closed socket");
How does one make the listener stop listening? It says in the API that when the listener is dropped, it stops. But how do we drop it if incoming() is a blocking call? Preferably without external crates like tokio/mio.
You'll want to put the TcpListener into non-blocking mode using the set_nonblocking() method, like so:
use std::io;
use std::net::TcpListener;
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
listener.set_nonblocking(true).expect("Cannot set non-blocking");
for stream in listener.incoming() {
match stream {
Ok(s) => {
// do something with the TcpStream
handle_connection(s);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
// Decide if we should exit
break;
// Decide if we should try to accept a connection again
continue;
}
Err(e) => panic!("encountered IO error: {}", e),
}
}
Instead of waiting for a connection, the incoming() call will immediately return a Result<> type. If Result is Ok(), then a connection was made and you can process it. If the Result is Err(WouldBlock), this isn't actually an error, there just wasn't a connection pending at the exact moment incoming() checked the socket.
Note that in the WouldBlock case, you may want to put a sleep() or something before continuing, otherwise your program will rapidly poll the incoming() function checking for a connection, resulting in high CPU usage.
Code example adapted from here
The standard library doesn't provide an API for this, but there are a few strategies you can use to work around it:
Shut down reads on the socket
You can use platform-specific APIs to shutdown reads on the socket which will cause the incoming iterator to return an error. You can then break out of handling connections when the error is received. For example, on a Unix system:
use std::net::TcpListener;
use std::os::unix::io::AsRawFd;
use std::thread;
let listener = TcpListener::bind("localhost:0")?;
let fd = listener.as_raw_fd();
let handle = thread::spawn(move || {
for connection in listener.incoming() {
match connection {
Ok(connection) => { /* handle connection */ }
Err(_) => break,
}
});
libc::shutdown(fd, libc::SHUT_RD);
handle.join();
Force the listener to wake up
Another (cross-platform) trick is to set a variable indicating that you want to stop listening, and then connect to the socket yourself to force the listening thread to wake up. When the listening thread wakes up, it checks the "stop listening" variable, and then exits cleanly if it's set.
use std::net::{TcpListener, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
let listener = TcpListener::bind("localhost:0")?;
let local_addr = listener.local_addr()?;
let shutdown = Arc::new(AtomicBool::new(false));
let server_shutdown = shutdown.clone();
let handle = thread::spawn(move || {
for connection in listener.incoming() {
if server_shutdown.load(Ordering::Relaxed) {
return;
}
match connection {
Ok(connection) => { /* handle connection */ }
Err(_) => break,
}
}
});
shutdown.store(true, Ordering::Relaxed);
let _ = TcpStream::connect(local_addr);
handle.join().unwrap();
You can poll your socket with an eventfd, which used for signaling.
I wrote a helper for this.
let shutdown = EventFd::new();
let listener = TcpListener::bind("0.0.0.0:12345")?;
let incoming = CancellableIncoming::new(&listener, &shutdown);
for stream in incoming {
// Your logic
}
// While in other thread
shutdown.add(1); // Light the shutdown signal, now your incoming loop exits gracefully.
use nix;
use nix::poll::{poll, PollFd, PollFlags};
use nix::sys::eventfd::{eventfd, EfdFlags};
use nix::unistd::{close, write};
use std;
use std::net::{TcpListener, TcpStream};
use std::os::unix::io::{AsRawFd, RawFd};
pub struct EventFd {
fd: RawFd,
}
impl EventFd {
pub fn new() -> Self {
EventFd {
fd: eventfd(0, EfdFlags::empty()).unwrap(),
}
}
pub fn add(&self, v: i64) -> nix::Result<usize> {
let b = v.to_le_bytes();
write(self.fd, &b)
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl Drop for EventFd {
fn drop(&mut self) {
let _ = close(self.fd);
}
}
// -----
//
pub struct CancellableIncoming<'a> {
listener: &'a TcpListener,
eventfd: &'a EventFd,
}
impl<'a> CancellableIncoming<'a> {
pub fn new(listener: &'a TcpListener, eventfd: &'a EventFd) -> Self {
Self { listener, eventfd }
}
}
impl<'a> Iterator for CancellableIncoming<'a> {
type Item = std::io::Result<TcpStream>;
fn next(&mut self) -> Option<std::io::Result<TcpStream>> {
use nix::errno::Errno;
let fd = self.listener.as_raw_fd();
let evfd = self.eventfd.as_raw_fd();
let mut poll_fds = vec![
PollFd::new(fd, PollFlags::POLLIN),
PollFd::new(evfd, PollFlags::POLLIN),
];
loop {
match poll(&mut poll_fds, -1) {
Ok(_) => break,
Err(nix::Error::Sys(Errno::EINTR)) => continue,
_ => panic!("Error polling"),
}
}
if poll_fds[0].revents().unwrap() == PollFlags::POLLIN {
Some(self.listener.accept().map(|p| p.0))
} else if poll_fds[1].revents().unwrap() == PollFlags::POLLIN {
None
} else {
panic!("Can't be!");
}
}
}

How to read line-by-line from a TcpStream?

I'm following the Make a simple TCP server example in the nightly docs. I've connected via telnet and I'd like to see data sent line by line. Right now I'm read_to_string and I only get data when I close the telnet connection. I'd like to be able to read each line at the server after the user in the telnet session hits enter.
This is what I came up after reading the docs for BufferedReader.
fn handle_client(mut stream: TcpStream) {
let wresult = stream.write_line("Welcome.");
match wresult {
Err(e) => {
println!("error writing: {}", e);
}
_ => {}
}
let mut reader = BufferedReader::new(stream);
loop {
let result = reader.read_line();
match result {
Ok(data) => {
println!("{}", data.as_slice().trim());
}
Err(e) => {
println!("error reading: {}", e);
break;
}
}
}
}

Resources