Repository: tokio-rs/mio
Pull request #1358 (2020-10-02 23:05:58)

Add `TcpSocket`, a basic TCP socket builder
@Thomasdezeeuw I'm posting this now for early feedback. The work is not done.

This provides `TcpSocket`, a basic API for building a TCP socket. The goal is not to provide comprehensive coverage of all system options, but to provide an API for the most common cases.

This is added now as a replacement for the removal of `TcpStream::connect_std`. The `connect_std` function from v0.6 was used until now as the strategy to set socket option before obtaining a mio TcpStream.

Providing some strategy for customizing a `TcpStream` is required for Hyper to be able to upgrade.

The `TcpSocket` API for Tokio is provided [here](https://github.com/tokio-rs/tokio/issues/2902). There has been some discussion in Discord related to how to handle configuring a TCP socket.

I know there is some discussion about using `socket2` for advanced socket configuration. I don't believe `TcpSocket` and `socket2` are mutually exclusive. `TcpSocket` provides the ability to configure a socket while maintaining mio invariants (non-blocking).

carllerche: @Thomasdezeeuw This is ready to go!

Patch:
diff --git a/src/net/mod.rs b/src/net/mod.rs
index c3ecabd..91804ec 100644
--- a/src/net/mod.rs
+++ b/src/net/mod.rs
@@ -9,7 +9,7 @@
 
 cfg_tcp! {
     mod tcp;
-    pub use self::tcp::{TcpListener, TcpStream};
+    pub use self::tcp::{TcpListener, TcpSocket, TcpStream};
 }
 
 cfg_udp! {
diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs
index ba7bdbd..b6f5736 100644
--- a/src/net/tcp/listener.rs
+++ b/src/net/tcp/listener.rs
@@ -5,7 +5,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
 use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
 use std::{fmt, io};
 
-use super::TcpStream;
+use super::{TcpSocket, TcpStream};
 use crate::io_source::IoSource;
 use crate::{event, sys, Interest, Registry, Token};
 
@@ -49,7 +49,20 @@ impl TcpListener {
     /// 3. Bind the socket to the specified address.
     /// 4. Calls `listen` on the socket to prepare it to receive new connections.
     pub fn bind(addr: SocketAddr) -> io::Result<TcpListener> {
-        sys::tcp::bind(addr).map(TcpListener::from_std)
+        let socket = TcpSocket::new_for_addr(addr)?;
+
+        // On platforms with Berkeley-derived sockets, this allows to quickly
+        // rebind a socket, without needing to wait for the OS to clean up the
+        // previous one.
+        //
+        // On Windows, this allows rebinding sockets which are actively in use,
+        // which allows “socket hijacking”, so we explicitly don't set it here.
+        // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
+        #[cfg(not(windows))]
+        socket.set_reuseaddr(true)?;
+
+        socket.bind(addr)?;
+        socket.listen(1024)
     }
 
     /// Creates a new `TcpListener` from a standard `net::TcpListener`.
diff --git a/src/net/tcp/mod.rs b/src/net/tcp/mod.rs
index 94af5c1..b39b909 100644
--- a/src/net/tcp/mod.rs
+++ b/src/net/tcp/mod.rs
@@ -1,5 +1,8 @@
 mod listener;
 pub use self::listener::TcpListener;
 
+mod socket;
+pub use self::socket::TcpSocket;
+
 mod stream;
 pub use self::stream::TcpStream;
diff --git a/src/net/tcp/socket.rs b/src/net/tcp/socket.rs
new file mode 100644
index 0000000..e86dc9a
--- /dev/null
+++ b/src/net/tcp/socket.rs
@@ -0,0 +1,107 @@
+use crate::net::{TcpStream, TcpListener};
+use crate::sys;
+
+use std::io;
+use std::mem;
+use std::net::SocketAddr;
+#[cfg(unix)]
+use std::os::unix::io::{AsRawFd, RawFd, FromRawFd};
+
+/// A non-blocking TCP socket used to configure a stream or listener.
+///
+/// The `TcpSocket` type wraps the operating-system's socket handle. This type
+/// is used to configure the socket before establishing a connection or start
+/// listening for inbound connections.
+///
+/// The socket will be closed when the value is dropped.
+#[derive(Debug)]
+pub struct TcpSocket {
+    sys: sys::tcp::TcpSocket,
+}
+
+impl TcpSocket {
+    /// Create a new IPv4 TCP socket.
+    ///
+    /// This calls `socket(2)`.
+    pub fn new_v4() -> io::Result<TcpSocket> {
+        sys::tcp::new_v4_socket().map(|sys| TcpSocket {
+            sys
+        })
+    }
+
+    /// Create a new IPv6 TCP socket.
+    ///
+    /// This calls `socket(2)`.
+    pub fn new_v6() -> io::Result<TcpSocket> {
+        sys::tcp::new_v6_socket().map(|sys| TcpSocket {
+            sys
+        })
+    }
+
+    pub(crate) fn new_for_addr(addr: SocketAddr) -> io::Result<TcpSocket> {
+        if addr.is_ipv4() {
+            TcpSocket::new_v4()
+        } else {
+            TcpSocket::new_v6()
+        }
+    }
+
+    /// Bind `addr` to the TCP socket.
+    pub fn bind(&self, addr: SocketAddr) -> io::Result<()> {
+        sys::tcp::bind(self.sys, addr)
+    }
+
+    /// Connect the socket to `addr`.
+    ///
+    /// This consumes the socket and performs the connect operation. Once the
+    /// connection completes, the socket is now a non-blocking `TcpStream` and
+    /// can be used as such.
+    pub fn connect(self, addr: SocketAddr) -> io::Result<TcpStream> {
+        let stream = sys::tcp::connect(self.sys, addr)?;
+
+        // Don't close the socket
+        mem::forget(self);
+        Ok(TcpStream::from_std(stream))
+    }
+
+    /// Listen for inbound connections, converting the socket to a
+    /// `TcpListener`.
+    pub fn listen(self, backlog: u32) -> io::Result<TcpListener> {
+        let listener = sys::tcp::listen(self.sys, backlog)?;
+
+        // Don't close the socket
+        mem::forget(self);
+        Ok(TcpListener::from_std(listener))
+    }
+
+    /// Sets the value of `SO_REUSEADDR` on this socket.
+    pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> {
+        sys::tcp::set_reuseaddr(self.sys, reuseaddr)
+    }
+}
+
+impl Drop for TcpSocket {
+    fn drop(&mut self) {
+        sys::tcp::close(self.sys);
+    }
+}
+
+#[cfg(unix)]
+impl AsRawFd for TcpSocket {
+    fn as_raw_fd(&self) -> RawFd {
+        self.sys
+    }
+}
+
+#[cfg(unix)]
+impl FromRawFd for TcpSocket {
+    /// Converts a `RawFd` to a `TcpStream`.
+    ///
+    /// # Notes
+    ///
+    /// The caller is responsible for ensuring that the socket is in
+    /// non-blocking mode.
+    unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket {
+        TcpSocket { sys: fd }
+    }
+}
\ No newline at end of file
diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs
index 00dd66f..86f674c 100644
--- a/src/net/tcp/stream.rs
+++ b/src/net/tcp/stream.rs
@@ -7,7 +7,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
 use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
 
 use crate::io_source::IoSource;
-use crate::{event, sys, Interest, Registry, Token};
+use crate::{event, Interest, Registry, Token};
+use crate::net::TcpSocket;
 
 /// A non-blocking TCP stream between a local socket and a remote socket.
 ///
@@ -48,7 +49,8 @@ impl TcpStream {
     /// Create a new TCP stream and issue a non-blocking connect to the
     /// specified address.
     pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
-        sys::tcp::connect(addr).map(TcpStream::from_std)
+        let socket = TcpSocket::new_for_addr(addr)?;
+        socket.connect(addr)
     }
 
     /// Creates a new `TcpStream` from a standard `net::TcpStream`.
diff --git a/src/sys/shell/tcp.rs b/src/sys/shell/tcp.rs
index f72dcca..963fccb 100644
--- a/src/sys/shell/tcp.rs
+++ b/src/sys/shell/tcp.rs
@@ -1,11 +1,33 @@
 use std::io;
 use std::net::{self, SocketAddr};
 
-pub fn connect(_: SocketAddr) -> io::Result<net::TcpStream> {
+pub(crate) type TcpSocket = i32;
+
+pub(crate) fn new_v4_socket() -> io::Result<TcpSocket> {
+    os_required!();
+}
+
+pub(crate) fn new_v6_socket() -> io::Result<TcpSocket> {
+    os_required!();
+}
+
+pub(crate) fn bind(_socket: TcpSocket, _addr: SocketAddr) -> io::Result<()> {
+    os_required!();
+}
+
+pub(crate) fn connect(_: TcpSocket, _addr: SocketAddr) -> io::Result<net::TcpStream> {
+    os_required!();
+}
+
+pub(crate) fn listen(_: TcpSocket, _: u32) -> io::Result<net::TcpListener> {
+    os_required!();
+}
+
+pub(crate) fn close(_: TcpSocket) {
     os_required!();
 }
 
-pub fn bind(_: SocketAddr) -> io::Result<net::TcpListener> {
+pub(crate) fn set_reuseaddr(_: TcpSocket, _: bool) -> io::Result<()> {
     os_required!();
 }
 
diff --git a/src/sys/unix/net.rs b/src/sys/unix/net.rs
index bd25ecd..2671b42 100644
--- a/src/sys/unix/net.rs
+++ b/src/sys/unix/net.rs
@@ -1,7 +1,7 @@
 #[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))]
 use std::net::SocketAddr;
 
-#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))]
+#[cfg(all(feature = "os-poll", any(feature = "udp")))]
 pub(crate) fn new_ip_socket(
     addr: SocketAddr,
     socket_type: libc::c_int,
diff --git a/src/sys/unix/tcp.rs b/src/sys/unix/tcp.rs
index 08a28c0..9d59632 100644
--- a/src/sys/unix/tcp.rs
+++ b/src/sys/unix/tcp.rs
@@ -3,52 +3,59 @@ use std::mem::{size_of, MaybeUninit};
 use std::net::{self, SocketAddr};
 use std::os::unix::io::{AsRawFd, FromRawFd};
 
-use crate::sys::unix::net::{new_ip_socket, socket_addr, to_socket_addr};
+use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr};
 
-pub fn connect(addr: SocketAddr) -> io::Result<net::TcpStream> {
-    new_ip_socket(addr, libc::SOCK_STREAM)
-        .and_then(|socket| {
-            let (raw_addr, raw_addr_length) = socket_addr(&addr);
-            syscall!(connect(socket, raw_addr, raw_addr_length))
-                .or_else(|err| match err {
-                    // Connect hasn't finished, but that is fine.
-                    ref err if err.raw_os_error() == Some(libc::EINPROGRESS) => Ok(0),
-                    err => Err(err),
-                })
-                .map(|_| socket)
-                .map_err(|err| {
-                    // Close the socket if we hit an error, ignoring the error
-                    // from closing since we can't pass back two errors.
-                    let _ = unsafe { libc::close(socket) };
-                    err
-                })
-        })
-        .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) })
+pub type TcpSocket = libc::c_int;
+
+pub(crate) fn new_v4_socket() -> io::Result<TcpSocket> {
+    new_socket(libc::AF_INET, libc::SOCK_STREAM)
 }
 
-pub fn bind(addr: SocketAddr) -> io::Result<net::TcpListener> {
-    new_ip_socket(addr, libc::SOCK_STREAM).and_then(|socket| {
-        // Set SO_REUSEADDR (mirrors what libstd does).
-        syscall!(setsockopt(
-            socket,
-            libc::SOL_SOCKET,
-            libc::SO_REUSEADDR,
-            &1 as *const libc::c_int as *const libc::c_void,
-            size_of::<libc::c_int>() as libc::socklen_t,
-        ))
-        .and_then(|_| {
-            let (raw_addr, raw_addr_length) = socket_addr(&addr);
-            syscall!(bind(socket, raw_addr, raw_addr_length))
-        })
-        .and_then(|_| syscall!(listen(socket, 1024)))
-        .map_err(|err| {
-            // Close the socket if we hit an error, ignoring the error
-            // from closing since we can't pass back two errors.
-            let _ = unsafe { libc::close(socket) };
-            err
-        })
-        .map(|_| unsafe { net::TcpListener::from_raw_fd(socket) })
-    })
+pub(crate) fn new_v6_socket() -> io::Result<TcpSocket> {
+    new_socket(libc::AF_INET6, libc::SOCK_STREAM)
+}
+
+pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> {
+    let (raw_addr, raw_addr_length) = socket_addr(&addr);
+    syscall!(bind(socket, raw_addr, raw_addr_length))?;
+    Ok(())
+}
+
+pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result<net::TcpStream> {
+    let (raw_addr, raw_addr_length) = socket_addr(&addr);
+
+    match syscall!(connect(socket, raw_addr, raw_addr_length)) {
+        Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => {
+            Err(err)
+        }
+        _ => {
+            Ok(unsafe { net::TcpStream::from_raw_fd(socket) })
+        }
+    }
+}
+
+pub(crate) fn listen(socket: TcpSocket, backlog: u32) -> io::Result<net::TcpListener> {
+    use std::convert::TryInto;
+
+    let backlog = backlog.try_into().unwrap_or(i32::max_value());
+    syscall!(listen(socket, backlog))?;
+    Ok(unsafe { net::TcpListener::from_raw_fd(socket) })
+}
+
+pub(crate) fn close(socket: TcpSocket) {
+    let _ = unsafe { net::TcpStream::from_raw_fd(socket) };
+}
+
+pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()> {
+    let val: libc::c_int = if reuseaddr { 1 } else { 0 };
+    syscall!(setsockopt(
+        socket,
+        libc::SOL_SOCKET,
+        libc::SO_REUSEADDR,
+        &val as *const libc::c_int as *const libc::c_void,
+        size_of::<libc::c_int>() as libc::socklen_t,
+    ))?;
+    Ok(())
 }
 
 pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
diff --git a/src/sys/windows/net.rs b/src/sys/windows/net.rs
index 7e55c06..f825ee3 100644
--- a/src/sys/windows/net.rs
+++ b/src/sys/windows/net.rs
@@ -1,14 +1,12 @@
 use std::io;
 use std::mem::size_of_val;
 use std::net::SocketAddr;
-#[cfg(all(feature = "os-poll", feature = "tcp"))]
-use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
 use std::sync::Once;
 
 use winapi::ctypes::c_int;
 use winapi::shared::ws2def::SOCKADDR;
 use winapi::um::winsock2::{
-    ioctlsocket, socket, FIONBIO, INVALID_SOCKET, PF_INET, PF_INET6, SOCKET,
+    ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET,
 };
 
 /// Initialise the network stack for Windows.
@@ -23,12 +21,19 @@ pub(crate) fn init() {
 }
 
 /// Create a new non-blocking socket.
-pub(crate) fn new_socket(addr: SocketAddr, socket_type: c_int) -> io::Result<SOCKET> {
+#[cfg(feature = "udp")]
+pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: c_int) -> io::Result<SOCKET> {
+    use winapi::um::winsock2::{PF_INET, PF_INET6};
+
     let domain = match addr {
         SocketAddr::V4(..) => PF_INET,
         SocketAddr::V6(..) => PF_INET6,
     };
 
+    new_socket(domain, socket_type)
+}
+
+pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result<SOCKET> {
     syscall!(
         socket(domain, socket_type, 0),
         PartialEq::eq,
@@ -51,19 +56,3 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const SOCKADDR, c_int) {
         ),
     }
 }
-
-#[cfg(all(feature = "os-poll", feature = "tcp"))]
-pub(crate) fn inaddr_any(other: SocketAddr) -> SocketAddr {
-    match other {
-        SocketAddr::V4(..) => {
-            let any = Ipv4Addr::new(0, 0, 0, 0);
-            let addr = SocketAddrV4::new(any, 0);
-            SocketAddr::V4(addr)
-        }
-        SocketAddr::V6(..) => {
-            let any = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
-            let addr = SocketAddrV6::new(any, 0, 0, 0);
-            SocketAddr::V6(addr)
-        }
-    }
-}
diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs
index 1c2aa3f..c5b0410 100644
--- a/src/sys/windows/tcp.rs
+++ b/src/sys/windows/tcp.rs
@@ -4,68 +4,92 @@ use std::os::windows::io::FromRawSocket;
 use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64.
 
 use winapi::um::winsock2::{
-    bind as win_bind, closesocket, connect as win_connect, listen, SOCKET_ERROR, SOCK_STREAM,
+    self,
+    closesocket, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, SOCK_STREAM,
 };
 
-use crate::sys::windows::net::{inaddr_any, init, new_socket, socket_addr};
+use crate::sys::windows::net::{init, new_socket, socket_addr};
 
-pub fn connect(addr: SocketAddr) -> io::Result<net::TcpStream> {
+pub(crate) type TcpSocket = SOCKET;
+
+pub(crate) fn new_v4_socket() -> io::Result<TcpSocket> {
     init();
-    new_socket(addr, SOCK_STREAM)
-        .and_then(|socket| {
-            // Required for a future `connect_overlapped` operation to be
-            // executed successfully.
-            let any_addr = inaddr_any(addr);
-            let (raw_addr, raw_addr_length) = socket_addr(&any_addr);
-            syscall!(
-                win_bind(socket, raw_addr, raw_addr_length),
-                PartialEq::eq,
-                SOCKET_ERROR
-            )
-            .and_then(|_| {
-                let (raw_addr, raw_addr_length) = socket_addr(&addr);
-                syscall!(
-                    win_connect(socket, raw_addr, raw_addr_length),
-                    PartialEq::eq,
-                    SOCKET_ERROR
-                )
-                .or_else(|err| match err {
-                    ref err if err.kind() == io::ErrorKind::WouldBlock => Ok(0),
-                    err => Err(err),
-                })
-            })
-            .map(|_| socket)
-            .map_err(|err| {
-                // Close the socket if we hit an error, ignoring the error
-                // from closing since we can't pass back two errors.
-                let _ = unsafe { closesocket(socket) };
-                err
-            })
-        })
-        .map(|socket| unsafe { net::TcpStream::from_raw_socket(socket as StdSocket) })
+    new_socket(PF_INET, SOCK_STREAM)
 }
 
-pub fn bind(addr: SocketAddr) -> io::Result<net::TcpListener> {
+pub(crate) fn new_v6_socket() -> io::Result<TcpSocket> {
     init();
-    new_socket(addr, SOCK_STREAM).and_then(|socket| {
-        let (raw_addr, raw_addr_length) = socket_addr(&addr);
-        syscall!(
-            win_bind(socket, raw_addr, raw_addr_length,),
-            PartialEq::eq,
-            SOCKET_ERROR
-        )
-        .and_then(|_| syscall!(listen(socket, 1024), PartialEq::eq, SOCKET_ERROR))
-        .map_err(|err| {
-            // Close the socket if we hit an error, ignoring the error
-            // from closing since we can't pass back two errors.
-            let _ = unsafe { closesocket(socket) };
-            err
-        })
-        .map(|_| unsafe { net::TcpListener::from_raw_socket(socket as StdSocket) })
-    })
+    new_socket(PF_INET6, SOCK_STREAM)
+}
+
+pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> {
+    use winsock2::bind;
+
+    let (raw_addr, raw_addr_length) = socket_addr(&addr);
+    syscall!(
+        bind(socket, raw_addr, raw_addr_length),
+        PartialEq::eq,
+        SOCKET_ERROR
+    )?;
+    Ok(())
+}
+
+pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result<net::TcpStream> {
+    use winsock2::connect;
+
+    let (raw_addr, raw_addr_length) = socket_addr(&addr);
+
+    let res = syscall!(
+        connect(socket, raw_addr, raw_addr_length),
+        PartialEq::eq,
+        SOCKET_ERROR
+    );
+
+    match res {
+        Err(err) if err.kind() != io::ErrorKind::WouldBlock => {
+            Err(err)
+        }
+        _ => {
+            Ok(unsafe { net::TcpStream::from_raw_socket(socket as StdSocket) })
+        }
+    }
+}
+
+pub(crate) fn listen(socket: TcpSocket, backlog: u32) -> io::Result<net::TcpListener> {
+    use winsock2::listen;
+    use std::convert::TryInto;
+
+    let backlog = backlog.try_into().unwrap_or(i32::max_value());
+    syscall!(listen(socket, backlog), PartialEq::eq, SOCKET_ERROR)?;
+    Ok(unsafe { net::TcpListener::from_raw_socket(socket as StdSocket) })
+}
+
+pub(crate) fn close(socket: TcpSocket) {
+    let _ = unsafe { closesocket(socket) };
+}
+
+pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()> {
+    use winapi::ctypes::{c_char, c_int};
+    use winapi::shared::minwindef::{BOOL, TRUE, FALSE};
+    use winapi::um::winsock2::{setsockopt, SOCKET_ERROR, SO_REUSEADDR, SOL_SOCKET};
+    use std::mem::size_of;
+
+    let val: BOOL = if reuseaddr { TRUE } else { FALSE };
+
+    match unsafe { setsockopt(
+        socket,
+        SOL_SOCKET,
+        SO_REUSEADDR,
+        &val as *const _ as *const c_char,
+        size_of::<BOOL>() as c_int,
+    ) } {
+        0 => Ok(()),
+        SOCKET_ERROR => Err(io::Error::last_os_error()),
+        _ => panic!("unexpected return value"),
+    }
 }
 
-pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
+pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
     // The non-blocking state of `listener` is inherited. See
     // https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept#remarks.
     listener.accept()
diff --git a/src/sys/windows/udp.rs b/src/sys/windows/udp.rs
index 0f83fd0..667c775 100644
--- a/src/sys/windows/udp.rs
+++ b/src/sys/windows/udp.rs
@@ -5,11 +5,11 @@ use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib use
 
 use winapi::um::winsock2::{bind as win_bind, closesocket, SOCKET_ERROR, SOCK_DGRAM};
 
-use crate::sys::windows::net::{init, new_socket, socket_addr};
+use crate::sys::windows::net::{init, new_ip_socket, socket_addr};
 
 pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> {
     init();
-    new_socket(addr, SOCK_DGRAM).and_then(|socket| {
+    new_ip_socket(addr, SOCK_DGRAM).and_then(|socket| {
         let (raw_addr, raw_addr_length) = socket_addr(&addr);
         syscall!(
             win_bind(socket, raw_addr, raw_addr_length,),


Test patch:
diff --git a/tests/tcp_socket.rs b/tests/tcp_socket.rs
new file mode 100644
index 0000000..e741db9
--- /dev/null
+++ b/tests/tcp_socket.rs
@@ -0,0 +1,23 @@
+#![cfg(all(feature = "os-poll", feature = "tcp"))]
+
+use mio::net::TcpSocket;
+
+#[test]
+fn is_send_and_sync() {
+    fn is_send<T: Send>() {}
+    fn is_sync<T: Sync>() {}
+
+    is_send::<TcpSocket>();
+    is_sync::<TcpSocket>();
+}
+
+#[test]
+fn set_reuseaddr() {
+    let addr = "127.0.0.1:0".parse().unwrap();
+
+    let socket = TcpSocket::new_v4().unwrap();
+    socket.set_reuseaddr(true).unwrap();
+    socket.bind(addr).unwrap();
+
+    let _ = socket.listen(128).unwrap();
+}