2008-05-09 Marek Habersack <mhabersack@novell.com>
authorMarek Habersack <grendel@twistedcode.net>
Fri, 9 May 2008 20:11:09 +0000 (20:11 -0000)
committerMarek Habersack <grendel@twistedcode.net>
Fri, 9 May 2008 20:11:09 +0000 (20:11 -0000)
* SocketAsyncEventArgs.cs: implemented support for executing
asynchronous socket actions, called from the new xxxxAsync methods
in Socket. The asynchronous operations do not use the ThreadPool
as I can't get the sample applications to work (send/receive
callbacks don't work) - it may change in the future.

* Socket.cs: implemented several xxxxAsync 2.0sp1 methods. The
ones not implemented for now are: ReceiveMessageFromAsync and
SendPacketsAsync. AcceptAsync doesn't perform the check for the
buffer size for now - need to implement runtime support for that.
Made several methods internal, so that they can be accessed from
SocketAsyncEventArgs code.

svn path=/trunk/mcs/; revision=102884

mcs/class/System/System.Net.Sockets/ChangeLog
mcs/class/System/System.Net.Sockets/Socket.cs
mcs/class/System/System.Net.Sockets/SocketAsyncEventArgs.cs

index ae6131e9e40e15e80e0a9407d046721423ad9e72..537c54d7907c2f384e0a0983aacb55ad4248952b 100644 (file)
@@ -1,3 +1,18 @@
+2008-05-09  Marek Habersack  <mhabersack@novell.com>
+
+       * SocketAsyncEventArgs.cs: implemented support for executing
+       asynchronous socket actions, called from the new xxxxAsync methods
+       in Socket. The asynchronous operations do not use the ThreadPool
+       as I can't get the sample applications to work (send/receive
+       callbacks don't work) - it may change in the future.
+
+       * Socket.cs: implemented several xxxxAsync 2.0sp1 methods. The
+       ones not implemented for now are: ReceiveMessageFromAsync and
+       SendPacketsAsync. AcceptAsync doesn't perform the check for the
+       buffer size for now - need to implement runtime support for that.
+       Made several methods internal, so that they can be accessed from
+       SocketAsyncEventArgs code.
+
 2008-05-08  Marek Habersack  <mhabersack@novell.com>
 
        * SocketAsyncOperation.cs, SendPacketsElement.cs,
index 88012d07956231395caca4ac74109cf2110dc9d9..9575a834c09dd74db25df0035125b63ca462f076 100644 (file)
@@ -566,7 +566,7 @@ namespace System.Net.Sockets
                 * As such, this value is set on Bind, SentTo, ReceiveFrom,
                 * Connect, etc.
                 */
-               private EndPoint seed_endpoint = null;
+               internal EndPoint seed_endpoint = null;
 
 #if NET_2_0
                private bool isbound;
@@ -798,6 +798,8 @@ namespace System.Net.Sockets
                        get {
                                return(connected);
                        }
+
+                       internal set { connected = value; }
                }
 
 #if NET_2_0
@@ -1281,6 +1283,41 @@ namespace System.Net.Sockets
                        }
                }
 
+#if NET_2_0
+               public bool AcceptAsync (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 (!IsBound)
+                               throw new InvalidOperationException ("You must call the Bind method before performing this operation.");
+                       if (!islistening)
+                               throw new InvalidOperationException ("You must call the Listen method before performing this operation.");
+                       if (e.BufferList != null)
+                               throw new ArgumentException ("Multiple buffers cannot be used with this method.");
+                       if (e.Count < 0)
+                               throw new ArgumentOutOfRangeException ("e.Count");
+                       
+                       Socket acceptSocket = e.AcceptSocket;
+                       if (acceptSocket != null) {
+                               if (acceptSocket.IsBound || acceptSocket.Connected)
+                                       throw new InvalidOperationException ("AcceptSocket: The socket must not be bound or connected.");
+                       } else
+                               e.AcceptSocket = new Socket (AddressFamily, SocketType, ProtocolType);
+
+                       try {
+                               e.DoOperation (SocketAsyncOperation.Accept, this);
+                       } catch {
+                               ((IDisposable)e).Dispose ();
+                               throw;
+                       }
+
+                       // We always return true for now
+                       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);
@@ -1315,7 +1352,7 @@ namespace System.Net.Sockets
                        return(accepted);
                }
 
