Hi \o,
I'm trying to send a file (HTTP) on the TCP level. So basically, it's a low-level static server.
I download it using wget
nothing fancy. Usually, it works well. But sometimes, it just doesn't. wget
say "Connection reset by peer" shortly before the end.
Here is what wget
shown
$ env LC_ALL=C wget localhost:8080/big # LC_ALL => Set env to english
--2019-09-24 17:21:09-- http://localhost:8080/big
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:8080... failed: Connection refused. # I'm not listening on ipv6
Connecting to localhost (localhost)|127.0.0.1|:8080... connected. # but on ipv4
HTTP request sent, awaiting response... 200 OK
Length: 1831366851 (1.7G) [application/octet-stream]
Saving to: 'big'
big 99%[============================================================================> ] 1.70G 570MB/s in 3.1s
2019-09-24 17:21:12 (570 MB/s) - Read error at byte 1828713146/1831366851 (Connection reset by peer). Retrying.
It will retry and eventually it will work.
I studied a little and it seems that "Connection reset by peer" mean that the socket receive a TCP "RST". My code doesn't seem to send a RST so I assume that it's send by the stream when Dropped (Drop trait).
To verify it, I use a short delay before dropping the Stream and it works very well. But that's not how i'm suppose to close a socket ! I just want the normal process FIN
ACK
FIN
ACK
.
I tried shutdown
doesn't work. Using wireshark is hard because the file is big, will try with a medium sized file.
Can you help me to close that socket correctly ? I just want a « active close » to certify that the client receive everything.
Here is the code :
use std::{io, ptr};
use std::os::unix::io::AsRawFd;
use std::fs::File;
use std::net::TcpListener;
use std::io::Write;
use std::convert::TryInto;
use std::{thread, time};
pub fn send_file<R: AsRawFd, W: AsRawFd>(r: &mut R, w: &mut W, size: usize) -> io::Result<usize> {
match unsafe { libc::sendfile(w.as_raw_fd(), r.as_raw_fd(), ptr::null_mut(), size) } {
-1 => Err(io::Error::last_os_error()),
copied => Ok(copied as usize),
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
let delay = false; // Set to true to add a short delay
let short_time = time::Duration::from_millis(500);
// accept connections and process them serially
for stream in listener.incoming() {
let mut stream = stream.unwrap();
stream.set_nodelay(false)?;
let http_header_first = "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-length: ";
let http_header_last = "\r\nServer: testSRV/0.1\r\nAccept-Ranges: bytes\r\n\r\n";
let mut file = File::open("big")?;
let metadata = file.metadata()?;
let size : usize = metadata.len().try_into().unwrap();
let http_header = http_header_first.to_owned() + &size.to_string() + http_header_last;
std::println!("File of {} bytes", size);
stream.write(http_header.as_bytes())?;
let mut total_send = 0;
let packet_size : usize = 2<<15;
println!("Packet size: {}", packet_size);
loop {
let this_packet_size = if total_send + packet_size > size { size - total_send } else { packet_size };
let i = send_file(&mut file, &mut stream, this_packet_size);
match i {
Ok(_) => (),
Err(_) => { println!("Error !") }
}
if let Err(_) = i{
println!("Error !");
return Ok(());
}
let i = i.unwrap();
total_send += i;
if total_send >= size {
break
}
}
println!("{} bytes send !", total_send);
// stream.flush()?; // Doesn't seem to work
stream.shutdown(std::net::Shutdown::Both)?;
if delay {
thread::sleep(short_time);
}
println!("Go !");
}
Ok(())
}
Thank you.