[System] Check SocketOptionName.ReuseAddress support.
[mono.git] / mcs / class / System / System.Net.Sockets / Socket.cs
index 53bd3f13b00d5e1a78baa13ba573944c9ef95352..98d5dc42d0a6bbe79e28a89e15b6c7add1a5ac54 100644 (file)
@@ -6,6 +6,7 @@
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //     Sridhar Kulkarni (sridharkulkarni@gmail.com)
 //     Brian Nickel (brian.nickel@gmail.com)
+//     Ludovic Henry (ludovic@xamarin.com)
 //
 // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc.
 //    http://www.myelin.co.nz
@@ -69,13 +70,13 @@ namespace System.Net.Sockets
 
                int linger_timeout;
 
-               /* the field "safe_handle" is looked up by name by the runtime */
-               SafeSocketHandle safe_handle;
-
                AddressFamily address_family;
                SocketType socket_type;
                ProtocolType protocol_type;
 
+               /* the field "safe_handle" is looked up by name by the runtime */
+               internal SafeSocketHandle safe_handle;
+
                /*
                 * This EndPoint is used when creating new endpoints. Because
                 * there are many types of EndPoints possible,
@@ -85,8 +86,8 @@ namespace System.Net.Sockets
                 */
                internal EndPoint seed_endpoint = null;
 
-               internal Queue readQ = new Queue (2);
-               internal Queue writeQ = new Queue (2);
+               internal Queue<KeyValuePair<IntPtr, IOSelectorJob>> readQ = new Queue<KeyValuePair<IntPtr, IOSelectorJob>> (2);
+               internal Queue<KeyValuePair<IntPtr, IOSelectorJob>> writeQ = new Queue<KeyValuePair<IntPtr, IOSelectorJob>> (2);
 
                internal bool is_blocking = true;
                internal bool is_bound;
@@ -155,10 +156,16 @@ namespace System.Net.Sockets
                        }
                }
 
-               [MonoTODO ("Currently hardcoded to IPv4. Ideally, support v4/v6 dual-stack.")]
+               //
+               // This constructor is used by servers that want to listen for instance on both
+               // ipv4 and ipv6.   Mono has historically done that if you use InterNetworkV6 (at
+               // least on Unix), because that is the default behavior unless the IPV6_V6ONLY
+               // option is explicitly set by using setsockopt (sock, IPPROTO_IPV6, IPV6_ONLY)
+               //
                public Socket (SocketType socketType, ProtocolType protocolType)
-                       : this (AddressFamily.InterNetwork, socketType, protocolType)
+                       : this (AddressFamily.InterNetworkV6, socketType, protocolType)
                {
+                       DualMode = true;
                }
                
                public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
@@ -194,11 +201,9 @@ namespace System.Net.Sockets
                        this.address_family = addressFamily;
                        this.socket_type = socketType;
                        this.protocol_type = protocolType;
-                       
-                       int error;
-                       var handle = Socket_internal (addressFamily, socketType, protocolType, out error);
 
-                       this.safe_handle = new SafeSocketHandle (handle, true);
+                       int error;
+                       this.safe_handle = new SafeSocketHandle (Socket_internal (addressFamily, socketType, protocolType, out error), true);
 
                        if (error != 0)
                                throw new SocketException (error);
@@ -274,6 +279,7 @@ namespace System.Net.Sockets
 
 #region Properties
 
+               [ObsoleteAttribute ("Use OSSupportsIPv4 instead")]
                public static bool SupportsIPv4 {
                        get { return ipv4_supported == 1; }
                }
@@ -287,6 +293,19 @@ namespace System.Net.Sockets
                public static bool OSSupportsIPv4 {
                        get { return ipv4_supported == 1; }
                }
+#else
+               public static bool OSSupportsIPv4 {
+                       get {
+                               NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
+
+                               foreach (NetworkInterface adapter in nics) {
+                                       if (adapter.Supports (NetworkInterfaceComponent.IPv4))
+                                               return true;
+                               }
+
+                               return false;
+                       }
+               }
 #endif
 
 #if NET_2_1
@@ -465,6 +484,27 @@ namespace System.Net.Sockets
                        }
                }
 
+               public bool DualMode {
+                       get {
+                               if (AddressFamily != AddressFamily.InterNetworkV6) 
+                                       throw new NotSupportedException("This protocol version is not supported");
+
+                               return ((int)GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only) == 0);
+                       }
+                       set {
+                               if (AddressFamily != AddressFamily.InterNetworkV6) 
+                                       throw new NotSupportedException("This protocol version is not supported");
+
+                               SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, value ? 0 : 1);
+                       }
+               }
+
+               private bool IsDualMode {
+                       get {
+                               return AddressFamily == AddressFamily.InterNetworkV6 && DualMode;
+                       }
+               }
+
                [MonoTODO ("This doesn't do anything on Mono yet")]
                public bool UseOnlyOverlappedIO {
                        get { return use_overlapped_io; }
@@ -799,6 +839,48 @@ namespace System.Net.Sockets
 
 #endregion
 
+#region Poll
+
+               public bool Poll (int time_us, SelectMode mode)
+               {
+                       ThrowIfDisposedAndClosed ();
+
+                       if (mode != SelectMode.SelectRead && mode != SelectMode.SelectWrite && mode != SelectMode.SelectError)
+                               throw new NotSupportedException ("'mode' parameter is not valid.");
+
+                       int error;
+                       bool result = Poll_internal (safe_handle, mode, time_us, out error);
+
+                       if (error != 0)
+                               throw new SocketException (error);
+
+                       if (mode == SelectMode.SelectWrite && result && !is_connected) {
+                               /* Update the is_connected state; for non-blocking Connect()
+                                * this is when we can find out that the connect succeeded. */
+                               if ((int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error) == 0)
+                                       is_connected = true;
+                       }
+
+                       return result;
+               }
+
+               static bool Poll_internal (SafeSocketHandle safeHandle, SelectMode mode, int timeout, out int error)
+               {
+                       bool release = false;
+                       try {
+                               safeHandle.DangerousAddRef (ref release);
+                               return Poll_internal (safeHandle.DangerousGetHandle (), mode, timeout, out error);
+                       } finally {
+                               if (release)
+                                       safeHandle.DangerousRelease ();
+                       }
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error);
+
+#endregion
+
 #region Accept
 
                public Socket Accept()
@@ -867,23 +949,32 @@ namespace System.Net.Sockets
                                        throw new InvalidOperationException ("AcceptSocket: The socket must not be bound or connected.");
                        }
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, SocketOperation.Accept);
-
-                       SocketAsyncResult sockares = e.Worker.result;
-
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (e.Worker);
-                               count = readQ.Count;
-                       }
+                       InitSocketAsyncEventArgs (e, AcceptAsyncCallback, e, SocketOperation.Accept);
 
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, e.socket_async_result));
 
                        return true;
                }
 