-               private void Accept (Socket acceptSocket)
+               internal void Accept (Socket acceptSocket)
                {
                        if (disposed && closed)
                                throw new ObjectDisposedException (GetType ().ToString ());
@@ -1967,8 +2004,27 @@ namespace System.Net.Sockets
                {
                        this.Close ();
                }
-#endif
 
+               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
+               
                // Connects to the remote address
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void Connect_internal(IntPtr sock,
@@ -2077,6 +2133,18 @@ namespace System.Net.Sockets
                        Connect (hostent.AddressList, port);
                }
 
+#if NET_2_0
+               public bool DisconnectAsync (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 ());
+
+                       e.DoOperation (SocketAsyncOperation.Disconnect, this);
+
+                       return true;
+               }
+#endif
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void Disconnect_internal(IntPtr sock,
                                                               bool reuse,
@@ -2523,7 +2591,7 @@ namespace System.Net.Sockets
                 * also needs to check the socket error status, but
                 * getsockopt(..., SO_ERROR) clears the error.
                 */
-               private bool Poll (int time_us, SelectMode mode, out int socket_error)
+               internal bool Poll (int time_us, SelectMode mode, out int socket_error)
                {
                        if (disposed && closed)
                                throw new ObjectDisposedException (GetType ().ToString ());
@@ -2553,6 +2621,28 @@ namespace System.Net.Sockets
                        
                        return result;
                }
+
+#if NET_2_0
+               public bool ReceiveAsync (SocketAsyncEventArgs e)
+               {
+                       // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
+                       //
+                       // LAME SPEC: the ArgumentException is never thrown, instead an NRE is
+                       // thrown when e.Buffer and e.BufferList are null (works fine when one is
+                       // set to a valid object)
+                       if (disposed && closed)
+                               throw new ObjectDisposedException (GetType ().ToString ());
+
+                       // We do not support recv into multiple buffers yet
+                       if (e.BufferList != null)
+                               throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
+                       
+                       e.DoOperation (SocketAsyncOperation.Receive, this);
+
+                       // We always return true for now
+                       return true;
+               }
+#endif
                
                public int Receive (byte [] buf)
                {
@@ -2697,7 +2787,7 @@ namespace System.Net.Sockets
                                                           SocketFlags flags,
                                                           out int error);
 
-               int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
+               internal int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
                {
                        int nativeError;
                        int ret = Receive_internal (socket, buf, offset, size, flags, out nativeError);
@@ -2709,6 +2799,25 @@ namespace System.Net.Sockets
                        
                        return ret;
                }
+
+#if NET_2_0
+               public bool ReceiveFromAsync (SocketAsyncEventArgs e)
+               {
+                       if (disposed && closed)
+                               throw new ObjectDisposedException (GetType ().ToString ());
+
+                       // We do not support recv into multiple buffers yet
+                       if (e.BufferList != null)
+                               throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
+                       if (e.RemoteEndPoint == null)
+                               throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
+
+                       e.DoOperation (SocketAsyncOperation.ReceiveFrom, this);
+
+                       // We always return true for now
+                       return true;
+               }
+#endif
                
                public int ReceiveFrom (byte [] buf, ref EndPoint remote_end)
                {
@@ -2786,8 +2895,8 @@ namespace System.Net.Sockets
                        return ReceiveFrom_nochecks (buf, offset, size, flags, ref remote_end);
                }
 
-               int ReceiveFrom_nochecks (byte [] buf, int offset, int size, SocketFlags flags,
-                                         ref EndPoint remote_end)
+               internal int ReceiveFrom_nochecks (byte [] buf, int offset, int size, SocketFlags flags,
+                                                  ref EndPoint remote_end)
                {
                        SocketAddress sockaddr = remote_end.Serialize();
                        int cnt, error;
@@ -2827,6 +2936,16 @@ namespace System.Net.Sockets
                }
 
 #if NET_2_0
+               [MonoTODO ("Not implemented")]
+               public bool ReceiveMessageFromAsync (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 ());
+                       
+                       throw new NotImplementedException ();
+               }
+               
                [MonoTODO ("Not implemented")]
                public int ReceiveMessageFrom (byte[] buffer, int offset,
                                               int size,
@@ -2854,6 +2973,32 @@ namespace System.Net.Sockets
                         */
                        throw new NotImplementedException ();
                }
+
+               [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 (disposed && closed)
+                               throw new ObjectDisposedException (GetType ().ToString ());
+                       
+                       throw new NotImplementedException ();
+               }
+
+               public bool SendAsync (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 (e.Buffer == null && e.BufferList == null)
+                               throw new ArgumentException ("Either e.Buffer or e.BufferList must be valid buffers.");
+
+                       e.DoOperation (SocketAsyncOperation.Send, this);
+
+                       // We always return true for now
+                       return true;
+               }
 #endif
 
                public int Send (byte [] buf)
@@ -2988,7 +3133,7 @@ namespace System.Net.Sockets
                                                        SocketFlags flags,
                                                        out int error);
 
-               int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
+               internal int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
                {
                        if (size == 0) {
                                error = SocketError.Success;
@@ -3047,8 +3192,23 @@ namespace System.Net.Sockets
                        /* FIXME: Implement TransmitFile */
                        throw new NotImplementedException ();
                }
-#endif
 
+               public bool SendToAsync (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 (e.RemoteEndPoint == null)
+                               throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
+                       
+                       e.DoOperation (SocketAsyncOperation.SendTo, this);
+
+                       // We always return true for now
+                       return true;
+               }
+#endif
+               
                public int SendTo (byte [] buffer, EndPoint remote_end)
                {
                        if (disposed && closed)
@@ -3124,8 +3284,8 @@ namespace System.Net.Sockets
                        return SendTo_nochecks (buffer, offset, size, flags, remote_end);
                }
 
-               int SendTo_nochecks (byte [] buffer, int offset, int size, SocketFlags flags,
-                                  EndPoint remote_end)
+               internal int SendTo_nochecks (byte [] buffer, int offset, int size, SocketFlags flags,
+                                             EndPoint remote_end)
                {
                        SocketAddress sockaddr = remote_end.Serialize ();
 
index 7d48bd3c41ad9896a4587bfebe8e048f224aeb16..be0ba9ff14836f9dcdfb409ddfff47c9c83a5b5e 100644 (file)
 #if NET_2_0
 using System;
 using System.Collections.Generic;
+using System.Threading;
 
 namespace System.Net.Sockets
 {
        public class SocketAsyncEventArgs : EventArgs, IDisposable
-       {               
+       {
                public event EventHandler<SocketAsyncEventArgs> Completed;
 
                IList <ArraySegment <byte>> _bufferList;
@@ -64,6 +65,8 @@ namespace System.Net.Sockets
                public SocketFlags SocketFlags { get; set; }
                public object UserToken { get; set; }
 
+               Socket curSocket;
+               
                public SocketAsyncEventArgs ()
                {
                        AcceptSocket = null;
@@ -96,12 +99,7 @@ namespace System.Net.Sockets
 
                        if (disposing)
                                GC.SuppressFinalize (this);
-               }
-               
-               public void Dispose ()
-               {
-                       Dispose (true);
-               }
+               }               
 
                void IDisposable.Dispose ()
                {
@@ -110,8 +108,11 @@ namespace System.Net.Sockets
                
                protected virtual void OnCompleted (SocketAsyncEventArgs e)
                {
-                       if (Completed != null)
-                               Completed (this, e);
+                       if (e == null)
+                               return;
+                       
+                       if (e.Completed != null)
+                               e.Completed (e.curSocket, e);
                }
 
                public void SetBuffer (int offset, int count)
@@ -134,7 +135,7 @@ namespace System.Net.Sockets
                                if (offset < 0 || offset >= buflen)
                                        throw new ArgumentOutOfRangeException ("offset");
 
-                               if (count < 0 || count + offset >= buflen)
+                               if (count < 0 || count + offset > buflen)
                                        throw new ArgumentOutOfRangeException ("count");
                        }
 
@@ -142,6 +143,171 @@ namespace System.Net.Sockets
                        Offset = offset;
                        Buffer = buffer;
                }
+
+#region Internals
+               void AcceptCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.Accept;
+                       try {
+                               curSocket.Accept (AcceptSocket);
+                       } catch (SocketException ex) {
+                               SocketError = ex.SocketErrorCode;
+                               throw;
+                       } finally {
+                               OnCompleted (this);
+                       }
+               }
+
+               void ReceiveCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.Receive;
+                       SocketError error = SocketError.Success;
+                       
+                       try {
+                               BytesTransferred = curSocket.Receive_nochecks (Buffer, Offset, Count, SocketFlags, out error);
+                       } finally {
+                               SocketError = error;
+                               OnCompleted (this);
+                       }
+               }
+
+               void ConnectCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.Connect;
+                       SocketError error = SocketError.Success;
+
+                       try {
+                               if (!curSocket.Blocking) {
+                                       int success;
+                                       curSocket.Poll (-1, SelectMode.SelectWrite, out success);
+                                       SocketError = (SocketError)success;
+                                       if (success == 0)
+                                               curSocket.Connected = true;
+                                       else
+                                               return;
+                               } else {
+                                       curSocket.seed_endpoint = RemoteEndPoint;
+                                       curSocket.Connect (RemoteEndPoint);
+                                       curSocket.Connected = true;
+                               }
+                       } finally {
+                               SocketError = error;
+                               OnCompleted (this);
+                       }
+               }
+
+               void SendCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.Send;
+                       SocketError error = SocketError.Success;
+
+                       try {
+                               BytesTransferred = curSocket.Send_nochecks (Buffer, Offset, Count, SocketFlags.None, out error);
+                       } finally {
+                               SocketError = error;
+                               OnCompleted (this);
+                       }
+               }
+
+               void DisconnectCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.Disconnect;
+
+                       try {
+                               curSocket.Disconnect (DisconnectReuseSocket);
+                       } catch (SocketException ex) {
+                               SocketError = ex.SocketErrorCode;
+                               throw;
+                       } finally {
+                               OnCompleted (this);
+                       }
+               }
+
+               void ReceiveFromCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.ReceiveFrom;
+
+                       try {
+                               EndPoint ep = RemoteEndPoint;
+                               BytesTransferred = curSocket.ReceiveFrom_nochecks (Buffer, Offset, Count, SocketFlags, ref ep);
+                       } catch (SocketException ex) {
+                               SocketError = ex.SocketErrorCode;
+                               throw;
+                       } finally {
+                               OnCompleted (this);
+                       }
+               }
+
+               void SendToCallback ()
+               {
+                       SocketError = SocketError.Success;
+                       LastOperation = SocketAsyncOperation.SendTo;
+                       int total = 0;
+                       
+                       try {
+                               int count = Count;
+
+                               while (total < count)
+                                       total += curSocket.SendTo_nochecks (Buffer, Offset, count, SocketFlags, RemoteEndPoint);
+                               BytesTransferred = total;
+                       } catch (SocketException ex) {
+                               SocketError = ex.SocketErrorCode;
+                               throw;
+                       } finally {
+                               OnCompleted (this);
+                       }
+               }
+               
+               internal void DoOperation (SocketAsyncOperation operation, Socket socket)
+               {
+                       ThreadStart callback;
+                       curSocket = socket;
+                       
+                       switch (operation) {
+                               case SocketAsyncOperation.Accept:
+                                       callback = new ThreadStart (AcceptCallback);
+                                       break;
+
+                               case SocketAsyncOperation.Receive:
+                                       callback = new ThreadStart (ReceiveCallback);
+                                       break;
+
+                               case SocketAsyncOperation.Connect:
+                                       callback = new ThreadStart (ConnectCallback);
+                                       break;
+
+                               case SocketAsyncOperation.Disconnect:
+                                       callback = new ThreadStart (DisconnectCallback);
+                                       break;
+
+                               case SocketAsyncOperation.ReceiveFrom:
+                                       callback = new ThreadStart (ReceiveFromCallback);
+                                       break;
+                                       
+                               case SocketAsyncOperation.Send:
+                                       callback = new ThreadStart (SendCallback);
+                                       break;
+
+                               case SocketAsyncOperation.SendTo:
+                                       callback = new ThreadStart (SendToCallback);
+                                       break;
+                                       
+                               default:
+                                       throw new NotSupportedException ();
+                                       break;
+                       }
+
+                       Thread t = new Thread (callback);
+                       t.IsBackground = true;
+                       t.Start ();
+               }
+#endregion
        }
 }
 #endif