// System.Net.Sockets.SocketAsyncEventArgs.cs // // Authors: // Marek Habersack (mhabersack@novell.com) // // Copyright (c) 2008 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 // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // #if NET_2_0 using System; using System.Collections.Generic; using System.Reflection; using System.Security; using System.Threading; namespace System.Net.Sockets { public class SocketAsyncEventArgs : EventArgs, IDisposable { #if NET_2_1 && !MONOTOUCH static MethodInfo check_socket_policy; static SocketAsyncEventArgs () { Type type = Type.GetType ("System.Windows.Browser.Net.CrossDomainPolicyManager, System.Windows.Browser, Version=2.0.5.0, Culture=Neutral, PublicKeyToken=7cec85d7bea7798e"); check_socket_policy = type.GetMethod ("CheckEndPoint"); } static internal bool CheckEndPoint (EndPoint endpoint) { if (check_socket_policy == null) throw new SecurityException (); return ((bool) check_socket_policy.Invoke (null, new object [1] { endpoint })); } #endif public event EventHandler Completed; IList > _bufferList; public Socket AcceptSocket { get; set; } public byte[] Buffer { get; private set; } [MonoTODO ("not supported in all cases")] public IList> BufferList { get { return _bufferList; } set { if (Buffer != null && value != null) throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null."); _bufferList = value; } } public int BytesTransferred { get; private set; } public int Count { get; private set; } public bool DisconnectReuseSocket { get; set; } public SocketAsyncOperation LastOperation { get; private set; } public int Offset { get; private set; } public EndPoint RemoteEndPoint { get; set; } #if !NET_2_1 public IPPacketInformation ReceiveMessageFromPacketInfo { get; private set; } public SendPacketsElement[] SendPacketsElements { get; set; } public TransmitFileOptions SendPacketsFlags { get; set; } #endif public int SendPacketsSendSize { get; set; } public SocketError SocketError { get; set; } public SocketFlags SocketFlags { get; set; } public object UserToken { get; set; } Socket curSocket; #if NET_2_1 public Socket ConnectSocket { get { switch (SocketError) { case SocketError.AccessDenied: return null; default: return curSocket; } } } internal bool PolicyRestricted { get; private set; } internal SocketAsyncEventArgs (bool policy) : this () { PolicyRestricted = policy; } #endif public SocketAsyncEventArgs () { AcceptSocket = null; Buffer = null; BufferList = null; BytesTransferred = 0; Count = 0; DisconnectReuseSocket = false; LastOperation = SocketAsyncOperation.None; Offset = 0; RemoteEndPoint = null; #if !NET_2_1 SendPacketsElements = null; SendPacketsFlags = TransmitFileOptions.UseDefaultWorkerThread; #endif SendPacketsSendSize = 0; SocketError = SocketError.Success; SocketFlags = SocketFlags.None; UserToken = null; } ~SocketAsyncEventArgs () { Dispose (false); } void Dispose (bool disposing) { Socket acceptSocket = AcceptSocket; if (acceptSocket != null) acceptSocket.Close (); if (disposing) GC.SuppressFinalize (this); } public void Dispose () { Dispose (true); } protected virtual void OnCompleted (SocketAsyncEventArgs e) { if (e == null) return; if (e.Completed != null) e.Completed (e.curSocket, e); } public void SetBuffer (int offset, int count) { SetBufferInternal (Buffer, offset, count); } public void SetBuffer (byte[] buffer, int offset, int count) { SetBufferInternal (buffer, offset, count); } void SetBufferInternal (byte[] buffer, int offset, int count) { if (buffer != null) { if (BufferList != null) throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null."); int buflen = buffer.Length; if (offset < 0 || offset >= buflen) throw new ArgumentOutOfRangeException ("offset"); if (count < 0 || count + offset > buflen) throw new ArgumentOutOfRangeException ("count"); } Count = count; Offset = offset; Buffer = buffer; } #region Internals void ReceiveCallback () { SocketError = SocketError.Success; LastOperation = SocketAsyncOperation.Receive; SocketError error = SocketError.Success; if (!curSocket.Connected) { SocketError = SocketError.NotConnected; return; } try { // FIXME: this does not support using BufferList BytesTransferred = curSocket.Receive_nochecks (Buffer, Offset, Count, SocketFlags, out error); } finally { SocketError = error; OnCompleted (this); } } void ConnectCallback () { LastOperation = SocketAsyncOperation.Connect; SocketError error = SocketError.Success; try { #if NET_2_1 && !MONOTOUCH // Connect to the first address that match the host name, like: // http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0-beta-2.aspx // while skipping entries that do not match the address family DnsEndPoint dep = (RemoteEndPoint as DnsEndPoint); if (dep != null) { IPAddress[] addresses = Dns.GetHostAddresses (dep.Host); foreach (IPAddress addr in addresses) { try { if (curSocket.AddressFamily == addr.AddressFamily) { error = TryConnect (new IPEndPoint (addr, dep.Port)); if (error == SocketError.Success) break; } } catch (SocketException) { error = SocketError.AccessDenied; } } } else { error = TryConnect (RemoteEndPoint); } #else error = TryConnect (RemoteEndPoint); #endif } finally { SocketError = error; OnCompleted (this); } } SocketError TryConnect (EndPoint endpoint) { curSocket.Connected = false; SocketError error = SocketError.Success; #if NET_2_1 && !MONOTOUCH // if we're not downloading a socket policy then check the policy if (!PolicyRestricted) { error = SocketError.AccessDenied; if (!CheckEndPoint (endpoint)) { return error; } } #endif try { if (!curSocket.Blocking) { int success; curSocket.Poll (-1, SelectMode.SelectWrite, out success); error = (SocketError)success; if (success == 0) curSocket.Connected = true; else return error; } else { curSocket.seed_endpoint = endpoint; curSocket.Connect (endpoint); curSocket.Connected = true; } } catch (SocketException se){ error = se.SocketErrorCode; } return error; } void SendCallback () { SocketError = SocketError.Success; LastOperation = SocketAsyncOperation.Send; SocketError error = SocketError.Success; if (!curSocket.Connected) { SocketError = SocketError.NotConnected; return; } try { if (Buffer != null) { BytesTransferred = curSocket.Send_nochecks (Buffer, Offset, Count, SocketFlags.None, out error); } else if (BufferList != null) { BytesTransferred = 0; foreach (ArraySegment asb in BufferList) { BytesTransferred += curSocket.Send_nochecks (asb.Array, asb.Offset, asb.Count, SocketFlags.None, out error); if (error != SocketError.Success) break; } } } finally { SocketError = error; OnCompleted (this); } } #if !NET_2_1 void AcceptCallback () { SocketError = SocketError.Success; LastOperation = SocketAsyncOperation.Accept; try { curSocket.Accept (AcceptSocket); } catch (SocketException ex) { SocketError = ex.SocketErrorCode; throw; } finally { 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); } } #endif internal void DoOperation (SocketAsyncOperation operation, Socket socket) { ThreadStart callback; curSocket = socket; switch (operation) { #if !NET_2_1 case SocketAsyncOperation.Accept: callback = new ThreadStart (AcceptCallback); break; case SocketAsyncOperation.Disconnect: callback = new ThreadStart (DisconnectCallback); break; case SocketAsyncOperation.ReceiveFrom: callback = new ThreadStart (ReceiveFromCallback); break; case SocketAsyncOperation.SendTo: callback = new ThreadStart (SendToCallback); break; #endif case SocketAsyncOperation.Receive: callback = new ThreadStart (ReceiveCallback); break; case SocketAsyncOperation.Connect: callback = new ThreadStart (ConnectCallback); break; case SocketAsyncOperation.Send: callback = new ThreadStart (SendCallback); break; default: throw new NotSupportedException (); } Thread t = new Thread (callback); t.IsBackground = true; t.Start (); } #endregion } } #endif