+               static AsyncCallback AcceptAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.AcceptSocket = e.current_socket.EndAccept (ares);
+                       } catch (SocketException ex) {
+                               e.SocketError = ex.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               if (e.AcceptSocket == null)
+                                       e.AcceptSocket = new Socket (e.current_socket.AddressFamily, e.current_socket.SocketType, e.current_socket.ProtocolType, null);
+                               e.Complete ();
+                       }
+               });
+
                public IAsyncResult BeginAccept(AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
@@ -891,20 +982,27 @@ namespace System.Net.Sockets
                        if (!is_bound || !is_listening)
                                throw new InvalidOperationException ();
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.Accept);
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Accept);
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (sockares.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, sockares));
 
                        return sockares;
                }
 
+               static IOAsyncCallback BeginAcceptCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+                       Socket socket = null;
+
+                       try {
+                               socket = sockares.socket.Accept ();
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete (socket);
+               });
+
                public IAsyncResult BeginAccept (int receiveSize, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
@@ -912,21 +1010,14 @@ namespace System.Net.Sockets
                        if (receiveSize < 0)
                                throw new ArgumentOutOfRangeException ("receiveSize", "receiveSize is less than zero");
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.AcceptReceive) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.AcceptReceive) {
                                Buffer = new byte [receiveSize],
                                Offset = 0,
                                Size = receiveSize,
                                SockFlags = SocketFlags.None,
                        };
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (sockares.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
 
                        return sockares;
                }
@@ -952,8 +1043,8 @@ namespace System.Net.Sockets
                                if (acceptSocket.ProtocolType != ProtocolType.Tcp)
                                        throw new SocketException ((int)SocketError.InvalidArgument);
                        }
-                       
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.AcceptReceive) {
+
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.AcceptReceive) {
                                Buffer = new byte [receiveSize],
                                Offset = 0,
                                Size = receiveSize,
@@ -961,18 +1052,46 @@ namespace System.Net.Sockets
                                AcceptSocket = acceptSocket,
                        };
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (sockares.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
 
                        return sockares;
                }
 
+               static IOAsyncCallback BeginAcceptReceiveCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+                       Socket acc_socket = null;
+
+                       try {
+                               if (sockares.AcceptSocket == null) {
+                                       acc_socket = sockares.socket.Accept ();
+                               } else {
+                                       acc_socket = sockares.AcceptSocket;
+                                       sockares.socket.Accept (acc_socket);
+                               }
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       /* It seems the MS runtime special-cases 0-length requested receive data.  See bug 464201. */
+                       int total = 0;
+                       if (sockares.Size > 0) {
+                               try {
+                                       SocketError error;
+                                       total = acc_socket.Receive_nochecks (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out error);
+                                       if (error != 0) {
+                                               sockares.Complete (new SocketException ((int) error));
+                                               return;
+                                       }
+                               } catch (Exception e) {
+                                       sockares.Complete (e);
+                                       return;
+                               }
+                       }
+
+                       sockares.Complete (acc_socket, total);
+               });
+
                public Socket EndAccept (IAsyncResult result)
                {
                        int bytes;
@@ -1000,7 +1119,7 @@ namespace System.Net.Sockets
                        buffer = sockares.Buffer;
                        bytesTransferred = sockares.Total;
 
-                       return sockares.Socket;
+                       return sockares.AcceptedSocket;
                }
 
                static SafeSocketHandle Accept_internal (SafeSocketHandle safeHandle, out int error, bool blocking)
@@ -1028,7 +1147,12 @@ namespace System.Net.Sockets
 
                        if (local_end == null)
                                throw new ArgumentNullException("local_end");
-
+                               
+                       var ipEndPoint = local_end as IPEndPoint;
+                       if (ipEndPoint != null) {
+                               local_end = RemapIPEndPoint (ipEndPoint);       
+                       }
+                       
                        int error;
                        Bind_internal (safe_handle, local_end.Serialize(), out error);
 
@@ -1058,6 +1182,41 @@ namespace System.Net.Sockets
 
 #endregion
 
+#region Listen
+
+               public void Listen (int backlog)
+               {
+                       ThrowIfDisposedAndClosed ();
+
+                       if (!is_bound)
+                               throw new SocketException ((int) SocketError.InvalidArgument);
+
+                       int error;
+                       Listen_internal(safe_handle, backlog, out error);
+
+                       if (error != 0)
+                               throw new SocketException (error);
+
+                       is_listening = true;
+               }
+
+               static void Listen_internal (SafeSocketHandle safeHandle, int backlog, out int error)
+               {
+                       bool release = false;
+                       try {
+                               safeHandle.DangerousAddRef (ref release);
+                               Listen_internal (safeHandle.DangerousGetHandle (), backlog, out error);
+                       } finally {
+                               if (release)
+                                       safeHandle.DangerousRelease ();
+                       }
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static void Listen_internal (IntPtr sock, int backlog, out int error);
+
+#endregion
+
 #region Connect
 
                public void Connect (IPAddress address, int port)
@@ -1085,6 +1244,8 @@ namespace System.Net.Sockets
                        int error = 0;
                        foreach (IPAddress address in addresses) {
                                IPEndPoint iep = new IPEndPoint (address, port);
+                               
+                               iep = RemapIPEndPoint (iep);
 
                                Connect_internal (safe_handle, iep.Serialize (), out error);
                                if (error == 0) {
@@ -1129,6 +1290,10 @@ namespace System.Net.Sockets
 
                        if (is_listening)
                                throw new InvalidOperationException ();
+                               
+                       if (ep != null) {
+                               remoteEP = RemapIPEndPoint (ep);
+                       }
 
                        SocketAddress serial = remoteEP.Serialize ();
 
@@ -1159,37 +1324,51 @@ namespace System.Net.Sockets
                        if (e.RemoteEndPoint == null)
                                throw new ArgumentNullException ("remoteEP");
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, SocketOperation.Connect);
-
-                       SocketAsyncResult result = e.Worker.result;
+                       InitSocketAsyncEventArgs (e, ConnectAsyncCallback, e, SocketOperation.Connect);
 
                        try {
                                IPAddress [] addresses;
-                               IAsyncResult ares;
+                               SocketAsyncResult ares;
 
                                if (!GetCheckedIPs (e, out addresses)) {
-                                       result.EndPoint = e.RemoteEndPoint;
-                                       ares = BeginConnect (e.RemoteEndPoint, SocketAsyncEventArgs.Dispatcher, e);
+                                       e.socket_async_result.EndPoint = e.RemoteEndPoint;
+                                       ares = (SocketAsyncResult) BeginConnect (e.RemoteEndPoint, ConnectAsyncCallback, e);
                                } else {
                                        DnsEndPoint dep = (e.RemoteEndPoint as DnsEndPoint);
-                                       result.Addresses = addresses;
-                                       result.Port = dep.Port;
-                                       ares = BeginConnect (addresses, dep.Port, SocketAsyncEventArgs.Dispatcher, e);
+                                       e.socket_async_result.Addresses = addresses;
+                                       e.socket_async_result.Port = dep.Port;
+                                       ares = (SocketAsyncResult) BeginConnect (addresses, dep.Port, ConnectAsyncCallback, e);
                                }
 
                                if (ares.IsCompleted && ares.CompletedSynchronously) {
-                                       ((SocketAsyncResult) ares).CheckIfThrowDelayedException ();
+                                       ares.CheckIfThrowDelayedException ();
                                        return false;
                                }
                        } catch (Exception exc) {
-                               result.Complete (exc, true);
+                               e.socket_async_result.Complete (exc, true);
                                return false;
                        }
 
                        return true;
                }
 
+               static AsyncCallback ConnectAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.current_socket.EndConnect (ares);
+                       } catch (SocketException se) {
+                               e.SocketError = se.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               e.Complete ();
+                       }
+               });
+
                public IAsyncResult BeginConnect (IPAddress address, int port, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
@@ -1229,7 +1408,7 @@ namespace System.Net.Sockets
                        if (end_point == null)
                                throw new ArgumentNullException ("end_point");
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.Connect) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
                                EndPoint = end_point,
                        };
 
