-
Notifications
You must be signed in to change notification settings - Fork 716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to wait for a socket to be writable? udp / Interest::READABLE | Interest::WRITABLE #1718
Comments
In general it's ok to try an I/O operations even if you don't get an event for it. However you have to handle the As for the not getting a writable event, can you share some code that demonstrates that? Because we have tests for it: Lines 123 to 145 in 9f21ce1
|
Thank you! That makes sense. As for the TX events: # Cargo.toml
[package]
name = "udp_test"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2.148"
mio = { version = "0.8", features = ["os-ext", "os-poll", "net"] } use std::{io::{self, ErrorKind, Write}, net::SocketAddr, thread, os::fd::AsRawFd, time::Duration};
use mio::{Token, Poll, unix::pipe::Receiver, Events, Interest, net::UdpSocket};
const UDP: Token = Token(0);
const PRODUCER: Token = Token(1);
fn main() -> io::Result<()> {
// register event sources
let port = 4242u16;
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
let mut socket = UdpSocket::bind(listen_addr)?;
let (pipe_tx, mut pipe_rx) = mio::unix::pipe::new()?;
pipe_rx.set_nonblocking(true)?;
let mut poll = Poll::new()?;
poll.registry().register(&mut socket, UDP, Interest::READABLE | Interest::WRITABLE)?;
poll.registry().register(&mut pipe_rx, PRODUCER, Interest::READABLE)?;
// producer thread writing stuff onto the pipe in intervals
thread::spawn(move || {
loop {
let buf = b"test";
if let Err(e) = pipe_tx.try_io(|| {
let buf_ptr = &buf as *const _ as *const _;
let res = unsafe { libc::write(pipe_tx.as_raw_fd(), buf_ptr, buf.len()) };
if res != -1 {
Ok(res as usize)
} else {
Err(io::Error::last_os_error())
}
}) {
eprintln!("{e}");
}
thread::sleep(Duration::from_millis(10));
}
});
let mut events = Events::with_capacity(10);
loop {
poll.poll(&mut events, None)?;
for event in &events {
match event.token() {
UDP => {
if event.is_readable() {
handle_udp_rx(&socket);
println!("udp RX {:?}", event);
}
if event.is_writable() {
println!("udp TX {:?}", event);
}
}
PRODUCER => {
handle_producer(&pipe_rx, &socket);
}
_ => panic!()
}
}
events.clear();
}
}
fn handle_udp_rx(socket: &UdpSocket) {
loop {
let mut buf = vec![0u8; 4];
let (count, addr) = match socket.recv_from(&mut buf) {
Ok((count, addr)) => (count, addr),
Err(e) if e.kind() == ErrorKind::WouldBlock => return,
Err(e) => {
eprintln!("{}", e);
continue
}
};
println!("{addr} {:?} ({count})", buf);
}
}
fn handle_producer(pipe_rx: &Receiver, socket: &UdpSocket) {
let mut buf = [0u8; 4];
if let Err(e) = pipe_rx.try_io(|| {
let buf_ptr = &mut buf as *mut _ as *mut _;
let res = unsafe { libc::read(pipe_rx.as_raw_fd(), buf_ptr, buf.len()) };
if res != -1 {
Ok(res as usize)
} else {
Err(io::Error::last_os_error())
}
}) {
println!("{e}");
}
if let Err(e) = socket.send_to(&buf, "192.168.178.189:4242".parse::<SocketAddr>().unwrap()) {
// eprintln!("{e}");
eprint!("e");
} else {
eprint!(".");
}
std::io::stderr().flush().unwrap();
if let Err(e) = socket.send_to(&buf, "192.168.178.172:4242".parse::<SocketAddr>().unwrap()) {
// eprintln!("{e}");
eprint!("e");
} else {
eprint!(".");
}
std::io::stderr().flush().unwrap();
if let Err(e) = socket.send_to(&buf, "192.168.178.125:4242".parse::<SocketAddr>().unwrap()) {
// eprintln!("{e}");
eprint!("e");
} else {
eprint!(".");
}
std::io::stderr().flush().unwrap();
} It results in an output that looks like this:
I kind of expected to receive a tx event everytime I could write to the socket (which should be after each The printed Now my real problem is the fact that sometimes it takes ~3 seconds until a new sendto() is possible. So this likely may not be related to mio at all. |
Mio's unix pipes are already non-blocking, so this is not needed.
Why not
This should be
Can do
Are you sure this IP works? Why not use
That can be because of a range of reasons. Running anti-virus/malware software by any chance?
It would recommend enabling debug logging for Mio, it will show all events that it processes. |
Thank you for the suggestion, I just forgot about the Read and Write traits ... (kind of new to rust). I'm on Linux and not using any AV software and also tried this with the firewall disabled. The ips correspond to different interfaces on my laptop, with only one of them reachable. What's strange is the fact, that all of this works, as long as I dont send to more than one of the ips. (I can send packages to either of the ips indefinitely without getting WouldBlock) however if I send to multiple interfaces these 3 second delays occur. |
I don't have a good explanation for this either. |
Well thanks a lot, you definitely helped me! I will close this, as it has nothing to do with mio :) |
I'm currently trying to implement a udp server that both receives and sends events.
In the example Server after reading from the socket, the response is simply sent, without ever waiting for a
writable event. (writable events are in fact not registered with the poll):
https://github.com/tokio-rs/mio/blob/9f21ce1eed11cc85a75164475317e1124759098c/examples/udp_server.rs#L61C36-L61C43
I also tried to use poll (using Interest::READABLE | Interest::WRITABLE) to wait before writing and noticed that I never received a WRITABLE event for the udp socket at all.
The documentation states:
However this does not seem to work? I cant seem to get the socket to send me any writable events.
To my understanding this kind of makes sense, since udp should theoretically never block.
Is this intended behaviour?
The text was updated successfully, but these errors were encountered: