2015-05-04 33 views
3

連接超時,我試圖連接到使用下面的代碼是可達服務器:如何設置上TcpStream

println!("Connecting"); 
TcpStream::connect(s).unwrap(); 
println!("Connected"); 

當我運行的代碼,它卡住在第二行。

輸出:

Connecting 
+2

不是永遠。它應該在一分鐘後超時。 – EJP

回答

0

它不是目前能夠改變上製作一個TCP連接的超時。網絡堆棧將擁有自己的默認設置,這些默認設置可能因操作系統而異;我相信一分鐘是典型的超時時間。

1

沒有簡單,標準的方法來做到這一點,所以我通過nix cratethis answer移植到了Rust上做了一些小小的改動:一旦建立連接,將套接字重新設置爲阻塞狀態,以便它可以與Rust的std I/O一起使用,當然也可以用std::net::TcpStream包裝。

這裏是回購:https://github.com/filsmick/rust-tcp-connection-timeout

從SRC/lib.rs:

pub fn tcp_connect_with_timeout(socket_addr: std::net::SocketAddr, timeout: Duration) -> Result<TcpStream, ConnectionError> { 
    // Create a socket file descriptor. 
    let socket_fd = try!(nix::sys::socket::socket(
    nix::sys::socket::AddressFamily::Inet, 
    nix::sys::socket::SockType::Stream, 
    nix::sys::socket::SockFlag::empty() 
)); 

    // Set the socket to non-blocking mode so we can `select()` on it. 
    try!(nix::fcntl::fcntl(
    socket_fd, 
    nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::O_NONBLOCK) 
)); 

    let connection_result = nix::sys::socket::connect(
    socket_fd, 
    &(nix::sys::socket::SockAddr::Inet(nix::sys::socket::InetAddr::from_std(&socket_addr))) 
); 

    match connection_result { 
    Ok(_) =>(), 
    Err(e) => { 
     match e { 
     nix::Error::Sys(errno) => { 
      match errno { 
      nix::errno::Errno::EINPROGRESS =>(), // socket is non-blocking so an EINPROGRESS is to be expected 
      _ => return Err(ConnectionError::from(e)) 
      } 
     } 
     nix::Error::InvalidPath => unreachable!() // 
     } 
    } 
    } 

    let mut timeout_timeval = nix::sys::time::TimeVal { 
    tv_sec: timeout.as_secs() as i64, 
    tv_usec: timeout.subsec_nanos() as i32 
    }; 

    // Create a new fd_set monitoring our socket file descriptor. 
    let mut fdset = nix::sys::select::FdSet::new(); 
    fdset.insert(socket_fd); 

    // `select()` on it, will return when the connection succeeds or times out. 
    let select_res = try!(nix::sys::select::select(
    socket_fd + 1, 
    None, 
    Some(&mut fdset), 
    None, 
    &mut timeout_timeval 
)); 

    // This it what fails if `addr` is unreachable. 
    if select_res != 1 { 
    println!("select return value: {}", select_res); 
    return Err(ConnectionError::SelectError); 
    } 

    // Make sure the socket encountered no error. 
    let socket_error_code = try!(nix::sys::socket::getsockopt(
    socket_fd, 
    nix::sys::socket::sockopt::SocketError 
)); 

    if socket_error_code != 0 { 
    return Err(ConnectionError::SocketError(socket_error_code)); 
    } 

    // Set the socket back to blocking mode so it can be used with std's I/O facilities. 
    try!(nix::fcntl::fcntl(
    socket_fd, 
    nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::empty()) 
)); 

    // Wrap it in a TcpStream and return that stream. 
    Ok(
    unsafe { TcpStream::from_raw_fd(socket_fd) } 
) 
} 

ConnectionErrorerror.rs定義,但如果你想通過解包,而不是使用try!您可以忽略它。

雖然有一個問題:select在編寫本文時尚未在主要的nix庫中實現,但有一個pending Pull Request,因此您必須在此期間依靠叉(它不應該儘管):

[dependencies] 
nix = { git = "https://github.com/utkarshkukreti/nix-rust.git", branch = "add-sys-select" }