如何在 TcpStream 上设置连接超时
How do I set connect timeout on TcpStream
我正在尝试使用以下代码连接到无法访问的服务器:
println!("Connecting");
TcpStream::connect(s).unwrap();
println!("Connected");
当我 运行 代码时,它卡在第二行。
输出:
Connecting
目前无法更改建立 TCP 连接的超时时间。网络堆栈将有自己的默认设置,可能从 OS 到 OS 不等;我认为一分钟是典型的超时。
没有简单、标准的方法来做到这一点,所以我通过移植 this answer to Rust using the nix crate 来做到这一点,并做了一个小改动:一旦建立连接,就将套接字设置回阻塞状态,这样它就可以与 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) }
)
}
ConnectionError
在 error.rs
中定义,但如果您愿意,可以通过解包而不是使用 try!
.
来忽略它
不过有一个问题:select
在撰写本文时尚未在主 nix 存储库中实现,但有一个 pending Pull Request,因此您必须依赖同时分叉(不过合并应该不会花很长时间):
[dependencies]
nix = { git = "https://github.com/utkarshkukreti/nix-rust.git", branch = "add-sys-select" }
如果你在 tokio 中使用异步 Rust,那么你可以使用这个:-
const CONNECTION_TIME: u64 = 100;
...
let (socket, _response) = match tokio::time::timeout(
Duration::from_secs(CONNECTION_TIME),
tokio::net::TcpStream::connect("127.0.0.1:8080")
)
.await
{
Ok(ok) => ok,
Err(e) => panic!(format!("timeout while connecting to server : {}", e)),
}
.expect("Error while connecting to server")
2020 年的问候。
与此同时,答案发生了变化,
它不再是“不容易完成”,而是:
TcpStream::connect_timeout()
https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect_timeout
我正在尝试使用以下代码连接到无法访问的服务器:
println!("Connecting");
TcpStream::connect(s).unwrap();
println!("Connected");
当我 运行 代码时,它卡在第二行。
输出:
Connecting
目前无法更改建立 TCP 连接的超时时间。网络堆栈将有自己的默认设置,可能从 OS 到 OS 不等;我认为一分钟是典型的超时。
没有简单、标准的方法来做到这一点,所以我通过移植 this answer to Rust using the nix crate 来做到这一点,并做了一个小改动:一旦建立连接,就将套接字设置回阻塞状态,这样它就可以与 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) }
)
}
ConnectionError
在 error.rs
中定义,但如果您愿意,可以通过解包而不是使用 try!
.
不过有一个问题:select
在撰写本文时尚未在主 nix 存储库中实现,但有一个 pending Pull Request,因此您必须依赖同时分叉(不过合并应该不会花很长时间):
[dependencies]
nix = { git = "https://github.com/utkarshkukreti/nix-rust.git", branch = "add-sys-select" }
如果你在 tokio 中使用异步 Rust,那么你可以使用这个:-
const CONNECTION_TIME: u64 = 100;
...
let (socket, _response) = match tokio::time::timeout(
Duration::from_secs(CONNECTION_TIME),
tokio::net::TcpStream::connect("127.0.0.1:8080")
)
.await
{
Ok(ok) => ok,
Err(e) => panic!(format!("timeout while connecting to server : {}", e)),
}
.expect("Error while connecting to server")
2020 年的问候。
与此同时,答案发生了变化, 它不再是“不容易完成”,而是:
TcpStream::connect_timeout()
https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.connect_timeout