File: net\System\Net\Sockets\UDPClient.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="UDPClient.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Net.Sockets {
 
    using System.Threading;
    using System.Threading.Tasks;
    using System.Security.Permissions;
    using System.Diagnostics.CodeAnalysis;
 
    /// <devdoc>
    ///    <para>
    ///       The <see cref='System.Net.Sockets.UdpClient'/> class provides access to UDP services at a
    ///       higher abstraction level than the <see cref='System.Net.Sockets.Socket'/> class. <see cref='System.Net.Sockets.UdpClient'/>
    ///       is used to connect to a remote host and to receive connections from a remote
    ///       Client.
    ///    </para>
    /// </devdoc>
    public class UdpClient : IDisposable {
 
        private const int MaxUDPSize = 0x10000;  
        private Socket m_ClientSocket;
        private bool m_Active;
        private byte[] m_Buffer = new byte[MaxUDPSize];
        /// <devdoc>
        ///    <para>
        ///       Address family for the client, defaults to IPv4.
        ///    </para>
        /// </devdoc>
        private AddressFamily m_Family = AddressFamily.InterNetwork;
 
        // bind to arbitrary IP+Port
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Net.Sockets.UdpClient'/>class.
        ///    </para>
        /// </devdoc>
        public UdpClient() : this(AddressFamily.InterNetwork){
        }
 
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Net.Sockets.UdpClient'/>class.
        ///    </para>
        /// </devdoc>
 
        public UdpClient(AddressFamily family) {
            //
            // Validate the address family
            //
            if ( family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6 ) {
                throw new ArgumentException(SR.GetString(SR.net_protocol_invalid_family, "UDP"), "family");
            }
 
            m_Family = family;
 
            createClientSocket();
        }
 
        // bind specific port, arbitrary IP
        /// <devdoc>
        ///    <para>
        ///       Creates a new instance of the UdpClient class that communicates on the
        ///       specified port number.
        ///    </para>
        /// </devdoc>
        /// We should obsolete this. This also breaks IPv6-only scenarios.
        /// But fixing it has many complications that we have decided not
        /// to fix it and instead obsolete it post-Orcas.
        public UdpClient(int port) : this(port,AddressFamily.InterNetwork) {
        }
 
        /// <devdoc>
        ///    <para>
        ///       Creates a new instance of the UdpClient class that communicates on the
        ///       specified port number.
        ///    </para>
        /// </devdoc>
        public UdpClient(int port,AddressFamily family) {
            //
            // parameter validation
            //
            if (!ValidationHelper.ValidateTcpPort(port)) {
                throw new ArgumentOutOfRangeException("port");
            }
            //
            // Validate the address family
            //
            if ( family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6 ) {
                throw new ArgumentException(SR.GetString(SR.net_protocol_invalid_family), "family");
            }
 
            IPEndPoint localEP;
            m_Family = family;
             
            if ( m_Family == AddressFamily.InterNetwork ) {
                localEP = new IPEndPoint(IPAddress.Any, port);
            }
            else {
                localEP = new IPEndPoint(IPAddress.IPv6Any, port);
            }
 
            createClientSocket();
 
            Client.Bind(localEP);
        }
 
        // bind to given local endpoint
        /// <devdoc>
        ///    <para>
        ///       Creates a new instance of the UdpClient class that communicates on the
        ///       specified end point.
        ///    </para>
        /// </devdoc>
        public UdpClient(IPEndPoint localEP) {
            //
            // parameter validation
            //
            if (localEP == null) {
                throw new ArgumentNullException("localEP");
            }
            //
            // IPv6 Changes: Set the AddressFamily of this object before
            //               creating the client socket.
            //
            m_Family = localEP.AddressFamily;
 
            createClientSocket();
 
            Client.Bind(localEP);
        }
 
        // bind and connect
        /// <devdoc>
        ///    <para>
        ///       Creates a new instance of the <see cref='System.Net.Sockets.UdpClient'/> class and connects to the
        ///       specified remote host on the specified port.
        ///    </para>
        /// </devdoc>
        public UdpClient(string hostname, int port) {
            //
            // parameter validation
            //
            if (hostname == null) {
                throw new ArgumentNullException("hostname");
            }
            if (!ValidationHelper.ValidateTcpPort(port)) {
                throw new ArgumentOutOfRangeException("port");
            }
            //
            // NOTE: Need to create different kinds of sockets based on the addresses
            //       returned from DNS. As a result, we defer the creation of the 
            //       socket until the Connect method.
            //
            //createClientSocket();
            Connect(hostname, port);
        }
 
        /// <devdoc>
        ///    <para>
        ///       Used by the class to provide the underlying network socket.
        ///    </para>
        /// </devdoc>
        public Socket Client {
            get {
                return m_ClientSocket;
            }
            set {
                m_ClientSocket = value;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Used by the class to indicate that a connection to a remote host has been
        ///       made.
        ///    </para>
        /// </devdoc>
        protected bool Active {
            get {
                return m_Active;
            }
            set {
                m_Active = value;
            }
        }
 
        public int Available{
            get{
                return m_ClientSocket.Available;
            }
        }
 
        public short Ttl{
            get{
                return m_ClientSocket.Ttl;
            } 
            set{
                m_ClientSocket.Ttl = value;
            }
        }      //new
 
 
        public bool DontFragment{
            get{
                return m_ClientSocket.DontFragment;
            } 
            set{
                m_ClientSocket.DontFragment = value;
            }
        }      //new
 
            
        public bool MulticastLoopback{
            get{
                return m_ClientSocket.MulticastLoopback;
            } 
            set{
                m_ClientSocket.MulticastLoopback = value;
            }
        }      //new
 
 
 
        public bool EnableBroadcast{
            get{
                return m_ClientSocket.EnableBroadcast;
            } 
            set{
                m_ClientSocket.EnableBroadcast = value;
            }
        }      //new
 
 
        public bool ExclusiveAddressUse {
            get{
                return m_ClientSocket.ExclusiveAddressUse;
            } 
            set{
                m_ClientSocket.ExclusiveAddressUse = value;
            }
        }      //new
 
        public void AllowNatTraversal(bool allowed) {
            if (allowed) {
                m_ClientSocket.SetIPProtectionLevel(IPProtectionLevel.Unrestricted);
            }
            else {
                m_ClientSocket.SetIPProtectionLevel(IPProtectionLevel.EdgeRestricted);
            }
        }
 
        public void Close() {
            Dispose(true);
        }
 
        private bool m_CleanedUp = false;
        private void FreeResources() {
            //
            // only resource we need to free is the network stream, since this
            // is based on the client socket, closing the stream will cause us
            // to flush the data to the network, close the stream and (in the
            // NetoworkStream code) close the socket as well.
            //
            if (m_CleanedUp) {
                return;
            }
 
            Socket chkClientSocket = Client;
            if (chkClientSocket!=null) {
                //
                // if the NetworkStream wasn't retrieved, the Socket might
                // still be there and needs to be closed to release the effect
                // of the Bind() call and free the bound IPEndPoint.
                //
                chkClientSocket.InternalShutdown(SocketShutdown.Both);
                chkClientSocket.Close();
                Client = null;
            }
            m_CleanedUp = true;
        }
 
        public void Dispose() {
            Dispose(true);
        }
 
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                GlobalLog.Print("UdpClient::Dispose()");
                FreeResources();
                GC.SuppressFinalize(this);
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Establishes a connection to the specified port on the
        ///       specified host.
        ///    </para>
        /// </devdoc>
        public void Connect(string hostname, int port){
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (hostname == null){
                throw new ArgumentNullException("hostname");
            }
            if (!ValidationHelper.ValidateTcpPort(port)){
                throw new ArgumentOutOfRangeException("port");
            }
 
            //
            // IPv6 Changes: instead of just using the first address in the list,
            //               we must now look for addresses that use a compatible
            //               address family to the client socket.
            //               However, in the case of the <hostname,port> constructor
            //               we will have deferred creating the socket and will
            //               do that here instead.
            //               In addition, the redundant CheckForBroadcast call was
            //               removed here since it is called from Connect().
            //
            IPAddress[] addresses = Dns.GetHostAddresses(hostname);
 
            Exception lastex = null;
            Socket ipv6Socket = null;
            Socket ipv4Socket = null;
 
            try {
                if (m_ClientSocket == null){
                    if (Socket.OSSupportsIPv4){
                        ipv4Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    }
                    if (Socket.OSSupportsIPv6){
                        ipv6Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
                    }
                }
 
 
                foreach (IPAddress address in addresses)
                {
                    try {
                        if (m_ClientSocket == null)
                        {
                            //
                            // We came via the <hostname,port> constructor. Set the
                            // address family appropriately, create the socket and
                            // try to connect.
                            //
                            if (address.AddressFamily == AddressFamily.InterNetwork && ipv4Socket != null) {
                                ipv4Socket.Connect(address, port);
                                m_ClientSocket = ipv4Socket;
                                if (ipv6Socket != null)
                                    ipv6Socket.Close();
                            }
                            else if (ipv6Socket != null) {
                                ipv6Socket.Connect(address, port);
                                m_ClientSocket = ipv6Socket;
                                if (ipv4Socket != null)
                                    ipv4Socket.Close();
                            }
 
 
                            m_Family = address.AddressFamily;
                            m_Active = true;
                            break;
                        }
                        else if (address.AddressFamily == m_Family) {
                            //
                            // Only use addresses with a matching family
                            //
                            Connect(new IPEndPoint(address, port));
                            m_Active = true;
                            break;
                        }
                    }
                    catch ( Exception ex )
                    {
                        if (NclUtilities.IsFatal(ex)) {
                            throw;
                        }
                        lastex = ex;
                    }
                }
            }
 
            catch (Exception ex){
                if (NclUtilities.IsFatal(ex)) {
                    throw;
                }
                lastex = ex;
            }
            finally
            {
                //cleanup temp sockets if failed
                //main socket gets closed when tcpclient gets closed
 
                //did we connect?
                if (!m_Active) {
                    if (ipv6Socket != null){
                        ipv6Socket.Close();
                    }
 
                    if (ipv4Socket != null){
                        ipv4Socket.Close();
                    }
 
 
                    //
                    // The connect failed - rethrow the last error we had
                    //
                    if (lastex != null)
                        throw lastex;
                    else
                        throw new SocketException(SocketError.NotConnected);
                }
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Establishes a connection with the host at the specified address on the
        ///       specified port.
        ///    </para>
        /// </devdoc>
        public void Connect(IPAddress addr, int port) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (addr==null){
                throw new ArgumentNullException("addr");
            }
            if (!ValidationHelper.ValidateTcpPort(port)) {
                throw new ArgumentOutOfRangeException("port");
            }
 
            //
            // IPv6 Changes: Removed redundant call to CheckForBroadcast() since
            //               it is made in the real Connect() method.
            //
            IPEndPoint endPoint = new IPEndPoint(addr, port);
 
            Connect(endPoint);
        }
 
        /// <devdoc>
        ///    <para>
        ///       Establishes a connection to a remote end point.
        ///    </para>
        /// </devdoc>
        public void Connect(IPEndPoint endPoint) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (endPoint==null){
                throw new ArgumentNullException("endPoint");
            }
            //
            // IPv6 Changes: Actually, no changes but we might want to check for 
            //               compatible protocols here rather than push it down
            //               to WinSock.
            //
            CheckForBroadcast(endPoint.Address);
            Client.Connect(endPoint);
            m_Active = true;
        }
        
    
        private bool m_IsBroadcast;
        private void CheckForBroadcast(IPAddress ipAddress) {
            //
            // Here we check to see if the user is trying to use a Broadcast IP address
            // we only detect IPAddress.Broadcast (which is not the only Broadcast address)
            // and in that case we set SocketOptionName.Broadcast on the socket to allow its use.
            // if the user really wants complete control over Broadcast addresses he needs to
            // inherit from UdpClient and gain control over the Socket and do whatever is appropriate.
            //
            if (Client!=null && !m_IsBroadcast && ipAddress.IsBroadcast) {
                //
                // we need to set the Broadcast socket option.
                // note that, once we set the option on the Socket, we never reset it.
                //
                m_IsBroadcast = true;
                Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            }
        }
 
 
 
        /// <devdoc>
        ///    <para>
        ///       Sends a UDP datagram to the host at the remote end point.
        ///    </para>
        /// </devdoc>
        public int Send(byte[] dgram, int bytes, IPEndPoint endPoint) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (dgram==null){
                throw new ArgumentNullException("dgram");
            }
            if (m_Active && endPoint!=null) {
                //
                // Do not allow sending packets to arbitrary host when connected
                //
                throw new InvalidOperationException(SR.GetString(SR.net_udpconnected));
            }
 
            if (endPoint==null) {
                return Client.Send(dgram, 0, bytes, SocketFlags.None);
            }
 
            CheckForBroadcast(endPoint.Address);
 
            return Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint);
        }
        
 
        /// <devdoc>
        ///    <para>
        ///       Sends a UDP datagram to the specified port on the specified remote host.
        ///    </para>
        /// </devdoc>
        public int Send(byte[] dgram, int bytes, string hostname, int port) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (dgram==null){
                throw new ArgumentNullException("dgram");
            }
            if (m_Active && ((hostname != null) || (port != 0))) {
                //
                // Do not allow sending packets to arbitrary host when connected
                //
                throw new InvalidOperationException(SR.GetString(SR.net_udpconnected));
            }
 
            if (hostname==null || port==0) {
                return Client.Send(dgram, 0, bytes, SocketFlags.None);
            }
 
            IPAddress[] addresses = Dns.GetHostAddresses(hostname);
            
            int i=0;
            for (;i<addresses.Length && addresses[i].AddressFamily != m_Family; i++);
 
            if (addresses.Length == 0 || i == addresses.Length) {
                throw new ArgumentException(SR.GetString(SR.net_invalidAddressList), "hostname");
            }
 
            CheckForBroadcast(addresses[i]);
            IPEndPoint ipEndPoint = new IPEndPoint(addresses[i], port);
            return Client.SendTo(dgram, 0, bytes, SocketFlags.None, ipEndPoint);
        }
 
 
 
 
        /// <devdoc>
        ///    <para>
        ///       Sends a UDP datagram to a
        ///       remote host.
        ///    </para>
        /// </devdoc>
        public int Send(byte[] dgram, int bytes) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (dgram==null){
                throw new ArgumentNullException("dgram");
            }
            if (!m_Active) {
                //
                // only allowed on connected socket
                //
                throw new InvalidOperationException(SR.GetString(SR.net_notconnected));
            }
 
            return Client.Send(dgram, 0, bytes, SocketFlags.None);
 
        }
 
    
 
        [HostProtection(ExternalThreading=true)]
        public IAsyncResult BeginSend(byte[] datagram, int bytes, IPEndPoint endPoint, AsyncCallback requestCallback, object state) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (datagram==null){
                throw new ArgumentNullException("datagram");
            }
 
            if (bytes > datagram.Length || bytes < 0) {
                throw new ArgumentOutOfRangeException("bytes");
            }
 
            if (m_Active && endPoint!=null) {
                //
                // Do not allow sending packets to arbitrary host when connected
                //
                throw new InvalidOperationException(SR.GetString(SR.net_udpconnected));
            }
 
            if (endPoint==null) {
                return Client.BeginSend(datagram, 0, bytes, SocketFlags.None, requestCallback, state);
            }
 
            CheckForBroadcast(endPoint.Address);
 
            return Client.BeginSendTo(datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state);
        }
 
 
 
        [HostProtection(ExternalThreading=true)]
        public IAsyncResult BeginSend(byte[] datagram, int bytes, string hostname, int port, AsyncCallback requestCallback, object state) {
            
            if (m_Active && ((hostname != null) || (port != 0))) {
                // Do not allow sending packets to arbitrary host when connected
                throw new InvalidOperationException(SR.GetString(SR.net_udpconnected));
            }
 
            IPEndPoint ipEndPoint = null;
 
            if (hostname!=null && port!=0) {
 
                IPAddress[] addresses = Dns.GetHostAddresses(hostname);
 
                int i=0; 
                for (;i<addresses.Length && addresses[i].AddressFamily != m_Family; i++);
 
                if (addresses.Length == 0 || i == addresses.Length) {
                    throw new ArgumentException(SR.GetString(SR.net_invalidAddressList), "hostname");
                }
 
                CheckForBroadcast(addresses[i]);
                ipEndPoint = new IPEndPoint(addresses[i], port);
            }
            return BeginSend(datagram, bytes, ipEndPoint,requestCallback, state);
        }
 
 
 
        [HostProtection(ExternalThreading=true)]
        public IAsyncResult BeginSend(byte[] datagram, int bytes, AsyncCallback requestCallback, object state) {
            return BeginSend(datagram, bytes, null, requestCallback, state);
        }
 
 
        public int EndSend(IAsyncResult asyncResult){
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            if (m_Active){
                return Client.EndSend(asyncResult);
            }
            else{
                return Client.EndSendTo(asyncResult);
            }
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Returns a datagram sent by a server.
        ///    </para>
        /// </devdoc>
        public byte[] Receive(ref IPEndPoint remoteEP) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            // this is a fix due to the nature of the ReceiveFrom() call and the 
            // ref parameter convention, we need to cast an IPEndPoint to it's base
            // class EndPoint and cast it back down to IPEndPoint. ugly but it works.
            //
            EndPoint tempRemoteEP;
            
            if ( m_Family == AddressFamily.InterNetwork ) {
                tempRemoteEP = IPEndPoint.Any;
            }
            else {
                tempRemoteEP = IPEndPoint.IPv6Any;
            }
 
            int received = Client.ReceiveFrom(m_Buffer, MaxUDPSize, 0 , ref tempRemoteEP);
            remoteEP = (IPEndPoint)tempRemoteEP;
 
 
            // because we don't return the actual length, we need to ensure the returned buffer
            // has the appropriate length.
 
            if (received < MaxUDPSize) {
                byte[] newBuffer = new byte[received];
                Buffer.BlockCopy(m_Buffer,0,newBuffer,0,received);
                return newBuffer;
            }
            return m_Buffer;
        }
 
        
        [HostProtection(ExternalThreading=true)]
        public IAsyncResult BeginReceive(AsyncCallback requestCallback, object state) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            // this is a fix due to the nature of the ReceiveFrom() call and the 
            // ref parameter convention, we need to cast an IPEndPoint to it's base
            // class EndPoint and cast it back down to IPEndPoint. ugly but it works.
            //
            EndPoint tempRemoteEP;
            
            if ( m_Family == AddressFamily.InterNetwork ) {
                tempRemoteEP = IPEndPoint.Any;
            }
            else {
                tempRemoteEP = IPEndPoint.IPv6Any;
            }
 
            return Client.BeginReceiveFrom(m_Buffer, 0, MaxUDPSize, SocketFlags.None , ref tempRemoteEP, requestCallback, state);
        }
 
 
        public byte[] EndReceive(IAsyncResult asyncResult, ref IPEndPoint remoteEP){
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            EndPoint tempRemoteEP;
            
            if ( m_Family == AddressFamily.InterNetwork ) {
                tempRemoteEP = IPEndPoint.Any;
            }
            else {
                tempRemoteEP = IPEndPoint.IPv6Any;
            }
 
            int received = Client.EndReceiveFrom(asyncResult,ref tempRemoteEP);
            remoteEP = (IPEndPoint)tempRemoteEP;
            
            // because we don't return the actual length, we need to ensure the returned buffer
            // has the appropriate length.
            
            if (received < MaxUDPSize) {
                byte[] newBuffer = new byte[received];
                Buffer.BlockCopy(m_Buffer,0,newBuffer,0,received);
                return newBuffer;
            }
            return m_Buffer;
        }
 
 
 
        ///     <devdoc>
        ///         <para>
        ///             Joins a multicast address group.
        ///         </para>
        ///     </devdoc>
        public void JoinMulticastGroup(IPAddress multicastAddr) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            if (multicastAddr==null){
                throw new ArgumentNullException("multicastAddr");
            }
 
            //
            // IPv6 Changes: we need to create the correct MulticastOption and
            //               must also check for address family compatibility
            //
            if ( multicastAddr.AddressFamily != m_Family ) {
                throw new ArgumentException(SR.GetString(SR.net_protocol_invalid_multicast_family, "UDP"), "multicastAddr");
            }
 
            if ( m_Family == AddressFamily.InterNetwork ) {
                MulticastOption mcOpt = new MulticastOption(multicastAddr);
 
                Client.SetSocketOption(
                    SocketOptionLevel.IP,
                    SocketOptionName.AddMembership,
                    mcOpt );
            }
            else {
                IPv6MulticastOption mcOpt = new IPv6MulticastOption(multicastAddr);
 
                Client.SetSocketOption(
                    SocketOptionLevel.IPv6,
                    SocketOptionName.AddMembership,
                    mcOpt );
            }
        }
 
 
 
        public void JoinMulticastGroup(IPAddress multicastAddr, IPAddress localAddress) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
        
            if ( m_Family != AddressFamily.InterNetwork ) {
                throw new SocketException(SocketError.OperationNotSupported);
            }
 
            MulticastOption mcOpt = new MulticastOption(multicastAddr,localAddress);
        
            Client.SetSocketOption(
               SocketOptionLevel.IP,
               SocketOptionName.AddMembership,
                mcOpt );
        }
 
 
 
        ///     <devdoc>
        ///         <para>
        ///             Joins an IPv6 multicast address group.
        ///         </para>
        ///     </devdoc>
        public void JoinMulticastGroup(int ifindex,IPAddress multicastAddr) {
            //
            // parameter validation
            //
            if ( m_CleanedUp ){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            if ( multicastAddr==null ) {
                throw new ArgumentNullException("multicastAddr");
            }
 
            if ( ifindex < 0 ) {
                throw new ArgumentException(SR.GetString(SR.net_value_cannot_be_negative), "ifindex");
            }
 
            //
            // Ensure that this is an IPv6 client, otherwise throw WinSock 
            // Operation not supported socked exception.
            //
            if ( m_Family != AddressFamily.InterNetworkV6 ) {
                throw new SocketException(SocketError.OperationNotSupported);
            }
 
            IPv6MulticastOption mcOpt = new IPv6MulticastOption(multicastAddr,ifindex);
 
            Client.SetSocketOption(
                SocketOptionLevel.IPv6,
                SocketOptionName.AddMembership,
                mcOpt );
        }
 
        /// <devdoc>
        ///     <para>
        ///         Joins a multicast address group with the specified time to live (TTL).
        ///     </para>
        /// </devdoc>
        public void JoinMulticastGroup(IPAddress multicastAddr, int timeToLive) {
            //
            // parameter validation;
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (multicastAddr==null){
                throw new ArgumentNullException("multicastAddr");
            }
            if (!ValidationHelper.ValidateRange(timeToLive, 0, 255)) {
                throw new ArgumentOutOfRangeException("timeToLive");
            }
 
            //
            // join the Multicast Group
            //
            JoinMulticastGroup(multicastAddr);
 
            //
            // set Time To Live (TLL)
            //
            Client.SetSocketOption(
                (m_Family == AddressFamily.InterNetwork) ? SocketOptionLevel.IP : SocketOptionLevel.IPv6,
                SocketOptionName.MulticastTimeToLive,
                timeToLive );
        }
 
        /// <devdoc>
        ///    <para>
        ///       Leaves a multicast address group.
        ///    </para>
        /// </devdoc>
        public void DropMulticastGroup(IPAddress multicastAddr) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            if (multicastAddr==null){
                throw new ArgumentNullException("multicastAddr");
            }
 
            //
            // IPv6 Changes: we need to create the correct MulticastOption and
            //               must also check for address family compatibility
            //
            if ( multicastAddr.AddressFamily != m_Family ) {
                throw new ArgumentException(SR.GetString(SR.net_protocol_invalid_multicast_family, "UDP"), "multicastAddr");
            }
 
            if ( m_Family == AddressFamily.InterNetwork ) {
                MulticastOption mcOpt = new MulticastOption(multicastAddr);
 
                Client.SetSocketOption(
                    SocketOptionLevel.IP,
                    SocketOptionName.DropMembership,
                    mcOpt );
            }
            else {
                IPv6MulticastOption mcOpt = new IPv6MulticastOption(multicastAddr);
 
                Client.SetSocketOption(
                    SocketOptionLevel.IPv6,
                    SocketOptionName.DropMembership,
                    mcOpt );
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Leaves an IPv6 multicast address group.
        ///    </para>
        /// </devdoc>
        public void DropMulticastGroup(IPAddress multicastAddr,int ifindex) {
            //
            // parameter validation
            //
            if (m_CleanedUp){
                throw new ObjectDisposedException(this.GetType().FullName);
            }
 
            if ( multicastAddr==null ) {
                throw new ArgumentNullException("multicastAddr");
            }
 
            if ( ifindex < 0 ) {
                throw new ArgumentException(SR.GetString(SR.net_value_cannot_be_negative), "ifindex");
            }
 
            //
            // Ensure that this is an IPv6 client, otherwise throw WinSock 
            // Operation not supported socked exception.
            //
            if ( m_Family != AddressFamily.InterNetworkV6 ) {
                throw new SocketException(SocketError.OperationNotSupported);
            }
 
            IPv6MulticastOption mcOpt = new IPv6MulticastOption(multicastAddr,ifindex);
 
            Client.SetSocketOption(
                SocketOptionLevel.IPv6,
                SocketOptionName.DropMembership,
                mcOpt );
        }
 
        //************* Task-based async public methods *************************
        [HostProtection(ExternalThreading = true)]
        public Task<int> SendAsync(byte[] datagram, int bytes)
        {
            return Task<int>.Factory.FromAsync(BeginSend, EndSend, datagram, bytes, null);
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<int> SendAsync(byte[] datagram, int bytes, IPEndPoint endPoint)
        {
            return Task<int>.Factory.FromAsync(BeginSend, EndSend, datagram, bytes, endPoint, null);
        }
 
        [HostProtection(ExternalThreading = true)]
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "hostname",
            Justification="Original parameter spelling is preserved for consistency")]
        public Task<int> SendAsync(byte[] datagram, int bytes, string hostname, int port)
        {
            return Task<int>.Factory.FromAsync((callback, state) => BeginSend(datagram, bytes, hostname, port, callback, state), EndSend, null);
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<UdpReceiveResult> ReceiveAsync()
        {
            return Task<UdpReceiveResult>.Factory.FromAsync((callback, state) => BeginReceive(callback, state), (ar)=>
                {
                    IPEndPoint remoteEP = null;
                    Byte[] buffer = EndReceive(ar, ref remoteEP);
                    return new UdpReceiveResult(buffer, remoteEP);
 
                }, null);
        }
 
 
        private void createClientSocket() {
            //
            // common initialization code
            //
            // IPv6 Changes: Use the AddressFamily of this class rather than hardcode.
            //
            Client = new Socket(m_Family, SocketType.Dgram, ProtocolType.Udp);
        }
 
    } // class UdpClient
 
 
 
} // namespace System.Net.Sockets