mio/net/tcp/
stream.rs

1use std::fmt;
2use std::io::{self, IoSlice, IoSliceMut, Read, Write};
3use std::net::{self, Shutdown, SocketAddr};
4#[cfg(any(unix, target_os = "wasi"))]
5use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
6// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
7// can use `std::os::fd` and be merged with the above.
8#[cfg(target_os = "hermit")]
9use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
10#[cfg(windows)]
11use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
12
13use crate::io_source::IoSource;
14#[cfg(not(target_os = "wasi"))]
15use crate::sys::tcp::{connect, new_for_addr};
16use crate::{event, Interest, Registry, Token};
17
18/// A non-blocking TCP stream between a local socket and a remote socket.
19///
20/// The socket will be closed when the value is dropped.
21///
22/// # Examples
23///
24#[cfg_attr(feature = "os-poll", doc = "```")]
25#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")]
26/// # use std::net::{TcpListener, SocketAddr};
27/// # use std::error::Error;
28/// #
29/// # fn main() -> Result<(), Box<dyn Error>> {
30/// let address: SocketAddr = "127.0.0.1:0".parse()?;
31/// let listener = TcpListener::bind(address)?;
32/// use mio::{Events, Interest, Poll, Token};
33/// use mio::net::TcpStream;
34/// use std::time::Duration;
35///
36/// let mut stream = TcpStream::connect(listener.local_addr()?)?;
37///
38/// let mut poll = Poll::new()?;
39/// let mut events = Events::with_capacity(128);
40///
41/// // Register the socket with `Poll`
42/// poll.registry().register(&mut stream, Token(0), Interest::WRITABLE)?;
43///
44/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
45///
46/// // The socket might be ready at this point
47/// #     Ok(())
48/// # }
49/// ```
50pub struct TcpStream {
51    inner: IoSource<net::TcpStream>,
52}
53
54impl TcpStream {
55    /// Create a new TCP stream and issue a non-blocking connect to the
56    /// specified address.
57    ///
58    /// # Notes
59    ///
60    /// The returned `TcpStream` may not be connected (and thus usable), unlike
61    /// the API found in `std::net::TcpStream`. Because Mio issues a
62    /// *non-blocking* connect it will not block the thread and instead return
63    /// an unconnected `TcpStream`.
64    ///
65    /// Ensuring the returned stream is connected is surprisingly complex when
66    /// considering cross-platform support. Doing this properly should follow
67    /// the steps below, an example implementation can be found
68    /// [here](https://github.com/Thomasdezeeuw/heph/blob/0c4f1ab3eaf08bea1d65776528bfd6114c9f8374/src/net/tcp/stream.rs#L560-L622).
69    ///
70    ///  1. Call `TcpStream::connect`
71    ///  2. Register the returned stream with at least [write interest].
72    ///  3. Wait for a (writable) event.
73    ///  4. Check `TcpStream::take_error`. If it returns an error, then
74    ///     something went wrong. If it returns `Ok(None)`, then proceed to
75    ///     step 5.
76    ///  5. Check `TcpStream::peer_addr`. If it returns `libc::EINPROGRESS` or
77    ///     `ErrorKind::NotConnected` it means the stream is not yet connected,
78    ///     go back to step 3. If it returns an address it means the stream is
79    ///     connected, go to step 6. If another error is returned something
80    ///     went wrong.
81    ///  6. Now the stream can be used.
82    ///
83    /// This may return a `WouldBlock` in which case the socket connection
84    /// cannot be completed immediately, it usually means there are insufficient
85    /// entries in the routing cache.
86    ///
87    /// [write interest]: Interest::WRITABLE
88    #[cfg(not(target_os = "wasi"))]
89    pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
90        let socket = new_for_addr(addr)?;
91        #[cfg(any(unix, target_os = "hermit"))]
92        let stream = unsafe { TcpStream::from_raw_fd(socket) };
93        #[cfg(windows)]
94        let stream = unsafe { TcpStream::from_raw_socket(socket as _) };
95        connect(&stream.inner, addr)?;
96        Ok(stream)
97    }
98
99    /// Creates a new `TcpStream` from a standard `net::TcpStream`.
100    ///
101    /// This function is intended to be used to wrap a TCP stream from the
102    /// standard library in the Mio equivalent. The conversion assumes nothing
103    /// about the underlying stream; it is left up to the user to set it in
104    /// non-blocking mode.
105    ///
106    /// # Note
107    ///
108    /// The TCP stream here will not have `connect` called on it, so it
109    /// should already be connected via some other means (be it manually, or
110    /// the standard library).
111    pub fn from_std(stream: net::TcpStream) -> TcpStream {
112        TcpStream {
113            inner: IoSource::new(stream),
114        }
115    }
116
117    /// Returns the socket address of the remote peer of this TCP connection.
118    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
119        self.inner.peer_addr()
120    }
121
122    /// Returns the socket address of the local half of this TCP connection.
123    pub fn local_addr(&self) -> io::Result<SocketAddr> {
124        self.inner.local_addr()
125    }
126
127    /// Shuts down the read, write, or both halves of this connection.
128    ///
129    /// This function will cause all pending and future I/O on the specified
130    /// portions to return immediately with an appropriate value (see the
131    /// documentation of `Shutdown`).
132    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
133        self.inner.shutdown(how)
134    }
135
136    /// Sets the value of the `TCP_NODELAY` option on this socket.
137    ///
138    /// If set, this option disables the Nagle algorithm. This means that
139    /// segments are always sent as soon as possible, even if there is only a
140    /// small amount of data. When not set, data is buffered until there is a
141    /// sufficient amount to send out, thereby avoiding the frequent sending of
142    /// small packets.
143    ///
144    /// # Notes
145    ///
146    /// On Windows make sure the stream is connected before calling this method,
147    /// by receiving an (writable) event. Trying to set `nodelay` on an
148    /// unconnected `TcpStream` is unspecified behavior.
149    pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
150        self.inner.set_nodelay(nodelay)
151    }
152
153    /// Gets the value of the `TCP_NODELAY` option on this socket.
154    ///
155    /// For more information about this option, see [`set_nodelay`][link].
156    ///
157    /// [link]: #method.set_nodelay
158    ///
159    /// # Notes
160    ///
161    /// On Windows make sure the stream is connected before calling this method,
162    /// by receiving an (writable) event. Trying to get `nodelay` on an
163    /// unconnected `TcpStream` is unspecified behavior.
164    pub fn nodelay(&self) -> io::Result<bool> {
165        self.inner.nodelay()
166    }
167
168    /// Sets the value for the `IP_TTL` option on this socket.
169    ///
170    /// This value sets the time-to-live field that is used in every packet sent
171    /// from this socket.
172    ///
173    /// # Notes
174    ///
175    /// On Windows make sure the stream is connected before calling this method,
176    /// by receiving an (writable) event. Trying to set `ttl` on an
177    /// unconnected `TcpStream` is unspecified behavior.
178    pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
179        self.inner.set_ttl(ttl)
180    }
181
182    /// Gets the value of the `IP_TTL` option for this socket.
183    ///
184    /// For more information about this option, see [`set_ttl`][link].
185    ///
186    /// # Notes
187    ///
188    /// On Windows make sure the stream is connected before calling this method,
189    /// by receiving an (writable) event. Trying to get `ttl` on an
190    /// unconnected `TcpStream` is unspecified behavior.
191    ///
192    /// [link]: #method.set_ttl
193    pub fn ttl(&self) -> io::Result<u32> {
194        self.inner.ttl()
195    }
196
197    /// Get the value of the `SO_ERROR` option on this socket.
198    ///
199    /// This will retrieve the stored error in the underlying socket, clearing
200    /// the field in the process. This can be useful for checking errors between
201    /// calls.
202    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
203        self.inner.take_error()
204    }
205
206    /// Receives data on the socket from the remote address to which it is
207    /// connected, without removing that data from the queue. On success,
208    /// returns the number of bytes peeked.
209    ///
210    /// Successive calls return the same data. This is accomplished by passing
211    /// `MSG_PEEK` as a flag to the underlying recv system call.
212    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
213        self.inner.peek(buf)
214    }
215
216    /// Execute an I/O operation ensuring that the socket receives more events
217    /// if it hits a [`WouldBlock`] error.
218    ///
219    /// # Notes
220    ///
221    /// This method is required to be called for **all** I/O operations to
222    /// ensure the user will receive events once the socket is ready again after
223    /// returning a [`WouldBlock`] error.
224    ///
225    /// [`WouldBlock`]: io::ErrorKind::WouldBlock
226    ///
227    /// # Examples
228    ///
229    #[cfg_attr(unix, doc = "```no_run")]
230    #[cfg_attr(windows, doc = "```ignore")]
231    /// # use std::error::Error;
232    /// #
233    /// # fn main() -> Result<(), Box<dyn Error>> {
234    /// use std::io;
235    /// #[cfg(any(unix, target_os = "wasi"))]
236    /// use std::os::fd::AsRawFd;
237    /// #[cfg(windows)]
238    /// use std::os::windows::io::AsRawSocket;
239    /// use mio::net::TcpStream;
240    ///
241    /// let address = "127.0.0.1:8080".parse().unwrap();
242    /// let stream = TcpStream::connect(address)?;
243    ///
244    /// // Wait until the stream is readable...
245    ///
246    /// // Read from the stream using a direct libc call, of course the
247    /// // `io::Read` implementation would be easier to use.
248    /// let mut buf = [0; 512];
249    /// let n = stream.try_io(|| {
250    ///     let buf_ptr = &mut buf as *mut _ as *mut _;
251    ///     #[cfg(unix)]
252    ///     let res = unsafe { libc::recv(stream.as_raw_fd(), buf_ptr, buf.len(), 0) };
253    ///     #[cfg(windows)]
254    ///     let res = unsafe { libc::recvfrom(stream.as_raw_socket() as usize, buf_ptr, buf.len() as i32, 0, std::ptr::null_mut(), std::ptr::null_mut()) };
255    ///     if res != -1 {
256    ///         Ok(res as usize)
257    ///     } else {
258    ///         // If EAGAIN or EWOULDBLOCK is set by libc::recv, the closure
259    ///         // should return `WouldBlock` error.
260    ///         Err(io::Error::last_os_error())
261    ///     }
262    /// })?;
263    /// eprintln!("read {} bytes", n);
264    /// # Ok(())
265    /// # }
266    /// ```
267    pub fn try_io<F, T>(&self, f: F) -> io::Result<T>
268    where
269        F: FnOnce() -> io::Result<T>,
270    {
271        self.inner.do_io(|_| f())
272    }
273}
274
275impl Read for TcpStream {
276    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
277        self.inner.do_io(|mut inner| inner.read(buf))
278    }
279
280    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
281        self.inner.do_io(|mut inner| inner.read_vectored(bufs))
282    }
283}
284
285impl<'a> Read for &'a TcpStream {
286    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
287        self.inner.do_io(|mut inner| inner.read(buf))
288    }
289
290    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
291        self.inner.do_io(|mut inner| inner.read_vectored(bufs))
292    }
293}
294
295impl Write for TcpStream {
296    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
297        self.inner.do_io(|mut inner| inner.write(buf))
298    }
299
300    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
301        self.inner.do_io(|mut inner| inner.write_vectored(bufs))
302    }
303
304    fn flush(&mut self) -> io::Result<()> {
305        self.inner.do_io(|mut inner| inner.flush())
306    }
307}
308
309impl<'a> Write for &'a TcpStream {
310    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
311        self.inner.do_io(|mut inner| inner.write(buf))
312    }
313
314    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
315        self.inner.do_io(|mut inner| inner.write_vectored(bufs))
316    }
317
318    fn flush(&mut self) -> io::Result<()> {
319        self.inner.do_io(|mut inner| inner.flush())
320    }
321}
322
323impl event::Source for TcpStream {
324    fn register(
325        &mut self,
326        registry: &Registry,
327        token: Token,
328        interests: Interest,
329    ) -> io::Result<()> {
330        self.inner.register(registry, token, interests)
331    }
332
333    fn reregister(
334        &mut self,
335        registry: &Registry,
336        token: Token,
337        interests: Interest,
338    ) -> io::Result<()> {
339        self.inner.reregister(registry, token, interests)
340    }
341
342    fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
343        self.inner.deregister(registry)
344    }
345}
346
347impl fmt::Debug for TcpStream {
348    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349        self.inner.fmt(f)
350    }
351}
352
353#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
354impl IntoRawFd for TcpStream {
355    fn into_raw_fd(self) -> RawFd {
356        self.inner.into_inner().into_raw_fd()
357    }
358}
359
360#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
361impl AsRawFd for TcpStream {
362    fn as_raw_fd(&self) -> RawFd {
363        self.inner.as_raw_fd()
364    }
365}
366
367#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
368impl FromRawFd for TcpStream {
369    /// Converts a `RawFd` to a `TcpStream`.
370    ///
371    /// # Notes
372    ///
373    /// The caller is responsible for ensuring that the socket is in
374    /// non-blocking mode.
375    unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
376        TcpStream::from_std(FromRawFd::from_raw_fd(fd))
377    }
378}
379
380#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
381impl AsFd for TcpStream {
382    fn as_fd(&self) -> BorrowedFd<'_> {
383        self.inner.as_fd()
384    }
385}
386
387#[cfg(windows)]
388impl IntoRawSocket for TcpStream {
389    fn into_raw_socket(self) -> RawSocket {
390        self.inner.into_inner().into_raw_socket()
391    }
392}
393
394#[cfg(windows)]
395impl AsRawSocket for TcpStream {
396    fn as_raw_socket(&self) -> RawSocket {
397        self.inner.as_raw_socket()
398    }
399}
400
401#[cfg(windows)]
402impl FromRawSocket for TcpStream {
403    /// Converts a `RawSocket` to a `TcpStream`.
404    ///
405    /// # Notes
406    ///
407    /// The caller is responsible for ensuring that the socket is in
408    /// non-blocking mode.
409    unsafe fn from_raw_socket(socket: RawSocket) -> TcpStream {
410        TcpStream::from_std(FromRawSocket::from_raw_socket(socket))
411    }
412}
413
414impl From<TcpStream> for net::TcpStream {
415    fn from(stream: TcpStream) -> Self {
416        // Safety: This is safe since we are extracting the raw fd from a well-constructed
417        // mio::net::TcpStream which ensures that we actually pass in a valid file
418        // descriptor/socket
419        unsafe {
420            #[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
421            {
422                net::TcpStream::from_raw_fd(stream.into_raw_fd())
423            }
424            #[cfg(windows)]
425            {
426                net::TcpStream::from_raw_socket(stream.into_raw_socket())
427            }
428        }
429    }
430}