@@ -1240,6 +1419,8 @@ namespace System.Net.Sockets
                                        sockares.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
                                        return sockares;
                                }
+                               
+                               end_point = RemapIPEndPoint (ep);
                        }
 
                        int error = 0;
@@ -1250,8 +1431,7 @@ namespace System.Net.Sockets
                                // an error. Better to just close the socket and move on.
                                connect_in_progress = false;
                                safe_handle.Dispose ();
-                               var handle = Socket_internal (address_family, socket_type, protocol_type, out error);
-                               safe_handle = new SafeSocketHandle (handle, true);
+                               safe_handle = new SafeSocketHandle (Socket_internal (address_family, socket_type, protocol_type, out error), true);
                                if (error != 0)
                                        throw new SocketException (error);
                        }
@@ -1284,7 +1464,7 @@ namespace System.Net.Sockets
                        is_bound = false;
                        connect_in_progress = true;
 
-                       socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
 
                        return sockares;
                }
@@ -1304,7 +1484,7 @@ namespace System.Net.Sockets
                        if (is_listening)
                                throw new InvalidOperationException ();
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.Connect) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
                                Addresses = addresses,
                                Port = port,
                        };
@@ -1316,17 +1496,21 @@ namespace System.Net.Sockets
 
                internal IAsyncResult BeginMConnect (SocketAsyncResult sockares)
                {
-                       IAsyncResult ares = null;
+                       SocketAsyncResult ares = null;
                        Exception exc = null;
+                       AsyncCallback callback;
 
                        for (int i = sockares.CurrentAddress; i < sockares.Addresses.Length; i++) {
                                try {
                                        sockares.CurrentAddress++;
 
-                                       ares = BeginConnect (new IPEndPoint (sockares.Addresses [i], sockares.Port), null, sockares);
+                                       ares = (SocketAsyncResult) BeginConnect (new IPEndPoint (sockares.Addresses [i], sockares.Port), null, sockares);
                                        if (ares.IsCompleted && ares.CompletedSynchronously) {
-                                               ((SocketAsyncResult) ares).CheckIfThrowDelayedException ();
-                                               sockares.DoMConnectCallback ();
+                                               ares.CheckIfThrowDelayedException ();
+
+                                               callback = ares.AsyncCallback;
+                                               if (callback != null)
+                                                       ThreadPool.UnsafeQueueUserWorkItem (_ => callback (ares), null);
                                        }
 
                                        break;
@@ -1342,6 +1526,57 @@ namespace System.Net.Sockets
                        return sockares;
                }
 
+               static IOAsyncCallback BeginConnectCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+
+                       if (sockares.EndPoint == null) {
+                               sockares.Complete (new SocketException ((int)SocketError.AddressNotAvailable));
+                               return;
+                       }
+
+                       SocketAsyncResult mconnect = sockares.AsyncState as SocketAsyncResult;
+                       bool is_mconnect = mconnect != null && mconnect.Addresses != null;
+
+                       try {
+                               EndPoint ep = sockares.EndPoint;
+                               int error_code = (int) sockares.socket.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
+
+                               if (error_code == 0) {
+                                       if (is_mconnect)
+                                               sockares = mconnect;
+
+                                       sockares.socket.seed_endpoint = ep;
+                                       sockares.socket.is_connected = true;
+                                       sockares.socket.is_bound = true;
+                                       sockares.socket.connect_in_progress = false;
+                                       sockares.error = 0;
+                                       sockares.Complete ();
+                                       return;
+                               }
+
+                               if (!is_mconnect) {
+                                       sockares.socket.connect_in_progress = false;
+                                       sockares.Complete (new SocketException (error_code));
+                                       return;
+                               }
+
+                               if (mconnect.CurrentAddress >= mconnect.Addresses.Length) {
+                                       mconnect.Complete (new SocketException (error_code));
+                                       return;
+                               }
+
+                               mconnect.socket.BeginMConnect (mconnect);
+                       } catch (Exception e) {
+                               sockares.socket.connect_in_progress = false;
+
+                               if (is_mconnect)
+                                       sockares = mconnect;
+
+                               sockares.Complete (e);
+                               return;
+                       }
+               });
+
                public void EndConnect (IAsyncResult result)
                {
                        ThrowIfDisposedAndClosed ();
@@ -1422,30 +1657,56 @@ namespace System.Net.Sockets
 
                        ThrowIfDisposedAndClosed ();
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, SocketOperation.Disconnect);
-
-                       SocketAsyncResult sockares = e.Worker.result;
+                       InitSocketAsyncEventArgs (e, DisconnectAsyncCallback, e, SocketOperation.Disconnect);
 
-                       socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       IOSelector.Add (e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, e.socket_async_result));
 
                        return true;
                }
 
+               static AsyncCallback DisconnectAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.current_socket.EndDisconnect (ares);
+                       } catch (SocketException ex) {
+                               e.SocketError = ex.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               e.Complete ();
+                       }
+               });
 
                public IAsyncResult BeginDisconnect (bool reuseSocket, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.Disconnect) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Disconnect) {
                                ReuseSocket = reuseSocket,
                        };
 
-                       socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, sockares));
 
                        return sockares;
                }
 
+               static IOAsyncCallback BeginDisconnectCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+
+                       try {
+                               sockares.socket.Disconnect (sockares.ReuseSocket);
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete ();
+               });
+
                public void EndDisconnect (IAsyncResult asyncResult)
                {
                        ThrowIfDisposedAndClosed ();
@@ -1623,55 +1884,56 @@ namespace System.Net.Sockets
                        if (e.Buffer == null && e.BufferList == null)
                                throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, e.Buffer != null ? SocketOperation.Receive : SocketOperation.ReceiveGeneric);
+                       if (e.Buffer == null) {
+                               InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.ReceiveGeneric);
 
-                       SocketAsyncResult sockares = e.Worker.result;
-                       sockares.SockFlags = e.SocketFlags;
+                               e.socket_async_result.Buffers = e.BufferList;
 
-                       if (e.Buffer != null) {
-                               sockares.Buffer = e.Buffer;
-                               sockares.Offset = e.Offset;
-                               sockares.Size = e.Count;
+                               QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, e.socket_async_result));
                        } else {
-                               sockares.Buffers = e.BufferList;
-                       }
+                               InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.Receive);
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (e.Worker);
-                               count = readQ.Count;
-                       }
+                               e.socket_async_result.Buffer = e.Buffer;
+                               e.socket_async_result.Offset = e.Offset;
+                               e.socket_async_result.Size = e.Count;
 
