-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TcpStream write
silently loses one message
#2234
Comments
This is more or less a race condition. The state of the client and server is fully decoupled. When one peer observes something it doesn't mean it is visible to the other. E.g. the Regarding
The only way you can really know is by sending a confirmation on application level. That's e.g. the response in HTTP, or replies in all the RPC protocols. Without those, the client would neither have an idea whether something was delivered nor processed by the server. Oh, and an interesting fact: Even with a reply, the server won't have any confirmation whether the client received it. |
I could be wrong, but I don't think this is a race condition. In fact, this bug happens even you send a message hours after the server went down; this is exactly what happened in our production environment. I am rewriting a Perl application in Rust and the original application does not have this problem and I guess that Perl uses the same OS socket buffers under the hood. |
I agree to @Matthias247 that this is a race condition that cannot be fixed at the TCP layer. @ufoscout try the following: Put the server on a different machine, establish the connection and unplug the network cable between the two. Then on the sender side you can still successfully write messages into the OS-own transmit buffer until it is full (then writes will block). On Linux it takes about 15 minutes until the sender notices that the connection was dropped (the OS tries with growing intervals to resend the transmit buffer but it never receives an ACK anymore and goes into a timeout after about 15 minutes). The receiver will never (I repeat: never) notice that the connection was dropped. Server: use std::io::Read;
fn main() {
let listener = std::net::TcpListener::bind("0.0.0.0:9090").unwrap();
let (mut stream, _) = listener.accept().unwrap();
let mut buf = [0; 1024];
loop {
match stream.read(&mut buf) {
Ok(count) if count == 0 => { println!("client closed connection"); break; },
Ok(count) => println!("read {} bytes", count),
Err(err) => { println!("error: {}", err); break; },
}
}
} Client: use std::io::Write;
fn main() {
let mut stream = std::net::TcpStream::connect("192.168.1.115:9090").unwrap();
let buf = [b'A'; 1024];
stream.write(&buf[..]).unwrap();
println!("sent 1024 bytes");
println!("now unplug the cable and press enter");
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
// Let's fill up the TCP send buffer.
// At some point write() will block and return an error 15 minutes later.
// Btw, you can replug the cable at any time and the connection will still be alive
// (it might take a couple seconds/minutes until the TCP stack notices, because of the growing retry intervals).
let mut total = 0;
loop {
stream.write(&buf[..]).unwrap();
total += 1;
println!("sent {} KiB", total);
}
} |
@gralpli @Matthias247 |
Version
tokio = 0.2.11
Platform
Linux 4.15.0-76-generic #86-Ubuntu SMP Fri Jan 17 17:24:28 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
Description
The issue: Given a TcpStream connected to a remote TCP server, when the server goes down, it is still possible to call the
write
methods on the stream without being notified of the failure.Below you can find a complete reproducer of the issue.
This is the tested flow:
write
and theflush
methods complete successfully, but the message is lost because the server is not availablewrite
fails, but it should have failed already on 'message_2'Is this a bug or a desired behaviour? And, if not a bug, how can we know whether a message was really sent to the server?
Issue reproducer
The text was updated successfully, but these errors were encountered: