Merge pull request #601 from knocte/sock_improvements
[mono.git] / mcs / class / System / System.Net.Sockets / Socket.cs
index 191ae5911e98e5086c1b8ae5b90423c3a91f04ca..a453d70e43fa9043f3e0ffd85690e57974f9b99e 100644 (file)
@@ -9,9 +9,8 @@
 //
 // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc.
 //    http://www.myelin.co.nz
-// (c) 2004-2006 Novell, Inc. (http://www.novell.com)
+// (c) 2004-2011 Novell, Inc. (http://www.novell.com)
 //
-
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -45,9 +44,7 @@ using System.IO;
 using System.Net.Configuration;
 using System.Text;
 using System.Timers;
-#if !MOONLIGHT
 using System.Net.NetworkInformation;
-#endif
 
 namespace System.Net.Sockets 
 {
@@ -55,8 +52,11 @@ namespace System.Net.Sockets
        {
                private bool islistening;
                private bool useoverlappedIO;
+               private const int SOCKET_CLOSED = 10004;
 
-               static void AddSockets (ArrayList sockets, IList list, string name)
+               private static readonly string timeout_exc_msg = "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond";
+
+               static void AddSockets (List<Socket> sockets, IList list, string name)
                {
                        if (list != null) {
                                foreach (Socket sock in list) {
@@ -76,7 +76,7 @@ namespace System.Net.Sockets
 #endif
                public static void Select (IList checkRead, IList checkWrite, IList checkError, int microSeconds)
                {
-                       ArrayList list = new ArrayList ();
+                       var list = new List<Socket> ();
                        AddSockets (list, checkRead, "checkRead");
                        AddSockets (list, checkWrite, "checkWrite");
                        AddSockets (list, checkError, "checkError");
@@ -92,7 +92,7 @@ namespace System.Net.Sockets
                         *                               WRITE socket 0-n, null,
                         *                               ERROR socket 0-n, null
                         */
-                       Socket [] sockets = (Socket []) list.ToArray (typeof (Socket));
+                       Socket [] sockets = list.ToArray ();
                        Select_internal (ref sockets, microSeconds, out error);
 
                        if (error != 0)
@@ -143,7 +143,7 @@ namespace System.Net.Sockets
 
                // private constructor used by Accept, which already
                // has a socket handle to use
-               private Socket(AddressFamily family, SocketType type,
+               internal Socket(AddressFamily family, SocketType type,
                               ProtocolType proto, IntPtr sock)
                {
                        address_family=family;
@@ -180,30 +180,26 @@ namespace System.Net.Sockets
                        }
                }
 
-
-               [MonoTODO]
+#if !MOBILE
                public Socket (SocketInformation socketInformation)
                {
-                       throw new NotImplementedException ("SocketInformation not figured out yet");
+                       var options = socketInformation.Options;
+                       islistening = (options & SocketInformationOptions.Listening) != 0;
+                       connected   = (options & SocketInformationOptions.Connected) != 0;
+                       blocking    = (options & SocketInformationOptions.NonBlocking) == 0;
+                       useoverlappedIO = (options & SocketInformationOptions.UseOnlyOverlappedIO) != 0;
 
-                       // ifdef to avoid the warnings.
-#if false
-                       //address_family = socketInformation.address_family;
-                       //socket_type = socketInformation.socket_type;
-                       //protocol_type = socketInformation.protocol_type;
-                       address_family = AddressFamily.InterNetwork;
-                       socket_type = SocketType.Stream;
-                       protocol_type = ProtocolType.IP;
+                       var result = Mono.DataConverter.Unpack ("iiiil", socketInformation.ProtocolInformation, 0);
                        
-                       int error;
-                       socket = Socket_internal (address_family, socket_type, protocol_type, out error);
-                       if (error != 0)
-                               throw new SocketException (error);
-
+                       address_family = (AddressFamily) (int) result [0];
+                       socket_type = (SocketType) (int) result [1];
+                       protocol_type = (ProtocolType) (int) result [2];
+                       isbound = (ProtocolType) (int) result [3] != 0;
+                       socket = (IntPtr) (long) result [4];
                        SocketDefaults ();
-#endif
                }
-
+#endif
+       
 #if !TARGET_JVM
                // Returns the amount of data waiting to be read on socket
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -411,7 +407,7 @@ namespace System.Net.Sockets
 #if !TARGET_JVM
                // Returns the local endpoint details in addr and port
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static SocketAddress LocalEndPoint_internal(IntPtr socket, out int error);
+               private extern static SocketAddress LocalEndPoint_internal(IntPtr socket, int family, out int error);
 #endif
 
                // Wish:  support non-IP endpoints.
@@ -431,7 +427,7 @@ namespace System.Net.Sockets
                                SocketAddress sa;
                                int error;
                                
-                               sa=LocalEndPoint_internal(socket, out error);
+                               sa=LocalEndPoint_internal(socket, (int) address_family, out error);
 
                                if (error != 0)
                                        throw new SocketException (error);
@@ -502,7 +498,6 @@ namespace System.Net.Sockets
                        }
                }
 
-#if !MOONLIGHT
                public bool AcceptAsync (SocketAsyncEventArgs e)
                {
                        // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
@@ -525,13 +520,17 @@ namespace System.Net.Sockets
                        }
 
                        e.curSocket = this;
-                       e.Worker.Init (this, null, e.AcceptCallback, SocketOperation.Accept);
-                       SocketAsyncCall sac = new SocketAsyncCall (e.Worker.Accept);
-                       sac.BeginInvoke (null, e.Worker.result);
+                       Worker w = e.Worker;
+                       w.Init (this, e, SocketOperation.Accept);
+                       int count;
+                       lock (readQ) {
+                               readQ.Enqueue (e.Worker);
+                               count = readQ.Count;
+                       }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, w.result);
                        return true;
                }
-#endif
-
                // Creates a new system socket, returning the handle
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static IntPtr Accept_internal(IntPtr sock, out int error, bool blocking);
@@ -542,21 +541,19 @@ namespace System.Net.Sockets
 
                        int error = 0;
                        IntPtr sock = (IntPtr) (-1);
-                       blocking_thread = Thread.CurrentThread;
                        try {
+                               RegisterForBlockingSyscall ();
                                sock = Accept_internal(socket, out error, blocking);
-                       } catch (ThreadAbortException) {
-                               if (disposed) {
-                                       Thread.ResetAbort ();
-                                       error = (int) SocketError.Interrupted;
-                               }
                        } finally {
-                               blocking_thread = null;
+                               UnRegisterForBlockingSyscall ();
+                       }
+
+                       if (error != 0) {
+                               if (closed)
+                                       error = SOCKET_CLOSED;
+                               throw new SocketException(error);
                        }
 
-                       if (error != 0)
-                               throw new SocketException (error);
-                       
                        Socket accepted = new Socket(this.AddressFamily, this.SocketType,
                                this.ProtocolType, sock);
 
@@ -572,21 +569,19 @@ namespace System.Net.Sockets
                        
                        int error = 0;
                        IntPtr sock = (IntPtr)(-1);
-                       blocking_thread = Thread.CurrentThread;
                        
                        try {
+                               RegisterForBlockingSyscall ();
                                sock = Accept_internal (socket, out error, blocking);
-                       } catch (ThreadAbortException) {
-                               if (disposed) {
-                                       Thread.ResetAbort ();
-                                       error = (int)SocketError.Interrupted;
-                               }
                        } finally {
-                               blocking_thread = null;
+                               UnRegisterForBlockingSyscall ();
                        }
                        
-                       if (error != 0)
+                       if (error != 0) {
+                               if (closed)
+                                       error = SOCKET_CLOSED;
                                throw new SocketException (error);
+                       }
                        
                        acceptSocket.address_family = this.AddressFamily;
                        acceptSocket.socket_type = this.SocketType;
@@ -610,10 +605,14 @@ namespace System.Net.Sockets
                                throw new InvalidOperationException ();
 
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.Accept);
-                       Worker worker = new Worker (req);
-                       SocketAsyncCall sac = new SocketAsyncCall (worker.Accept);
-                       sac.BeginInvoke (null, req);
-                       return(req);
+                       int count;
+                       lock (readQ) {
+                               readQ.Enqueue (req.Worker);
+                               count = readQ.Count;
+                       }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
+                       return req;
                }
 
                public IAsyncResult BeginAccept (int receiveSize,
@@ -627,16 +626,18 @@ namespace System.Net.Sockets
                                throw new ArgumentOutOfRangeException ("receiveSize", "receiveSize is less than zero");
 
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.AcceptReceive);
-                       Worker worker = new Worker (req);
-                       SocketAsyncCall sac = new SocketAsyncCall (worker.AcceptReceive);
-                       
                        req.Buffer = new byte[receiveSize];
                        req.Offset = 0;
                        req.Size = receiveSize;
                        req.SockFlags = SocketFlags.None;