-                       if (count == 1) {
-                               // Receive takes care of ReceiveGeneric
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                               QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, e.socket_async_result));
                        }
 
                        return true;
                }
 
+               static AsyncCallback ReceiveAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.BytesTransferred = e.current_socket.EndReceive (ares);
+                       } catch (SocketException se){
+                               e.SocketError = se.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               e.Complete ();
+                       }
+               });
+
                public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, SocketFlags socket_flags, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
                        ThrowIfBufferOutOfRange (buffer, offset, size);
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.Receive) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Receive) {
                                Buffer = buffer,
                                Offset = offset,
                                Size = size,
                                SockFlags = socket_flags,
                        };
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (sockares.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, sockares));
 
                        return sockares;
                }
@@ -1685,6 +1947,20 @@ namespace System.Net.Sockets
                        return BeginReceive (buffer, offset, size, flags, callback, state);
                }
 
+               static IOAsyncCallback BeginReceiveCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+                       int total = 0;
+
+                       try {
+                               total = Receive_internal (sockares.socket.safe_handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error);
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete (total);
+               });
+
                [CLSCompliant (false)]
                public IAsyncResult BeginReceive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, AsyncCallback callback, object state)
                {
@@ -1693,19 +1969,12 @@ namespace System.Net.Sockets
                        if (buffers == null)
                                throw new ArgumentNullException ("buffers");
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.ReceiveGeneric) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveGeneric) {
                                Buffers = buffers,
                                SockFlags = socketFlags,
                        };
 
-                       int count;
-                       lock(readQ) {
-                               readQ.Enqueue (sockares.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, sockares));
 
                        return sockares;
                }
@@ -1718,6 +1987,20 @@ namespace System.Net.Sockets
                        return BeginReceive (buffers, socketFlags, callback, state);
                }
 
+               static IOAsyncCallback BeginReceiveGenericCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+                       int total = 0;
+
+                       try {
+                               total = sockares.socket.Receive (sockares.Buffers, sockares.SockFlags);
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete (total);
+               });
+
                public int EndReceive (IAsyncResult result)
                {
                        SocketError error;
@@ -1749,7 +2032,7 @@ namespace System.Net.Sockets
                        return sockares.Total;
                }
 
-               internal int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
+               int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
                {
                        int nativeError;
                        int ret = Receive_internal (safe_handle, buf, offset, size, flags, out nativeError);
@@ -1799,24 +2082,16 @@ namespace System.Net.Sockets
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
-                       ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
-
-                       if (remoteEP == null)
-                               throw new ArgumentNullException ("remoteEP");
 
-                       return ReceiveFrom_nochecks (buffer, 0, buffer.Length, SocketFlags.None, ref remoteEP);
+                       return ReceiveFrom (buffer, 0, buffer.Length, SocketFlags.None, ref remoteEP);
                }
 
                public int ReceiveFrom (byte [] buffer, SocketFlags flags, ref EndPoint remoteEP)
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
-                       ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
-
-                       if (remoteEP == null)
-                               throw new ArgumentNullException ("remoteEP");
 
-                       return ReceiveFrom_nochecks (buffer, 0, buffer.Length, flags, ref remoteEP);
+                       return ReceiveFrom (buffer, 0, buffer.Length, flags, ref remoteEP);
                }
 
                public int ReceiveFrom (byte [] buffer, int size, SocketFlags flags, ref EndPoint remoteEP)
@@ -1825,10 +2100,7 @@ namespace System.Net.Sockets
                        ThrowIfBufferNull (buffer);
                        ThrowIfBufferOutOfRange (buffer, 0, size);
 
-                       if (remoteEP == null)
-                               throw new ArgumentNullException ("remoteEP");
-
-                       return ReceiveFrom_nochecks (buffer, 0, size, flags, ref remoteEP);
+                       return ReceiveFrom (buffer, 0, size, flags, ref remoteEP);
                }
 
                public int ReceiveFrom (byte [] buffer, int offset, int size, SocketFlags flags, ref EndPoint remoteEP)
@@ -1840,7 +2112,8 @@ namespace System.Net.Sockets
                        if (remoteEP == null)
                                throw new ArgumentNullException ("remoteEP");
 
-                       return ReceiveFrom_nochecks (buffer, offset, size, flags, ref remoteEP);
+                       int error;
+                       return ReceiveFrom_nochecks_exc (buffer, offset, size, flags, ref remoteEP, true, out error);
                }
 
                public bool ReceiveFromAsync (SocketAsyncEventArgs e)
@@ -1853,28 +2126,36 @@ namespace System.Net.Sockets
                        if (e.RemoteEndPoint == null)
                                throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, SocketOperation.ReceiveFrom);
+                       InitSocketAsyncEventArgs (e, ReceiveFromAsyncCallback, e, SocketOperation.ReceiveFrom);
 
-                       SocketAsyncResult sockares = e.Worker.result;
-                       sockares.Buffer = e.Buffer;
-                       sockares.Offset = e.Offset;
-                       sockares.Size = e.Count;
-                       sockares.EndPoint = e.RemoteEndPoint;
-                       sockares.SockFlags = e.SocketFlags;
+                       e.socket_async_result.Buffer = e.Buffer;
+                       e.socket_async_result.Offset = e.Offset;
+                       e.socket_async_result.Size = e.Count;
+                       e.socket_async_result.EndPoint = e.RemoteEndPoint;
+                       e.socket_async_result.SockFlags = e.SocketFlags;
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (e.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, e.socket_async_result));
 
                        return true;
                }
 
+               static AsyncCallback ReceiveFromAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.BytesTransferred = e.current_socket.EndReceiveFrom (ares, ref e.remote_ep);
+                       } catch (SocketException ex) {
+                               e.SocketError = ex.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               e.Complete ();
+                       }
+               });
+
                public IAsyncResult BeginReceiveFrom (byte[] buffer, int offset, int size, SocketFlags socket_flags, ref EndPoint remote_end, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
@@ -1884,7 +2165,7 @@ namespace System.Net.Sockets
                        if (remote_end == null)
                                throw new ArgumentNullException ("remote_end");
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.ReceiveFrom) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveFrom) {
                                Buffer = buffer,
                                Offset = offset,
                                Size = size,
@@ -1892,18 +2173,26 @@ namespace System.Net.Sockets
                                EndPoint = remote_end,
                        };
 
-                       int count;
-                       lock (readQ) {
-                               readQ.Enqueue (sockares.Worker);
-                               count = readQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
 
                        return sockares;
                }
 
+               static IOAsyncCallback BeginReceiveFromCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+                       int total = 0;
+
+                       try {
+                               int error;
+                               total = sockares.socket.ReceiveFrom_nochecks_exc (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, ref sockares.EndPoint, true, out error);
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete (total);
+               });
+
                public int EndReceiveFrom(IAsyncResult result, ref EndPoint end_point)
                {
                        ThrowIfDisposedAndClosed ();
@@ -1923,12 +2212,6 @@ namespace System.Net.Sockets
                        return sockares.Total;
                }
 
