Quellcode durchsuchen

2008-05-09 Marek Habersack <[email protected]>

	* 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
Marek Habersack vor 17 Jahren
Ursprung
Commit
a0fc121d39

+ 15 - 0
mcs/class/System/System.Net.Sockets/ChangeLog

@@ -1,3 +1,18 @@
+2008-05-09  Marek Habersack  <[email protected]>
+
+	* 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  <[email protected]>
 
 	* SocketAsyncOperation.cs, SendPacketsElement.cs,

+ 171 - 11
mcs/class/System/System.Net.Sockets/Socket.cs

@@ -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 ();
 

+ 176 - 10
mcs/class/System/System.Net.Sockets/SocketAsyncEventArgs.cs

@@ -29,11 +29,12 @@
 #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