-
-                       sac.BeginInvoke (null, req);
-                       return(req);
+                       int count;
+                       lock (readQ) {
+                               readQ.Enqueue (req.Worker);
+                               count = readQ.Count;
+                       }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
+                       return req;
                }
 
                public IAsyncResult BeginAccept (Socket acceptSocket,
@@ -667,64 +668,18 @@ namespace System.Net.Sockets
                        }
                        
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.AcceptReceive);
-                       Worker worker = new Worker (req);
-                       SocketAsyncCall sac = new SocketAsyncCall (worker.AcceptReceive);
-                       
                        req.Buffer = new byte[receiveSize];
                        req.Offset = 0;
                        req.Size = receiveSize;
                        req.SockFlags = SocketFlags.None;
                        req.AcceptSocket = acceptSocket;
-
-                       sac.BeginInvoke (null, req);
-                       return(req);
-               }
-
-               public IAsyncResult BeginConnect(EndPoint end_point,
-                                                AsyncCallback callback,
-                                                object state) {
-
-                       if (disposed && closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-
-                       if (end_point == null)
-                               throw new ArgumentNullException ("end_point");
-
-                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.Connect);
-                       req.EndPoint = end_point;
-
-                       // Bug #75154: Connect() should not succeed for .Any addresses.
-                       if (end_point is IPEndPoint) {
-                               IPEndPoint ep = (IPEndPoint) end_point;
-                               if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) {
-                                       req.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
-                                       return req;
-                               }
-                       }
-
-                       int error = 0;
-                       if (!blocking) {
-                               SocketAddress serial = end_point.Serialize ();
-                               Connect_internal (socket, serial, out error);
-                               if (error == 0) {
-                                       // succeeded synch
-                                       connected = true;
-                                       req.Complete (true);
-                               } else if (error != (int) SocketError.InProgress && error != (int) SocketError.WouldBlock) {
-                                       // error synch
-                                       connected = false;
-                                       req.Complete (new SocketException (error), true);
-                               }
-                       }
-
-                       if (blocking || error == (int) SocketError.InProgress || error == (int) SocketError.WouldBlock) {
-                               // continue asynch
-                               connected = false;
-                               Worker worker = new Worker (req);
-                               SocketAsyncCall sac = new SocketAsyncCall (worker.Connect);
-                               sac.BeginInvoke (null, req);
+                       int count;
+                       lock (readQ) {
+                               readQ.Enqueue (req.Worker);
+                               count = readQ.Count;
                        }
-
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
                        return(req);
                }
 
@@ -741,6 +696,9 @@ namespace System.Net.Sockets
                        if (address.ToString ().Length == 0)
                                throw new ArgumentException ("The length of the IP address is zero");
 
+                       if (port <= 0 || port > 65535)
+                               throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
+
                        if (islistening)
                                throw new InvalidOperationException ();
 
@@ -748,36 +706,6 @@ namespace System.Net.Sockets
                        return(BeginConnect (iep, callback, state));
                }
 
-               public IAsyncResult BeginConnect (IPAddress[] addresses,
-                                                 int port,
-                                                 AsyncCallback callback,
-                                                 object state)
-               {
-                       if (disposed && closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-
-                       if (addresses == null)
-                               throw new ArgumentNullException ("addresses");
-
-                       if (this.AddressFamily != AddressFamily.InterNetwork &&
-                               this.AddressFamily != AddressFamily.InterNetworkV6)
-                               throw new NotSupportedException ("This method is only valid for addresses in the InterNetwork or InterNetworkV6 families");
-
-                       if (islistening)
-                               throw new InvalidOperationException ();
-
-                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.Connect);
-                       req.Addresses = addresses;
-                       req.Port = port;
-                       
-                       connected = false;
-                       Worker worker = new Worker (req);
-                       SocketAsyncCall sac = new SocketAsyncCall (worker.Connect);
-                       sac.BeginInvoke (null, req);
-                       
-                       return(req);
-               }
-
                public IAsyncResult BeginConnect (string host, int port,
                                                  AsyncCallback callback,
                                                  object state)
@@ -792,11 +720,13 @@ namespace System.Net.Sockets
                                address_family != AddressFamily.InterNetworkV6)
                                throw new NotSupportedException ("This method is valid only for sockets in the InterNetwork and InterNetworkV6 families");
 
+                       if (port <= 0 || port > 65535)
+                               throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
+
                        if (islistening)
                                throw new InvalidOperationException ();
 
-                       IPAddress [] addresses = Dns.GetHostAddresses (host);
-                       return (BeginConnect (addresses, port, callback, state));
+                       return BeginConnect (Dns.GetHostAddresses (host), port, callback, state);
                }
 
                public IAsyncResult BeginDisconnect (bool reuseSocket,
@@ -808,13 +738,24 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.Disconnect);
                        req.ReuseSocket = reuseSocket;
-                       
-                       Worker worker = new Worker (req);
-                       SocketAsyncCall sac = new SocketAsyncCall (worker.Disconnect);
-                       sac.BeginInvoke (null, req);
-                       
+                       socket_pool_queue (Worker.Dispatcher, req);
                        return(req);
                }
+
+               void CheckRange (byte[] buffer, int offset, int size)
+               {
+                       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");
+               }
                
                public IAsyncResult BeginReceive(byte[] buffer, int offset,
                                                 int size,
@@ -828,26 +769,20 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
 
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.Receive);
                        req.Buffer = buffer;
                        req.Offset = offset;
                        req.Size = size;
                        req.SockFlags = socket_flags;
-                       Worker worker = new Worker (req);
+                       int count;
                        lock (readQ) {
-                               readQ.Enqueue (worker);
-                               if (readQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (worker.Receive);
-                                       sac.BeginInvoke (null, req);
-                               }
+                               readQ.Enqueue (req.Worker);
+                               count = readQ.Count;
                        }
-
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
                        return req;
                }
 
@@ -883,16 +818,14 @@ namespace System.Net.Sockets
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.ReceiveGeneric);
                        req.Buffers = buffers;
                        req.SockFlags = socketFlags;
-                       Worker worker = new Worker (req);
+                       int count;
                        lock(readQ) {
-                               readQ.Enqueue (worker);
-                               if (readQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (worker.ReceiveGeneric);
-                                       sac.BeginInvoke (null, req);
-                               }
+                               readQ.Enqueue (req.Worker);
+                               count = readQ.Count;
                        }
-                       
-                       return(req);
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
+                       return req;
                }
                
                [CLSCompliant (false)]
@@ -921,14 +854,10 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0)
-                               throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
-
-                       if (size < 0)
-                               throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
 
-                       if (offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset, size", "offset + size exceeds the buffer length");
+                       CheckRange (buffer, offset, size);
 
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.ReceiveFrom);
                        req.Buffer = buffer;
@@ -936,14 +865,13 @@ namespace System.Net.Sockets
                        req.Size = size;
                        req.SockFlags = socket_flags;
                        req.EndPoint = remote_end;
-                       Worker worker = new Worker (req);
+                       int count;
                        lock (readQ) {
-                               readQ.Enqueue (worker);
-                               if (readQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (worker.ReceiveFrom);
-                                       sac.BeginInvoke (null, req);
-                               }
+                               readQ.Enqueue (req.Worker);
+                               count = readQ.Count;
                        }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
                        return req;
                }
 
@@ -962,11 +890,7 @@ namespace System.Net.Sockets
                        if (remoteEP == null)
                                throw new ArgumentNullException ("remoteEP");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
 
                        throw new NotImplementedException ();
                }
@@ -980,14 +904,7 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0)
-                               throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
-
-                       if (size < 0)
-                               throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
-
-                       if (offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset, size", "offset + size exceeds the buffer length");
+                       CheckRange (buffer, offset, size);
 
                        if (!connected)
                                throw new SocketException ((int)SocketError.NotConnected);
@@ -997,14 +914,13 @@ namespace System.Net.Sockets
                        req.Offset = offset;
                        req.Size = size;
                        req.SockFlags = socket_flags;
-                       Worker worker = new Worker (req);
+                       int count;
                        lock (writeQ) {
-                               writeQ.Enqueue (worker);
-                               if (writeQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (worker.Send);
-                                       sac.BeginInvoke (null, req);
-                               }
+                               writeQ.Enqueue (req.Worker);
+                               count = writeQ.Count;
                        }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
                        return req;
                }
 
@@ -1043,16 +959,14 @@ namespace System.Net.Sockets
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.SendGeneric);
                        req.Buffers = buffers;
                        req.SockFlags = socketFlags;
-                       Worker worker = new Worker (req);
+                       int count;
                        lock (writeQ) {
-                               writeQ.Enqueue (worker);
-                               if (writeQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (worker.SendGeneric);
-                                       sac.BeginInvoke (null, req);
-                               }
+                               writeQ.Enqueue (req.Worker);
+                               count = writeQ.Count;
                        }
-                       
-                       return(req);
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
+                       return req;
                }
 
                [CLSCompliant (false)]
@@ -1141,7 +1055,10 @@ namespace System.Net.Sockets
                                throw new FileNotFoundException ();
 
                        SendFileHandler d = new SendFileHandler (SendFile);
-                       return new SendFileAsyncResult (d, d.BeginInvoke (fileName, preBuffer, postBuffer, flags, callback, state));
+                       return new SendFileAsyncResult (d, d.BeginInvoke (fileName, preBuffer, postBuffer, flags, ar => {
+                               SendFileAsyncResult sfar = new SendFileAsyncResult (d, ar);
+                               callback (sfar);
+                       }, state));
                }
 
                public IAsyncResult BeginSendTo(byte[] buffer, int offset,
@@ -1156,14 +1073,7 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0)
-                               throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
-
-                       if (size < 0)
-                               throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
-
-                       if (offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset, size", "offset + size exceeds the buffer length");
+                       CheckRange (buffer, offset, size);
 
                        SocketAsyncResult req = new SocketAsyncResult (this, state, callback, SocketOperation.SendTo);
                        req.Buffer = buffer;
@@ -1171,14 +1081,13 @@ namespace System.Net.Sockets
                        req.Size = size;
                        req.SockFlags = socket_flags;
                        req.EndPoint = remote_end;
-                       Worker worker = new Worker (req);
+                       int count;
                        lock (writeQ) {
-                               writeQ.Enqueue (worker);
-                               if (writeQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (worker.SendTo);
-                                       sac.BeginInvoke (null, req);
-                               }
+                               writeQ.Enqueue (req.Worker);
+                               count = writeQ.Count;
                        }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, req);
                        return req;
                }
 
@@ -1206,27 +1115,6 @@ namespace System.Net.Sockets
                        seed_endpoint = local_end;
                }
 
-#if !MOONLIGHT
-               public bool ConnectAsync (SocketAsyncEventArgs e)
-               {
-                       // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
-                       
-                       if (disposed && closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-                       if (islistening)
-                               throw new InvalidOperationException ("You may not perform this operation after calling the Listen method.");
-                       if (e.RemoteEndPoint == null)
-                               throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
-                       if (e.BufferList != null)
-                               throw new ArgumentException ("Multiple buffers cannot be used with this method.");
-
-                       e.DoOperation (SocketAsyncOperation.Connect, this);
-
-                       // We always return true for now
-                       return true;
-               }
-#endif
-               
                public void Connect (IPAddress address, int port)
                {
                        Connect (new IPEndPoint (address, port));
@@ -1285,7 +1173,6 @@ namespace System.Net.Sockets
                        Connect (addresses, port);
                }
 
-#if !MOONLIGHT
                public bool DisconnectAsync (SocketAsyncEventArgs e)
                {
                        // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
@@ -1293,12 +1180,10 @@ namespace System.Net.Sockets
                                throw new ObjectDisposedException (GetType ().ToString ());
 
                        e.curSocket = this;
-                       e.Worker.Init (this, null, e.DisconnectCallback, SocketOperation.Disconnect);
-                       SocketAsyncCall sac = new SocketAsyncCall (e.Worker.Disconnect);
-                       sac.BeginInvoke (null, e.Worker.result);
+                       e.Worker.Init (this, e, SocketOperation.Disconnect);
+                       socket_pool_queue (Worker.Dispatcher, e.Worker.result);
                        return true;
                }
-#endif
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                extern static void Disconnect_internal(IntPtr sock, bool reuse, out int error);
@@ -1332,17 +1217,24 @@ namespace System.Net.Sockets
                        }
                }
 
-               [MonoTODO ("Not implemented")]
+#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)
                {
-                       /* Need to serialize this socket into a
-                        * SocketInformation struct, but must study
-                        * the MS implementation harder to figure out
-                        * behaviour as documentation is lacking
-                        */
-                       throw new NotImplementedException ();
-               }
+                       var si = new SocketInformation ();
+                       si.Options =
+                               (islistening ? SocketInformationOptions.Listening : 0) |
+                               (connected ? SocketInformationOptions.Connected : 0) |
+                               (blocking ? 0 : SocketInformationOptions.NonBlocking) |
+                               (useoverlappedIO ? SocketInformationOptions.UseOnlyOverlappedIO : 0);
+
+                       si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", (int)address_family, (int)socket_type, (int)protocol_type, isbound ? 1 : 0, (long)socket);
+                       socket = (IntPtr) (-1);
 
+                       return si;
+               }
+#endif
+       
                public Socket EndAccept (IAsyncResult result)
                {
                        int bytes;
@@ -1402,7 +1294,6 @@ namespace System.Net.Sockets
                        req.CheckIfThrowDelayedException();
                }
 
-#if !MOONLIGHT
                public void EndDisconnect (IAsyncResult asyncResult)
                {
                        if (disposed && closed)
@@ -1422,7 +1313,6 @@ namespace System.Net.Sockets
 
                        req.CheckIfThrowDelayedException ();
                }
-#endif
 
                [MonoTODO]
                public int EndReceiveMessageFrom (IAsyncResult asyncResult,
@@ -1463,7 +1353,6 @@ namespace System.Net.Sockets
                        ares.Delegate.EndInvoke (ares.Original);
                }
 
-#if !MOONLIGHT
                public int EndSendTo (IAsyncResult result)
                {
                        if (disposed && closed)
@@ -1484,7 +1373,6 @@ namespace System.Net.Sockets
                        req.CheckIfThrowDelayedException();
                        return req.Total;
                }
-#endif
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void GetSocketOption_arr_internal(IntPtr socket,
@@ -1527,7 +1415,8 @@ namespace System.Net.Sockets
                // 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.
+               // 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);
@@ -1550,13 +1439,9 @@ namespace System.Net.Sockets
                        return result;
                }
 
-               [MonoTODO]
                public int IOControl (IOControlCode ioControlCode, byte[] optionInValue, byte[] optionOutValue)
                {
-                       /* Probably just needs to mirror the int
-                        * overload, but more investigation needed.
-                        */
-                       throw new NotImplementedException ();
+                       return IOControl ((int) ioControlCode, optionInValue, optionOutValue);
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -1610,20 +1495,7 @@ namespace System.Net.Sockets
 
                public int Receive (byte [] buffer)
                {
-                       if (disposed && closed)
-                               throw new ObjectDisposedException (GetType ().ToString ());
-
-                       if (buffer == null)
-                               throw new ArgumentNullException ("buffer");
-
-                       SocketError error;
-
-                       int ret = Receive_nochecks (buffer, 0, buffer.Length, SocketFlags.None, out error);
-                       
-                       if (error != SocketError.Success)
-                               throw new SocketException ((int) error);
-
-                       return ret;
+                       return Receive (buffer, SocketFlags.None);
                }
 
                public int Receive (byte [] buffer, SocketFlags flags)
@@ -1640,7 +1512,7 @@ namespace System.Net.Sockets
                        
                        if (error != SocketError.Success) {
                                if (error == SocketError.WouldBlock && blocking) // This might happen when ReceiveTimeout is set
-                                       throw new SocketException ((int) error, "Operation timed out.");
+                                       throw new SocketException ((int) error, timeout_exc_msg);
                                throw new SocketException ((int) error);
                        }
 
@@ -1655,8 +1527,7 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (size < 0 || size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, 0, size);
 
                        SocketError error;
 
@@ -1664,7 +1535,7 @@ namespace System.Net.Sockets
                        
                        if (error != SocketError.Success) {
                                if (error == SocketError.WouldBlock && blocking) // This might happen when ReceiveTimeout is set
-                                       throw new SocketException ((int) error, "Operation timed out.");
+                                       throw new SocketException ((int) error, timeout_exc_msg);
                                throw new SocketException ((int) error);
                        }
 
@@ -1679,11 +1550,7 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
                        
                        SocketError error;
 
@@ -1691,7 +1558,7 @@ namespace System.Net.Sockets
                        
                        if (error != SocketError.Success) {
                                if (error == SocketError.WouldBlock && blocking) // This might happen when ReceiveTimeout is set
-                                       throw new SocketException ((int) error, "Operation timed out.");
+                                       throw new SocketException ((int) error, timeout_exc_msg);
                                throw new SocketException ((int) error);
                        }
 
@@ -1706,16 +1573,11 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
                        
                        return Receive_nochecks (buffer, offset, size, flags, out error);
                }
 
-#if !MOONLIGHT
                public bool ReceiveFromAsync (SocketAsyncEventArgs e)
                {
                        if (disposed && closed)
@@ -1728,24 +1590,22 @@ namespace System.Net.Sockets
                                throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
 
                        e.curSocket = this;
-                       e.Worker.Init (this, null, e.ReceiveFromCallback, SocketOperation.ReceiveFrom);
+                       e.Worker.Init (this, e, SocketOperation.ReceiveFrom);
                        SocketAsyncResult res = e.Worker.result;
                        res.Buffer = e.Buffer;
                        res.Offset = e.Offset;
                        res.Size = e.Count;
                        res.EndPoint = e.RemoteEndPoint;
                        res.SockFlags = e.SocketFlags;
-                       Worker worker = new Worker (e);
+                       int count;
                        lock (readQ) {
-                               readQ.Enqueue (worker);
-                               if (readQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (e.Worker.ReceiveFrom);
-                                       sac.BeginInvoke (null, res);
-                               }
+                               readQ.Enqueue (e.Worker);
+                               count = readQ.Count;
                        }
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, res);
                        return true;
                }
-#endif
 
                public int ReceiveFrom (byte [] buffer, ref EndPoint remoteEP)
                {
@@ -1814,11 +1674,7 @@ namespace System.Net.Sockets
                        if (remoteEP == null)
                                throw new ArgumentNullException ("remoteEP");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
 
                        return ReceiveFrom_nochecks (buffer, offset, size, flags, ref remoteEP);
                }
@@ -1841,7 +1697,7 @@ namespace System.Net.Sockets
                                        connected = false;
                                else if (err == SocketError.WouldBlock && blocking) { // This might happen when ReceiveTimeout is set
                                        if (throwOnError)       
-                                               throw new SocketException ((int) SocketError.TimedOut, "Operation timed out");
+                                               throw new SocketException ((int) SocketError.TimedOut, timeout_exc_msg);
                                        error = (int) SocketError.TimedOut;
                                        return 0;
                                }
@@ -1896,11 +1752,7 @@ namespace System.Net.Sockets
                        if (remoteEP == null)
                                throw new ArgumentNullException ("remoteEP");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
 
                        /* FIXME: figure out how we get hold of the
                         * IPPacketInformation
@@ -1963,8 +1815,7 @@ namespace System.Net.Sockets
                        if (buf == null)
                                throw new ArgumentNullException ("buf");
 
-                       if (size < 0 || size > buf.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buf, 0, size);
 
                        SocketError error;
 
@@ -1984,11 +1835,7 @@ namespace System.Net.Sockets
                        if (buf == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0 || offset > buf.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buf.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buf, offset, size);
 
                        SocketError error;
 
@@ -2008,11 +1855,7 @@ namespace System.Net.Sockets
                        if (buf == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0 || offset > buf.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buf.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buf, offset, size);
 
                        return Send_nochecks (buf, offset, size, flags, out error);
                }
@@ -2053,7 +1896,6 @@ namespace System.Net.Sockets
                        }
                }
 
-#if !MOONLIGHT
                public bool SendToAsync (SocketAsyncEventArgs e)
                {
                        // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
@@ -2066,25 +1908,22 @@ namespace System.Net.Sockets
                                throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
 
                        e.curSocket = this;
-                       e.Worker.Init (this, null, e.SendToCallback, SocketOperation.SendTo);
+                       e.Worker.Init (this, e, SocketOperation.SendTo);
                        SocketAsyncResult res = e.Worker.result;
                        res.Buffer = e.Buffer;
                        res.Offset = e.Offset;
                        res.Size = e.Count;
                        res.SockFlags = e.SocketFlags;
                        res.EndPoint = e.RemoteEndPoint;
-                       Worker worker = new Worker (e);
+                       int count;
                        lock (writeQ) {
-                               writeQ.Enqueue (worker);
-                               if (writeQ.Count == 1) {
-                                       SocketAsyncCall sac = new SocketAsyncCall (e.Worker.SendTo);
-                                       sac.BeginInvoke (null, res);
-                               }
+                               writeQ.Enqueue (e.Worker);
+                               count = writeQ.Count;
                        }
-                       // We always return true for now
+                       if (count == 1)
+                               socket_pool_queue (Worker.Dispatcher, res);
                        return true;
                }
-#endif
                
                public int SendTo (byte [] buffer, EndPoint remote_end)
                {
@@ -2125,8 +1964,7 @@ namespace System.Net.Sockets
                        if (remote_end == null)
                                throw new ArgumentNullException ("remote_end");
 
-                       if (size < 0 || size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, 0, size);
 
                        return SendTo_nochecks (buffer, 0, size, flags, remote_end);
                }
@@ -2152,11 +1990,7 @@ namespace System.Net.Sockets
                        if (remote_end == null)
                                throw new ArgumentNullException("remote_end");
 
-                       if (offset < 0 || offset > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset");
-
-                       if (size < 0 || offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("size");
+                       CheckRange (buffer, offset, size);
 
                        return SendTo_nochecks (buffer, offset, size, flags, remote_end);
                }