mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-07 00:00:09 +02:00
net: add TcpStream::set_zero_linger (#7837)
This commit is contained in:
@@ -429,17 +429,41 @@ impl TcpSocket {
|
||||
/// > it. Rely on the `shutdown()`-followed-by-`read()`-eof technique instead.
|
||||
/// >
|
||||
/// > From [The ultimate `SO_LINGER` page, or: why is my tcp not reliable](https://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable)
|
||||
///
|
||||
/// Although this method is deprecated, it will not be removed from Tokio.
|
||||
///
|
||||
/// Note that the special case of setting `SO_LINGER` to zero does not lead to blocking. Tokio
|
||||
/// provides [`set_zero_linger`](Self::set_zero_linger) for this purpose.
|
||||
#[deprecated = "`SO_LINGER` causes the socket to block the thread on drop"]
|
||||
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
self.inner.set_linger(dur)
|
||||
}
|
||||
|
||||
/// Sets a linger duration of zero on this socket by setting the `SO_LINGER` option.
|
||||
///
|
||||
/// This causes the connection to be forcefully aborted ("abortive close") when the socket is
|
||||
/// dropped or closed. Instead of the normal TCP shutdown handshake (`FIN`/`ACK`), a TCP `RST`
|
||||
/// (reset) segment is sent to the peer, and the socket immediately discards any unsent data
|
||||
/// residing in the socket send buffer. This prevents the socket from entering the `TIME_WAIT`
|
||||
/// state after closing it.
|
||||
///
|
||||
/// This is a destructive action. Any data currently buffered by the OS but not yet transmitted
|
||||
/// will be lost. The peer will likely receive a "Connection Reset" error rather than a clean
|
||||
/// end-of-stream.
|
||||
///
|
||||
/// See the documentation for [`set_linger`](Self::set_linger) for additional details on how
|
||||
/// `SO_LINGER` works.
|
||||
pub fn set_zero_linger(&self) -> io::Result<()> {
|
||||
self.inner.set_linger(Some(Duration::ZERO))
|
||||
}
|
||||
|
||||
/// Reads the linger duration for this socket by getting the `SO_LINGER`
|
||||
/// option.
|
||||
///
|
||||
/// For more information about this option, see [`set_linger`].
|
||||
/// For more information about this option, see [`set_zero_linger`] and [`set_linger`].
|
||||
///
|
||||
/// [`set_linger`]: TcpSocket::set_linger
|
||||
/// [`set_zero_linger`]: TcpSocket::set_zero_linger
|
||||
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
||||
self.inner.linger()
|
||||
}
|
||||
|
||||
@@ -1256,9 +1256,10 @@ impl TcpStream {
|
||||
/// Reads the linger duration for this socket by getting the `SO_LINGER`
|
||||
/// option.
|
||||
///
|
||||
/// For more information about this option, see [`set_linger`].
|
||||
/// For more information about this option, see [`set_zero_linger`] and [`set_linger`].
|
||||
///
|
||||
/// [`set_linger`]: TcpStream::set_linger
|
||||
/// [`set_zero_linger`]: TcpStream::set_zero_linger
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1295,6 +1296,11 @@ impl TcpStream {
|
||||
/// >
|
||||
/// > From [The ultimate `SO_LINGER` page, or: why is my tcp not reliable](https://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable)
|
||||
///
|
||||
/// Although this method is deprecated, it will not be removed from Tokio.
|
||||
///
|
||||
/// Note that the special case of setting `SO_LINGER` to zero does not lead to blocking.
|
||||
/// Tokio provides [`set_zero_linger`](Self::set_zero_linger) for this purpose.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -1312,6 +1318,39 @@ impl TcpStream {
|
||||
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
socket2::SockRef::from(self).set_linger(dur)
|
||||
}
|
||||
|
||||
/// Sets a linger duration of zero on this socket by setting the `SO_LINGER` option.
|
||||
///
|
||||
/// This causes the connection to be forcefully aborted ("abortive close") when the socket
|
||||
/// is dropped or closed. Instead of the normal TCP shutdown handshake (`FIN`/`ACK`), a TCP
|
||||
/// `RST` (reset) segment is sent to the peer, and the socket immediately discards any
|
||||
/// unsent data residing in the socket send buffer. This prevents the socket from entering
|
||||
/// the `TIME_WAIT` state after closing it.
|
||||
///
|
||||
/// This is a destructive action. Any data currently buffered by the OS but not yet
|
||||
/// transmitted will be lost. The peer will likely receive a "Connection Reset" error
|
||||
/// rather than a clean end-of-stream.
|
||||
///
|
||||
/// See the documentation for [`set_linger`](Self::set_linger) for additional details on
|
||||
/// how `SO_LINGER` works.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::time::Duration;
|
||||
/// use tokio::net::TcpStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
///
|
||||
/// stream.set_zero_linger()?;
|
||||
/// assert_eq!(stream.linger()?, Some(Duration::ZERO));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_zero_linger(&self) -> io::Result<()> {
|
||||
socket2::SockRef::from(self).set_linger(Some(Duration::ZERO))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_TTL` option for this socket.
|
||||
|
||||
@@ -181,7 +181,6 @@ mod linux {
|
||||
use std::{net, thread};
|
||||
|
||||
#[tokio::test]
|
||||
#[expect(deprecated)] // set_linger is deprecated
|
||||
fn poll_hup() {
|
||||
let addr = assert_ok!("127.0.0.1:0".parse());
|
||||
let mut srv = assert_ok!(TcpListener::bind(&addr));
|
||||
@@ -189,7 +188,7 @@ mod linux {
|
||||
|
||||
tokio::spawn(async move {
|
||||
let (mut client, _) = assert_ok!(srv.accept().await);
|
||||
assert_ok!(client.set_linger(Some(Duration::from_millis(0))));
|
||||
assert_ok!(client.set_zero_linger());
|
||||
assert_ok!(client.write_all(b"hello world").await);
|
||||
|
||||
// TODO: Drop?
|
||||
@@ -198,7 +197,7 @@ mod linux {
|
||||
/*
|
||||
let t = thread::spawn(move || {
|
||||
let mut client = assert_ok!(srv.accept()).0;
|
||||
client.set_linger(Some(Duration::from_millis(0))).unwrap();
|
||||
client.set_zero_linger().unwrap();
|
||||
client.write(b"hello world").unwrap();
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#![cfg(all(feature = "full", not(target_os = "wasi"), not(miri)))] // Wasi doesn't support bind
|
||||
// No `socket` on miri.
|
||||
|
||||
use std::time::Duration;
|
||||
use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::oneshot::channel;
|
||||
@@ -33,7 +32,6 @@ async fn shutdown() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[expect(deprecated)] // set_linger is deprecated
|
||||
async fn shutdown_after_tcp_reset() {
|
||||
let srv = assert_ok!(TcpListener::bind("127.0.0.1:0").await);
|
||||
let addr = assert_ok!(srv.local_addr());
|
||||
@@ -51,7 +49,7 @@ async fn shutdown_after_tcp_reset() {
|
||||
|
||||
let (stream, _) = assert_ok!(srv.accept().await);
|
||||
// By setting linger to 0 we will trigger a TCP reset
|
||||
stream.set_linger(Some(Duration::new(0, 0))).unwrap();
|
||||
stream.set_zero_linger().unwrap();
|
||||
connected_rx.await.unwrap();
|
||||
|
||||
drop(stream);
|
||||
|
||||
@@ -62,7 +62,6 @@ async fn bind_before_connect() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[expect(deprecated)] // set_linger is deprecated
|
||||
async fn basic_linger() {
|
||||
// Create server
|
||||
let addr = assert_ok!("127.0.0.1:0".parse());
|
||||
@@ -71,7 +70,7 @@ async fn basic_linger() {
|
||||
|
||||
assert!(srv.linger().unwrap().is_none());
|
||||
|
||||
srv.set_linger(Some(Duration::new(0, 0))).unwrap();
|
||||
srv.set_zero_linger().unwrap();
|
||||
assert_eq!(srv.linger().unwrap(), Some(Duration::new(0, 0)));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ async fn set_linger() {
|
||||
assert_ok!(stream.set_linger(Some(Duration::from_secs(1))));
|
||||
assert_eq!(stream.linger().unwrap().unwrap().as_secs(), 1);
|
||||
|
||||
assert_ok!(stream.set_zero_linger());
|
||||
assert_eq!(stream.linger().unwrap().unwrap().as_secs(), 0);
|
||||
|
||||
assert_ok!(stream.set_linger(None));
|
||||
assert!(stream.linger().unwrap().is_none());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user