来自rust std网络库:
let listener = TcpListener::bind(("127.0.0.1", port)).unwrap();
info!("Opened socket on localhost port {}", port);
// accept connections and process them serially
for stream in listener.incoming() {
break;
}
info!("closed socket");
如何使收听者停止收听?它在API中说,当侦听器被删除时,它会停止。但是,如果incoming()
是阻塞呼叫,我们如何删除它?最好不要使用外部包装箱,例如tokio / mio。
答案 0 :(得分:2)
您将要使用set_nonblocking()方法将TcpListener置于非阻止模式,如下所示:
use std::io;
use std::net::TcpListener;
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
listener.set_nonblocking(true).expect("Cannot set non-blocking");
for stream in listener.incoming() {
match stream {
Ok(s) => {
// do something with the TcpStream
handle_connection(s);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
// Decide if we should exit
break;
// Decide if we should try to accept a connection again
continue;
}
Err(e) => panic!("encountered IO error: {}", e),
}
}
而不是等待连接,incoming()调用将立即返回Result <>类型。如果Result为Ok(),则建立了连接,您可以对其进行处理。如果Result是Err(WouldBlock),则实际上这不是错误,只是在incoming()检查套接字的那一刻没有连接挂起。
请注意,在WouldBlock情况下,您可能需要在继续之前放置sleep()或其他内容,否则您的程序将快速轮询传入的()函数以检查连接,从而导致CPU使用率高。
代码示例改编自here
答案 1 :(得分:2)
您可以使用poll
eventfd
来使用套接字,该套接字用于发送信号。
我为此写了一个助手。
let shutdown = EventFd::new();
let listener = TcpListener::bind("0.0.0.0:12345")?;
let incoming = CancellableIncoming::new(&listener, &shutdown);
for stream in incoming {
// Your logic
}
// While in other thread
shutdown.add(1); // Light the shutdown signal, now your incoming loop exits gracefully.
use nix;
use nix::poll::{poll, PollFd, PollFlags};
use nix::sys::eventfd::{eventfd, EfdFlags};
use nix::unistd::{close, write};
use std;
use std::net::{TcpListener, TcpStream};
use std::os::unix::io::{AsRawFd, RawFd};
pub struct EventFd {
fd: RawFd,
}
impl EventFd {
pub fn new() -> Self {
EventFd {
fd: eventfd(0, EfdFlags::empty()).unwrap(),
}
}
pub fn add(&self, v: i64) -> nix::Result<usize> {
let b = v.to_le_bytes();
write(self.fd, &b)
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl Drop for EventFd {
fn drop(&mut self) {
let _ = close(self.fd);
}
}
// -----
//
pub struct CancellableIncoming<'a> {
listener: &'a TcpListener,
eventfd: &'a EventFd,
}
impl<'a> CancellableIncoming<'a> {
pub fn new(listener: &'a TcpListener, eventfd: &'a EventFd) -> Self {
Self { listener, eventfd }
}
}
impl<'a> Iterator for CancellableIncoming<'a> {
type Item = std::io::Result<TcpStream>;
fn next(&mut self) -> Option<std::io::Result<TcpStream>> {
use nix::errno::Errno;
let fd = self.listener.as_raw_fd();
let evfd = self.eventfd.as_raw_fd();
let mut poll_fds = vec![
PollFd::new(fd, PollFlags::POLLIN),
PollFd::new(evfd, PollFlags::POLLIN),
];
loop {
match poll(&mut poll_fds, -1) {
Ok(_) => break,
Err(nix::Error::Sys(Errno::EINTR)) => continue,
_ => panic!("Error polling"),
}
}
if poll_fds[0].revents().unwrap() == PollFlags::POLLIN {
Some(self.listener.accept().map(|p| p.0))
} else if poll_fds[1].revents().unwrap() == PollFlags::POLLIN {
None
} else {
panic!("Can't be!");
}
}
}
答案 2 :(得分:2)
注意设置
listener.set_nonblocking(true).expect("Cannot set non-blocking");
如 @effect's answer 中推荐的那样,在所有流上也会自动将非阻塞设置为 true。也许你想要这个,也许你不想要;我不得不将它们设置回 false
stream.set_nonblocking(false);
答案 3 :(得分:0)
标准库没有为此提供API,但是您可以使用特定于平台的API来关闭套接字上的读取,这将导致incoming
迭代器返回错误。收到错误后,您可以中断处理连接。例如,在Unix系统上:
use std::net::TcpListener;
use std::os::unix::io::AsRawFd;
use std::thread;
let listener = TcpListener::bind("localhost:0")?;
let fd = listener.as_raw_fd();
let handle = thread::spawn(move || {
for connection in listener.incoming() {
match connection {
Ok(connection) => /* handle connection */
Err(_) => break,
}
});
libc::shutdown(fd, libc::SHUT_RD);
handle.join();