esp-rs / rust

Rust for the xtensa architecture. Built in targets for the ESP32 and ESP8266
https://www.rust-lang.org
Other
730 stars 35 forks source link

Setting TCP_NODELAY does not work using std::net::TcpStream #218

Open jothan opened 5 months ago

jothan commented 5 months ago

I tried this code:

// Wi-Fi setup, etc....

let s = std::net::TcpStream::connect(("example.com", 80))?;
log::info!("socket connected");
if let Err(e) = s.set_nodelay(true) {
    log::error!("nodelay error: {e:?}");
};

I expected to see this happen: the socket should have been put in nodelay mode without issue.

Instead, this happened:

I (5067) myproject: socket connected
E (5067) myproject: nodelay error: Os { code: 109, kind: Uncategorized, message: "Protocol not available" }

The TCP_NODELAY option should be supported: https://docs.espressif.com/projects/esp-idf/en/v5.2.1/esp32/api-guides/lwip.html#tcp-options

Meta

rustc --version --verbose:

rustc 1.77.0-nightly (424037dcb 2024-03-18) (1.77.0.0)
binary: rustc
commit-hash: 424037dcb6937656992747c4bbff310c40061498
commit-date: 2024-03-18
host: x86_64-unknown-linux-gnu
release: 1.77.0-nightly
LLVM version: 17.0.1
jothan commented 5 months ago

I think I found the bug.

LWIP defines TCP_NODELAY as 1 in esp-idf/components/lwip/lwip/src/include/lwip/sockets.h:

#define TCP_NODELAY    0x01    /* don't delay send to coalesce packets */

While Rust uses the libc::TCP_NODELAY with a value of 8193 defined by the libc crate in src/unix/newlib/mod.rs:

cfg_if! {
    if #[cfg(target_os = "vita")] {
        pub const TCP_NODELAY: ::c_int = 1;
        pub const TCP_MAXSEG: ::c_int = 2;
    } else {
        pub const TCP_NODELAY: ::c_int = 8193;
        pub const TCP_MAXSEG: ::c_int = 8194;
    }
}
jothan commented 5 months ago

I found this pull request that would fix this issue on the libc crate repository: https://github.com/rust-lang/libc/pull/3345

DavidVentura commented 5 months ago

I've hit the same, for now I've done this as a workaround

extern "C" {                                                                                                                                        
    fn lwip_setsockopt(fd: i32, level: i32, optname: i32);                                                                                          
}   
unsafe {                                                                                                                                    
    lwip_setsockopt(                                                                                                                        
              conn.as_raw_fd(),                                                                                                                   
              6, // tcp https://github.com/stm32duino/LwIP/blob/main/src/lwip/sockets.h#L250                                               
              1, // nodelay https://github.com/stm32duino/LwIP/blob/main/src/lwip/sockets.h#L279                                         
      )
};