Skip to content
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

feat: Allow changing the UDP send/receive buffer sizes #2179

Merged
merged 1 commit into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 13 additions & 6 deletions perf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::net::SocketAddr;

use anyhow::{Context, Result};
use quinn::udp::UdpSocketState;
use rustls::crypto::ring::cipher_suite;
use socket2::{Domain, Protocol, Socket, Type};
use tracing::warn;
Expand All @@ -25,22 +26,28 @@ pub fn bind_socket(
socket
.bind(&socket2::SockAddr::from(addr))
.context("binding endpoint")?;
socket
.set_send_buffer_size(send_buffer_size)

let socket_state = UdpSocketState::new((&socket).into())?;
socket_state
.set_send_buffer_size((&socket).into(), send_buffer_size)
.context("send buffer size")?;
socket
.set_recv_buffer_size(recv_buffer_size)
socket_state
.set_recv_buffer_size((&socket).into(), recv_buffer_size)
.context("recv buffer size")?;

let buf_size = socket.send_buffer_size().context("send buffer size")?;
let buf_size = socket_state
.send_buffer_size((&socket).into())
.context("send buffer size")?;
if buf_size < send_buffer_size {
warn!(
"Unable to set desired send buffer size. Desired: {}, Actual: {}",
send_buffer_size, buf_size
);
}

let buf_size = socket.recv_buffer_size().context("recv buffer size")?;
let buf_size = socket_state
.recv_buffer_size((&socket).into())
.context("recv buffer size")?;
if buf_size < recv_buffer_size {
warn!(
"Unable to set desired recv buffer size. Desired: {}, Actual: {}",
Expand Down
2 changes: 1 addition & 1 deletion quinn-udp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "quinn-udp"
version = "0.5.10"
version = "0.5.11"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
Expand Down
24 changes: 24 additions & 0 deletions quinn-udp/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ impl UdpSocketState {
1
}

/// Resize the send buffer of `socket` to `bytes`
#[inline]
pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
socket.0.set_send_buffer_size(bytes)
}

/// Resize the receive buffer of `socket` to `bytes`
#[inline]
pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
socket.0.set_recv_buffer_size(bytes)
}

/// Get the size of the `socket` send buffer
#[inline]
pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
socket.0.send_buffer_size()
}

/// Get the size of the `socket` receive buffer
#[inline]
pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
socket.0.recv_buffer_size()
}

#[inline]
pub fn may_fragment(&self) -> bool {
true
Expand Down
24 changes: 24 additions & 0 deletions quinn-udp/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,30 @@ impl UdpSocketState {
self.gro_segments
}

/// Resize the send buffer of `socket` to `bytes`
#[inline]
pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
socket.0.set_send_buffer_size(bytes)
}

/// Resize the receive buffer of `socket` to `bytes`
#[inline]
pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
socket.0.set_recv_buffer_size(bytes)
}

/// Get the size of the `socket` send buffer
#[inline]
pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
socket.0.send_buffer_size()
}

/// Get the size of the `socket` receive buffer
#[inline]
pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
socket.0.recv_buffer_size()
}

/// Whether transmitted datagrams might get fragmented by the IP layer
///
/// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
Expand Down
24 changes: 24 additions & 0 deletions quinn-udp/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,30 @@ impl UdpSocketState {
64
}

/// Resize the send buffer of `socket` to `bytes`
#[inline]
pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
socket.0.set_send_buffer_size(bytes)
}

/// Resize the receive buffer of `socket` to `bytes`
#[inline]
pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
socket.0.set_recv_buffer_size(bytes)
}

/// Get the size of the `socket` send buffer
#[inline]
pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
socket.0.send_buffer_size()
}

/// Get the size of the `socket` receive buffer
#[inline]
pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
socket.0.recv_buffer_size()
}

#[inline]
pub fn may_fragment(&self) -> bool {
false
Expand Down
77 changes: 75 additions & 2 deletions quinn-udp/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd", solarish)))]
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::net::{SocketAddr, SocketAddrV6};
use std::{
io::IoSliceMut,
net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, UdpSocket},
slice,
};

Expand Down Expand Up @@ -186,6 +186,79 @@ fn gso() {
);
}

#[test]
fn socket_buffers() {
const BUFFER_SIZE: usize = 123456;
const FACTOR: usize = if cfg!(any(target_os = "linux", target_os = "android")) {
2 // Linux and Android set the buffer to double the requested size
} else {
1 // Everyone else is sane.
};

let send = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::DGRAM,
Some(socket2::Protocol::UDP),
)
.unwrap();
let recv = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::DGRAM,
Some(socket2::Protocol::UDP),
)
.unwrap();
for sock in [&send, &recv] {
sock.bind(&socket2::SockAddr::from(SocketAddrV4::new(
Ipv4Addr::LOCALHOST,
0,
)))
.unwrap();

let socket_state = UdpSocketState::new(sock.into()).expect("created socket state");

// Change the send buffer size.
let buffer_before = socket_state.send_buffer_size(sock.into()).unwrap();
assert_ne!(
buffer_before,
BUFFER_SIZE * FACTOR,
"make sure buffer is not already desired size"
);
socket_state
.set_send_buffer_size(sock.into(), BUFFER_SIZE)
.expect("set send buffer size {buffer_before} -> {BUFFER_SIZE}");
let buffer_after = socket_state.send_buffer_size(sock.into()).unwrap();
assert_eq!(
buffer_after,
BUFFER_SIZE * FACTOR,
"setting send buffer size to {BUFFER_SIZE} resulted in {buffer_before} -> {buffer_after}",
);

// Change the receive buffer size.
let buffer_before = socket_state.recv_buffer_size(sock.into()).unwrap();
socket_state
.set_recv_buffer_size(sock.into(), BUFFER_SIZE)
.expect("set recv buffer size {buffer_before} -> {BUFFER_SIZE}");
let buffer_after = socket_state.recv_buffer_size(sock.into()).unwrap();
assert_eq!(
buffer_after,
BUFFER_SIZE * FACTOR,
"setting recv buffer size to {BUFFER_SIZE} resulted in {buffer_before} -> {buffer_after}",
);
}

test_send_recv(
&send,
&recv,
Transmit {
destination: recv.local_addr().unwrap().as_socket().unwrap(),
ecn: None,
contents: b"hello",
segment_size: None,
src_ip: None,
},
);
}

fn test_send_recv(send: &Socket, recv: &Socket, transmit: Transmit) {
let send_state = UdpSocketState::new(send.into()).unwrap();
let recv_state = UdpSocketState::new(recv.into()).unwrap();
Expand Down