use std::{io, net::SocketAddr, thread};
use compio::{BufResult, net::UdpSocket, runtime::Runtime};
use compio_pool::{ManageConnection, Pool, cpu};
use socket2::{Domain, Protocol, Socket, Type};
struct Buffers;
impl ManageConnection for Buffers {
type Connection = Vec<u8>;
type Error = io::Error;
async fn connect(&self) -> io::Result<Vec<u8>> {
Ok(Vec::with_capacity(64 * 1024))
}
async fn is_valid(&self, _buf: &mut Vec<u8>) -> io::Result<()> {
Ok(())
}
fn has_broken(&self, _buf: &mut Vec<u8>) -> bool {
false
}
}
fn bind_reuseport_udp(addr: SocketAddr) -> io::Result<std::net::UdpSocket> {
let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
socket.set_reuse_address(true)?;
socket.set_reuse_port(true)?;
socket.bind(&addr.into())?;
Ok(socket.into())
}
fn main() -> io::Result<()> {
let addr: SocketAddr = std::env::args()
.nth(1)
.unwrap_or_else(|| "0.0.0.0:7000".into())
.parse()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("bad address: {e}")))?;
let capacity: u32 = std::env::args()
.nth(2)
.and_then(|s| s.parse().ok())
.unwrap_or(1024);
let pool = Pool::builder().max_size(capacity).build(Buffers);
let cores = cpu::cores();
if cores.is_empty() {
return Err(io::Error::new(io::ErrorKind::Unsupported, "no cores to run on"));
}
println!(
"udp_echo on {addr}: {} workers, capacity {capacity}/worker",
cores.len()
);
let mut handles = Vec::new();
for (index, core) in cores.into_iter().enumerate() {
let pool = pool.clone();
let handle = thread::Builder::new()
.name(format!("worker/{index}"))
.spawn(move || worker(core, addr, pool))?;
handles.push(handle);
}
for handle in handles {
let _ = handle.join();
}
Ok(())
}
fn worker(core: cpu::CoreId, addr: SocketAddr, pool: Pool<Buffers>) {
cpu::pin_current(core);
let runtime = Runtime::builder().build().expect("build compio runtime");
runtime.block_on(async move {
let local = pool.local();
let std_socket = bind_reuseport_udp(addr).expect("bind SO_REUSEPORT UDP");
let socket = UdpSocket::from_std(std_socket).expect("wrap socket in ring");
loop {
let mut lease = match local.get().await {
Ok(lease) => lease,
Err(_) => continue,
};
let mut buf = std::mem::take(&mut *lease);
buf.clear();
let BufResult(res, buf) = socket.recv_from(buf).await;
let (buf, peer) = match res {
Ok((_n, peer)) => (buf, peer),
Err(_) => {
*lease = buf;
continue;
}
};
let BufResult(_sent, buf) = socket.send_to(buf, peer).await;
*lease = buf; }
});
}