-               internal int ReceiveFrom_nochecks (byte [] buf, int offset, int size, SocketFlags flags, ref EndPoint remote_end)
-               {
-                       int error;
-                       return ReceiveFrom_nochecks_exc (buf, offset, size, flags, ref remote_end, true, out error);
-               }
-
                internal int ReceiveFrom_nochecks_exc (byte [] buf, int offset, int size, SocketFlags flags, ref EndPoint remote_end, bool throwOnError, out int error)
                {
                        SocketAddress sockaddr = remote_end.Serialize();
@@ -2174,7 +2457,7 @@ namespace System.Net.Sockets
                        return ret;
                }
 
-               internal int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
+               int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
                {
                        if (size == 0) {
                                error = SocketError.Success;
@@ -2205,34 +2488,42 @@ namespace System.Net.Sockets
                        if (e.Buffer == null && e.BufferList == null)
                                throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, e.Buffer != null ? SocketOperation.Send : SocketOperation.SendGeneric);
+                       if (e.Buffer == null) {
+                               InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.SendGeneric);
 
-                       SocketAsyncResult sockares = e.Worker.result;
-                       sockares.SockFlags = e.SocketFlags;
+                               e.socket_async_result.Buffers = e.BufferList;
 
-                       if (e.Buffer != null) {
-                               sockares.Buffer = e.Buffer;
-                               sockares.Offset = e.Offset;
-                               sockares.Size = e.Count;
+                               QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, e.socket_async_result));
                        } else {
-                               sockares.Buffers = e.BufferList;
-                       }
+                               InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.Send);
 
-                       int count;
-                       lock (writeQ) {
-                               writeQ.Enqueue (e.Worker);
-                               count = writeQ.Count;
-                       }
+                               e.socket_async_result.Buffer = e.Buffer;
+                               e.socket_async_result.Offset = e.Offset;
+                               e.socket_async_result.Size = e.Count;
 
-                       if (count == 1) {
-                               // Send takes care of SendGeneric
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                               QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
                        }
 
                        return true;
                }
 
+               static AsyncCallback SendAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.BytesTransferred = e.current_socket.EndSend (ares);
+                       } catch (SocketException se){
+                               e.SocketError = se.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               e.Complete ();
+                       }
+               });
+
                public IAsyncResult BeginSend (byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
                {
                        if (!is_connected) {
@@ -2253,23 +2544,48 @@ namespace System.Net.Sockets
                        if (!is_connected)
                                throw new SocketException ((int)SocketError.NotConnected);
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.Send) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Send) {
                                Buffer = buffer,
                                Offset = offset,
                                Size = size,
                                SockFlags = socket_flags,
                        };
 
-                       int count;
-                       lock (writeQ) {
-                               writeQ.Enqueue (sockares.Worker);
-                               count = writeQ.Count;
+                       QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), sockares));
+
+                       return sockares;
+               }
+
+               static void BeginSendCallback (SocketAsyncResult sockares, int sent_so_far)
+               {
+                       int total = 0;
+
+                       try {
+                               total = Socket.Send_internal (sockares.socket.safe_handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error);
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
                        }
 
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       if (sockares.error == 0) {
+                               sent_so_far += total;
+                               sockares.Offset += total;
+                               sockares.Size -= total;
 
-                       return sockares;
+                               if (sockares.socket.is_disposed) {
+                                       sockares.Complete (total);
+                                       return;
+                               }
+
+                               if (sockares.Size > 0) {
+                                       IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
+                                       return; // Have to finish writing everything. See bug #74475.
+                               }
+
+                               sockares.Total = sent_so_far;
+                       }
+
+                       sockares.Complete (total);
                }
 
                public IAsyncResult BeginSend (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, AsyncCallback callback, object state)
@@ -2281,19 +2597,12 @@ namespace System.Net.Sockets
                        if (!is_connected)
                                throw new SocketException ((int)SocketError.NotConnected);
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.SendGeneric) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendGeneric) {
                                Buffers = buffers,
                                SockFlags = socketFlags,
                        };
 
-                       int count;
-                       lock (writeQ) {
-                               writeQ.Enqueue (sockares.Worker);
-                               count = writeQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, sockares));
 
                        return sockares;
                }
@@ -2310,6 +2619,20 @@ namespace System.Net.Sockets
                        return BeginSend (buffers, socketFlags, callback, state);
                }
 
+               static IOAsyncCallback BeginSendGenericCallback = new IOAsyncCallback (ares => {
+                       SocketAsyncResult sockares = (SocketAsyncResult) ares;
+                       int total = 0;
+
+                       try {
+                               total = sockares.socket.Send (sockares.Buffers, sockares.SockFlags);
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete (total);
+               });
+
                public int EndSend (IAsyncResult result)
                {
                        SocketError error;
@@ -2377,36 +2700,21 @@ namespace System.Net.Sockets
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
-                       ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
-
-                       if (remote_end == null)
-                               throw new ArgumentNullException ("remote_end");
 
-                       return SendTo_nochecks (buffer, 0, buffer.Length, SocketFlags.None, remote_end);
+                       return SendTo (buffer, 0, buffer.Length, SocketFlags.None, remote_end);
                }
 
                public int SendTo (byte [] buffer, SocketFlags flags, EndPoint remote_end)
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
-                       ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
-
-                       if (remote_end == null)
-                               throw new ArgumentNullException ("remote_end");
 
-                       return SendTo_nochecks (buffer, 0, buffer.Length, flags, remote_end);
+                       return SendTo (buffer, 0, buffer.Length, flags, remote_end);
                }
 
                public int SendTo (byte [] buffer, int size, SocketFlags flags, EndPoint remote_end)
                {
-                       ThrowIfDisposedAndClosed ();
-                       ThrowIfBufferNull (buffer);
-                       ThrowIfBufferOutOfRange (buffer, 0, size);
-
-                       if (remote_end == null)
-                               throw new ArgumentNullException ("remote_end");
-
-                       return SendTo_nochecks (buffer, 0, size, flags, remote_end);
+                       return SendTo (buffer, 0, size, flags, remote_end);
                }
 
                public int SendTo (byte [] buffer, int offset, int size, SocketFlags flags, EndPoint remote_end)
@@ -2421,25 +2729,6 @@ namespace System.Net.Sockets
                        return SendTo_nochecks (buffer, offset, size, flags, remote_end);
                }
 
-               internal int SendTo_nochecks (byte [] buffer, int offset, int size, SocketFlags flags, EndPoint remote_end)
-               {
-                       int error;
-                       int ret = SendTo_internal (safe_handle, buffer, offset, size, flags, remote_end.Serialize (), out error);
-
-                       SocketError err = (SocketError) error;
-                       if (err != 0) {
-                               if (err != SocketError.WouldBlock && err != SocketError.InProgress)
-                                       is_connected = false;
-                               throw new SocketException (error);
-                       }
-
-                       is_connected = true;
-                       is_bound = true;
-                       seed_endpoint = remote_end;
-
-                       return ret;
-               }
-
                public bool SendToAsync (SocketAsyncEventArgs e)
                {
                        // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
@@ -2451,28 +2740,35 @@ namespace System.Net.Sockets
                        if (e.RemoteEndPoint == null)
                                throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
 
-                       e.curSocket = this;
-                       e.Worker.Init (this, e, SocketOperation.SendTo);
-
-                       SocketAsyncResult sockares = e.Worker.result;
-                       sockares.Buffer = e.Buffer;
-                       sockares.Offset = e.Offset;
-                       sockares.Size = e.Count;
-                       sockares.SockFlags = e.SocketFlags;
-                       sockares.EndPoint = e.RemoteEndPoint;
+                       InitSocketAsyncEventArgs (e, SendToAsyncCallback, e, SocketOperation.SendTo);
 
-                       int count;
-                       lock (writeQ) {
-                               writeQ.Enqueue (e.Worker);
-                               count = writeQ.Count;
-                       }
+                       e.socket_async_result.Buffer = e.Buffer;
+                       e.socket_async_result.Offset = e.Offset;
+                       e.socket_async_result.Size = e.Count;
+                       e.socket_async_result.SockFlags = e.SocketFlags;
+                       e.socket_async_result.EndPoint = e.RemoteEndPoint;
 
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
 
                        return true;
                }
 
+               static AsyncCallback SendToAsyncCallback = new AsyncCallback (ares => {
+                       SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
+
+                       if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+
+                       try {
+                               e.BytesTransferred = e.current_socket.EndSendTo (ares);
+                       } catch (SocketException ex) {
+                               e.SocketError = ex.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               e.SocketError = SocketError.OperationAborted;
+                       } finally {
+                               e.Complete ();
+                       }
+               });
 
                public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socket_flags, EndPoint remote_end, AsyncCallback callback, object state)
                {
@@ -2480,7 +2776,7 @@ namespace System.Net.Sockets
                        ThrowIfBufferNull (buffer);
                        ThrowIfBufferOutOfRange (buffer, offset, size);
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, state, callback, SocketOperation.SendTo) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendTo) {
                                Buffer = buffer,
                                Offset = offset,
                                Size = size,
@@ -2488,18 +2784,37 @@ namespace System.Net.Sockets
                                EndPoint = remote_end,
                        };
 
-                       int count;
-                       lock (writeQ) {
-                               writeQ.Enqueue (sockares.Worker);
-                               count = writeQ.Count;
-                       }
-
-                       if (count == 1)
-                               socket_pool_queue (SocketAsyncWorker.Dispatcher, sockares);
+                       QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
 
                        return sockares;
                }
 
+               static void BeginSendToCallback (SocketAsyncResult sockares, int sent_so_far)
+               {
+                       int total = 0;
+                       try {
+                               total = sockares.socket.SendTo_nochecks (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, sockares.EndPoint);
+
+                               if (sockares.error == 0) {
+                                       sent_so_far += total;
+                                       sockares.Offset += total;
+                                       sockares.Size -= total;
+                               }
+
+                               if (sockares.Size > 0) {
+                                       IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, sent_so_far), sockares));
+                                       return; // Have to finish writing everything. See bug #74475.
+                               }
+
+                               sockares.Total = sent_so_far;
+                       } catch (Exception e) {
+                               sockares.Complete (e);
+                               return;
+                       }
+
+                       sockares.Complete ();
+               }
+
                public int EndSendTo (IAsyncResult result)
                {
                        ThrowIfDisposedAndClosed ();
@@ -2514,6 +2829,25 @@ namespace System.Net.Sockets
                        return sockares.Total;
                }
 
+               int SendTo_nochecks (byte [] buffer, int offset, int size, SocketFlags flags, EndPoint remote_end)
+               {
+                       int error;
+                       int ret = SendTo_internal (safe_handle, buffer, offset, size, flags, remote_end.Serialize (), out error);
+
+                       SocketError err = (SocketError) error;
+                       if (err != 0) {
+                               if (err != SocketError.WouldBlock && err != SocketError.InProgress)
+                                       is_connected = false;
+                               throw new SocketException (error);
+                       }
+
+                       is_connected = true;
+                       is_bound = true;
+                       seed_endpoint = remote_end;
+
+                       return ret;
+               }
+
                static int SendTo_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error)
                {
                        try {
@@ -2652,18 +2986,22 @@ namespace System.Net.Sockets
 
 #endregion
 
-               void CheckRange (byte[] buffer, int offset, int size)
+#region SendPackets
+
+               [MonoTODO ("Not implemented")]
+               public bool SendPacketsAsync (SocketAsyncEventArgs e)
                {
-                       if (offset < 0)
-                               throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
-                       if (offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset", "offset must be <= buffer.Length");
-                       if (size < 0)
-                               throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
-                       if (size > buffer.Length - offset)
-                               throw new ArgumentOutOfRangeException ("size", "size must be <= buffer.Length - offset");
+                       // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
+
+                       ThrowIfDisposedAndClosed ();
+
+                       throw new NotImplementedException ();
                }
 
+#endregion
+
+#region DuplicateAndClose
+
 #if !MOBILE
                [MonoLimitation ("We do not support passing sockets across processes, we merely allow this API to pass the socket across AppDomains")]
                public SocketInformation DuplicateAndClose (int targetProcessId)
@@ -2682,191 +3020,103 @@ namespace System.Net.Sockets
                }
 #endif
 
-               [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void GetSocketOption_arr_internal(IntPtr socket,
-                       SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val,
-                       out int error);
+#endregion
 
-               private static void GetSocketOption_arr_internal (SafeSocketHandle safeHandle,
-                       SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val,
-                       out int error)
-               {
-                       bool release = false;
-                       try {
-                               safeHandle.DangerousAddRef (ref release);
-                               GetSocketOption_arr_internal (safeHandle.DangerousGetHandle (), level, name, ref byte_val, out error);
-                       } finally {
-                               if (release)
-                                       safeHandle.DangerousRelease ();
-                       }
-               }
+#region GetSocketOption
 
                public void GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
                {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
+                       ThrowIfDisposedAndClosed ();
 
                        if (optionValue == null)
-                               throw new SocketException ((int) SocketError.Fault,
-                                       "Error trying to dereference an invalid pointer");
+                               throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
 
                        int error;
+                       GetSocketOption_arr_internal (safe_handle, optionLevel, optionName, ref optionValue, out error);
 
-                       GetSocketOption_arr_internal (safe_handle, optionLevel, optionName, ref optionValue,
-                               out error);
                        if (error != 0)
                                throw new SocketException (error);
                }
 
                public byte [] GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int length)
                {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
+                       ThrowIfDisposedAndClosed ();
 
-                       byte[] byte_val=new byte[length];
                        int error;
+                       byte[] byte_val = new byte [length];
+                       GetSocketOption_arr_internal (safe_handle, optionLevel, optionName, ref byte_val, out error);
 
-                       GetSocketOption_arr_internal (safe_handle, optionLevel, optionName, ref byte_val,
-                               out error);
                        if (error != 0)
                                throw new SocketException (error);
 
-                       return(byte_val);
-               }
-
-               // See Socket.IOControl, WSAIoctl documentation in MSDN. The
-               // common options between UNIX and Winsock are FIONREAD,
-               // FIONBIO and SIOCATMARK. Anything else will depend on the
-               // system except SIO_KEEPALIVE_VALS which is properly handled
-               // on both windows and linux.
-               [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static int WSAIoctl (IntPtr sock, int ioctl_code, byte [] input,
-                       byte [] output, out int error);
-
-               private static int WSAIoctl (SafeSocketHandle safeHandle, int ioctl_code, byte [] input,
-                       byte [] output, out int error)
-               {
-                       bool release = false;
-                       try {
-                               safeHandle.DangerousAddRef (ref release);
-                               return WSAIoctl (safeHandle.DangerousGetHandle (), ioctl_code, input, output, out error);
-                       } finally {
-                               if (release)
-                                       safeHandle.DangerousRelease ();
-                       }
+                       return byte_val;
                }
 
-               public int IOControl (int ioctl_code, byte [] in_value, byte [] out_value)
+               public object GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName)
                {
-                       if (is_disposed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
+                       ThrowIfDisposedAndClosed ();
 
                        int error;
-                       int result = WSAIoctl (safe_handle, ioctl_code, in_value, out_value,
-                               out error);
+                       object obj_val;
+                       GetSocketOption_obj_internal (safe_handle, optionLevel, optionName, out obj_val, out error);
 
                        if (error != 0)
                                throw new SocketException (error);
-                       
-                       if (result == -1)
-                               throw new InvalidOperationException ("Must use Blocking property instead.");
 
-                       return result;
+                       if (optionName == SocketOptionName.Linger)
+                               return (LingerOption) obj_val;
+                       else if (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)
+                               return (MulticastOption) obj_val;
+                       else if (obj_val is int)
+                               return (int) obj_val;
+                       else
+                               return obj_val;
                }
 
-               public int IOControl (IOControlCode ioControlCode, byte[] optionInValue, byte[] optionOutValue)
+               static void GetSocketOption_arr_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error)
                {
-                       return IOControl ((int) ioControlCode, optionInValue, optionOutValue);
+                       bool release = false;
+                       try {
+                               safeHandle.DangerousAddRef (ref release);
+                               GetSocketOption_arr_internal (safeHandle.DangerousGetHandle (), level, name, ref byte_val, out error);
+                       } finally {
+                               if (release)
+                                       safeHandle.DangerousRelease ();
+                       }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void Listen_internal(IntPtr sock, int backlog, out int error);
+               extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error);
 
-               private static void Listen_internal (SafeSocketHandle safeHandle, int backlog, out int error)
+               static void GetSocketOption_obj_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error)
                {
                        bool release = false;
                        try {
                                safeHandle.DangerousAddRef (ref release);
-                               Listen_internal (safeHandle.DangerousGetHandle (), backlog, out error);
+                               GetSocketOption_obj_internal (safeHandle.DangerousGetHandle (), level, name, out obj_val, out error);
                        } finally {
                                if (release)
                                        safeHandle.DangerousRelease ();
                        }
                }
 
-               public void Listen (int backlog)
-               {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-
-                       if (!is_bound)
-                               throw new SocketException ((int)SocketError.InvalidArgument);
-
-                       int error;
-                       Listen_internal(safe_handle, backlog, out error);
-
-                       if (error != 0)
-                               throw new SocketException (error);
-
-                       is_listening = true;
-               }
-
-               public bool Poll (int time_us, SelectMode mode)
-               {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-
-                       if (mode != SelectMode.SelectRead &&
-                           mode != SelectMode.SelectWrite &&
-                           mode != SelectMode.SelectError)
-                               throw new NotSupportedException ("'mode' parameter is not valid.");
-
-                       int error;
-                       bool result = Poll_internal (safe_handle, mode, time_us, out error);
-                       if (error != 0)
-                               throw new SocketException (error);
-
-                       if (mode == SelectMode.SelectWrite && result && !is_connected) {
-                               /* Update the is_connected state; for
-                                * non-blocking Connect()s this is
-                                * when we can find out that the
-                                * connect succeeded.
-                                */
-                               if ((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error) == 0) {
-                                       is_connected = true;
-                               }
-                       }
-                       
-                       return result;
-               }
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error);
 
-               
+#endregion
 
-               [MonoTODO ("Not implemented")]
-               public bool SendPacketsAsync (SocketAsyncEventArgs e)
-               {
-                       // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
-                       
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-                       
-                       throw new NotImplementedException ();
-               }
+#region SetSocketOption
 
                public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
                {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
+                       ThrowIfDisposedAndClosed ();
 
                        // I'd throw an ArgumentNullException, but this is what MS does.
                        if (optionValue == null)
-                               throw new SocketException ((int) SocketError.Fault,
-                                       "Error trying to dereference an invalid pointer");
-                       
-                       int error;
+                               throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
 
-                       SetSocketOption_internal (safe_handle, optionLevel, optionName, null,
-                                                optionValue, 0, out error);
+                       int error;
+                       SetSocketOption_internal (safe_handle, optionLevel, optionName, null, optionValue, 0, out error);
 
                        if (error != 0) {
                                if (error == (int) SocketError.InvalidArgument)
@@ -2877,13 +3127,12 @@ namespace System.Net.Sockets
 
                public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue)
                {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
+                       ThrowIfDisposedAndClosed ();
 
                        // NOTE: if a null is passed, the byte[] overload is used instead...
                        if (optionValue == null)
                                throw new ArgumentNullException("optionValue");
-                       
+
                        int error;
 
                        if (optionLevel == SocketOptionLevel.Socket && optionName == SocketOptionName.Linger) {
@@ -2914,12 +3163,21 @@ namespace System.Net.Sockets
 
                public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
                {
-                       if (is_disposed && is_closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
+                       int int_val = optionValue ? 1 : 0;
+
+                       SetSocketOption (optionLevel, optionName, int_val);
+               }
+
+               public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
+               {
+                       ThrowIfDisposedAndClosed ();
+
+                       if (optionName == SocketOptionName.ReuseAddress && optionValue != 0 && !SupportsPortReuse ())
+                               throw new SocketException ((int) SocketError.OperationNotSupported, "Operating system sockets do not support ReuseAddress.\nIf your socket is not intended to bind to the same address and port multiple times remove this option, otherwise you should ignore this exception inside a try catch and check that ReuseAddress is true before binding to the same address and port multiple times.");
 
                        int error;
-                       int int_val = (optionValue) ? 1 : 0;
-                       SetSocketOption_internal (safe_handle, optionLevel, optionName, null, null, int_val, out error);
+                       SetSocketOption_internal (safe_handle, optionLevel, optionName, null, null, optionValue, out error);
+
                        if (error != 0) {
                                if (error == (int) SocketError.InvalidArgument)
                                        throw new ArgumentException ();
@@ -2927,6 +3185,178 @@ namespace System.Net.Sockets
                        }
                }
 
+               static void SetSocketOption_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error)
+               {
+                       bool release = false;
+                       try {
+                               safeHandle.DangerousAddRef (ref release);
+                               SetSocketOption_internal (safeHandle.DangerousGetHandle (), level, name, obj_val, byte_val, int_val, out error);
+                       } finally {
+                               if (release)
+                                       safeHandle.DangerousRelease ();
+                       }
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error);
+
+#endregion
+
+#region IOControl
+
+               public int IOControl (int ioctl_code, byte [] in_value, byte [] out_value)
+               {
+                       if (is_disposed)
+                               throw new ObjectDisposedException (GetType ().ToString ());
+
+                       int error;
+                       int result = IOControl_internal (safe_handle, ioctl_code, in_value, out_value, out error);
+
+                       if (error != 0)
+                               throw new SocketException (error);
+                       if (result == -1)
+                               throw new InvalidOperationException ("Must use Blocking property instead.");
+
+                       return result;
+               }
+
+               public int IOControl (IOControlCode ioControlCode, byte[] optionInValue, byte[] optionOutValue)
+               {
+                       return IOControl ((int) ioControlCode, optionInValue, optionOutValue);
+               }
+
+               static int IOControl_internal (SafeSocketHandle safeHandle, int ioctl_code, byte [] input, byte [] output, out int error)
+               {
+                       bool release = false;
+                       try {
+                               safeHandle.DangerousAddRef (ref release);
+                               return IOControl_internal (safeHandle.DangerousGetHandle (), ioctl_code, input, output, out error);
+                       } finally {
+                               if (release)
+                                       safeHandle.DangerousRelease ();
+                       }
+               }
+
+               /* See Socket.IOControl, WSAIoctl documentation in MSDN. The common options between UNIX
+                * and Winsock are FIONREAD, FIONBIO and SIOCATMARK. Anything else will depend on the system
+                * except SIO_KEEPALIVE_VALS which is properly handled on both windows and linux. */
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static int IOControl_internal (IntPtr sock, int ioctl_code, byte [] input, byte [] output, out int error);
+
+#endregion
+
+#region Close
+
+               public void Close ()
+               {
+                       linger_timeout = 0;
+                       Dispose ();
+               }
+
+               public void Close (int timeout)
+               {
+                       linger_timeout = timeout;
+                       Dispose ();
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               internal extern static void Close_internal (IntPtr socket, out int error);
+
+#endregion
+
+#region Shutdown
+
+               public void Shutdown (SocketShutdown how)
+               {
+                       ThrowIfDisposedAndClosed ();
+
+                       if (!is_connected)
+                               throw new SocketException (10057); // Not connected
+
+                       int error;
+                       Shutdown_internal (safe_handle, how, out error);
+
+                       if (error != 0)
+                               throw new SocketException (error);
+               }
+
+               static void Shutdown_internal (SafeSocketHandle safeHandle, SocketShutdown how, out int error)
+               {
+                       bool release = false;
+                       try {
+                               safeHandle.DangerousAddRef (ref release);
+                               Shutdown_internal (safeHandle.DangerousGetHandle (), how, out error);
+                       } finally {
+                               if (release)
+                                       safeHandle.DangerousRelease ();
+                       }
+               }
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
+
+#endregion
+
+#region Dispose
+
+               protected virtual void Dispose (bool disposing)
+               {
+                       if (is_disposed)
+                               return;
+
+                       is_disposed = true;
+                       bool was_connected = is_connected;
+                       is_connected = false;
+
+                       if (safe_handle != null) {
+                               is_closed = true;
+                               IntPtr x = Handle;
+
+                               if (was_connected)
+                                       Linger (x);
+
+                               safe_handle.Dispose ();
+                       }
+               }
+
+               public void Dispose ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+
+               void Linger (IntPtr handle)
+               {
+                       if (!is_connected || linger_timeout <= 0)
+                               return;
+
+                       /* We don't want to receive any more data */
+                       int error;
+                       Shutdown_internal (handle, SocketShutdown.Receive, out error);
+
+                       if (error != 0)
+                               return;
+
+                       int seconds = linger_timeout / 1000;
+                       int ms = linger_timeout % 1000;
+                       if (ms > 0) {
+                               /* If the other end closes, this will return 'true' with 'Available' == 0 */
+                               Poll_internal (handle, SelectMode.SelectRead, ms * 1000, out error);
+                               if (error != 0)
+                                       return;
+                       }
+
+                       if (seconds > 0) {
+                               LingerOption linger = new LingerOption (true, seconds);
+                               SetSocketOption_internal (handle, SocketOptionLevel.Socket, SocketOptionName.Linger, linger, null, 0, out error);
+                               /* Not needed, we're closing upon return */
+                               //if (error != 0)
+                               //      return;
+                       }
+               }
+
+#endregion
+
                void ThrowIfDisposedAndClosed (Socket socket)
                {
                        if (socket.is_disposed && socket.is_closed)
@@ -2974,10 +3404,76 @@ namespace System.Net.Sockets
                        if (sockares == null)
                                throw new ArgumentException ("Invalid IAsyncResult", argName);
                        if (Interlocked.CompareExchange (ref sockares.EndCalled, 1, 0) == 1)
-                               throw InvalidAsyncOp (methodName);
+                               throw new InvalidOperationException (methodName + " can only be called once per asynchronous operation");
 
                        return sockares;
                }
+
+               void QueueIOSelectorJob (Queue<KeyValuePair<IntPtr, IOSelectorJob>> queue, IntPtr handle, IOSelectorJob job)
+               {
+                       int count;
+                       lock (queue) {
+                               queue.Enqueue (new KeyValuePair<IntPtr, IOSelectorJob> (handle, job));
+                               count = queue.Count;
+                       }
+
+                       if (count == 1)
+                               IOSelector.Add (handle, job);
+               }
+
+               void InitSocketAsyncEventArgs (SocketAsyncEventArgs e, AsyncCallback callback, object state, SocketOperation operation)
+               {
+                       e.socket_async_result.Init (this, callback, state, operation);
+
+                       e.current_socket = this;
+                       e.SetLastOperation (SocketOperationToSocketAsyncOperation (operation));
+                       e.SocketError = SocketError.Success;
+                       e.BytesTransferred = 0;
+               }
+
+               SocketAsyncOperation SocketOperationToSocketAsyncOperation (SocketOperation op)
+               {
+                       switch (op) {
+                       case SocketOperation.Connect:
+                               return SocketAsyncOperation.Connect;
+                       case SocketOperation.Accept:
+                               return SocketAsyncOperation.Accept;
+                       case SocketOperation.Disconnect:
+                               return SocketAsyncOperation.Disconnect;
+                       case SocketOperation.Receive:
+                       case SocketOperation.ReceiveGeneric:
+                               return SocketAsyncOperation.Receive;
+                       case SocketOperation.ReceiveFrom:
+                               return SocketAsyncOperation.ReceiveFrom;
+                       case SocketOperation.Send:
+                       case SocketOperation.SendGeneric:
+                               return SocketAsyncOperation.Send;
+                       case SocketOperation.SendTo:
+                               return SocketAsyncOperation.SendTo;
+                       default:
+                               throw new NotImplementedException (String.Format ("Operation {0} is not implemented", op));
+                       }
+               }
+               
+               IPEndPoint RemapIPEndPoint (IPEndPoint input) {
+                       // If socket is DualMode ensure we automatically handle mapping IPv4 addresses to IPv6.
+                       if (IsDualMode && input.AddressFamily == AddressFamily.InterNetwork)
+                               return new IPEndPoint (input.Address.MapToIPv6 (), input.Port);
+                       
+                       return input;
+               }
+               
+               [StructLayout (LayoutKind.Sequential)]
+               struct WSABUF {
+                       public int len;
+                       public IntPtr buf;
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               internal static extern void cancel_blocking_socket_operation (Thread thread);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               internal static extern bool SupportsPortReuse ();
        }
 }