3

I am writing some kind of server application (in C++), which holds an fd_set of file descriptors of sockets the server is communicating with, and I wish to know when one of them is closed\shut down on the other side without having to read\write to it. I routinely block on select() (with no time out), waiting for read readiness and exceptional conditions of the two fd_set I supply it with.

I started out thinking that any socket being closed on the other side would appear in the exceptfds, but as I read on I came to understand that it is not necessarily the case. The select() man page does not really say much about what exceptions exceptfds is being watched for. Further more, to my understanding what is considered as an exceptional condition changes from OS to OS (I myself am using Ubnubt 11.10), and I have not found anywhere a reference to a socket being closed\shut down (on the other side) as an exception.

Also, the socket(7) man page mentions two kinds of signals that a socket may send:

  1. SIGPIPE - Is sent when writing into a shut down socket.
  2. SIGIO - Is sent when an I/O event occurs.

Unfortunately, it is not mentioned what constitutes as an I/O event, and anyway shutting down might not be considered as an I/O event by everyone (I myself am a good example).

So what I'm asking is - Can I make a socket either send a signal or insert itself into exceptfds when it is closed, or to actively prompt me in some other way?

Thanks, Shay

Community
  • 1
  • 1

5 Answers5

3

Usually, when a socket is closed by the remote peer, it becomes readable (for the purposes of select()), and when you read from it you get zero bytes back. This zero byte read indicates that the other end closed the socket normally. This is not considered an exceptional condition.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • In "usually", do you mean implementation-dependent, or when a proper close/shutdown call is made? Because I specifically want to get prompted when the client application got terminated without properly shutting down. Do socket closing done on program abrupt termination is identical to explicit system calls? – Shay Palachy Affek Jun 20 '12 at 21:08
  • I mean "usually" in the sense of a normal shutdown by the remote peer. Every Berkeley socket implementation I've used behaves this same way. One way to cause an abnormal close is to close a socket while there is still data left to read in the buffer. Depending on many factors, you may or may not be able to rely on the socket layer to determine if a client was terminated. It might be better to use an application level message for that (analogous to the SMTP "QUIT" command, for example). – Greg Hewgill Jun 20 '12 at 21:14
  • Thank you! Further research shows that the socket implementation I use behaves this way also, so I can now count on closed sockets popping up in readfds. Your answers really helped me out! – Shay Palachy Affek Jun 22 '12 at 07:55
  • 2
    I'm working on ios, and in here it seems that the sockets do not indicates themself as readable. – Itay Levin Dec 26 '12 at 17:46
2

Within my own applications, when my select() call (for read events) returns on my file descriptor I call ioctl() to see how much is there on the buffer for reading. If there are zero bytes, then I use that to indicate the connection is closed.

I actually found this out when I started writing linux sockets from this answer here at stack overflow

Community
  • 1
  • 1
mathematician1975
  • 21,161
  • 6
  • 59
  • 101
  • Why not just use the return value of `recv()`, since you'll need to call that anyway? That's one fewer kernel call. – Greg Hewgill Jun 19 '12 at 20:23
  • @GregHewgill You make a good point here. I read that answer here on SO when I very first started linux sockets, and just assumed it was the best thing to do. I won't delete this answer though as the OP actually specifies that he wants to detect it without read/write of the socket. Personally though I will be changing my code to how you suggest it at work tomorrow morning as it makes a lot more sense. Thanks for that. – mathematician1975 Jun 19 '12 at 20:28
  • Hmm. Well, the whole issue is that I am not sure whether closed sockets will appear on readfs on closing. If they do, I'll read them anyway, and then close them when I get -1 back from recv(). In my question, I meant that I don't want to cycle through the whole set of sockets and actively read socket that were not prompted as read-ready, just to make sure they didn't get close without notifying me (logging off my server). – Shay Palachy Affek Jun 20 '12 at 21:13
0

You can also abstract this event among many others using an 'adapter'.

From one of my projects for instance...

    #region Copyright
    /*
    This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/      
 */
#endregion

namespace Media.Sockets
{
    #region NetworkConnection

    /// <summary>
    /// Represents a <see cref="Connection"/> specific to the Network.
    /// </summary>
    public class NetworkConnection : Connection, Common.ISocketReference
    {
        #region NetworkConnectionState

        [System.Flags]
        protected enum NetworkConnectionState : long
        {
            None = 0,
            Initialized = 1,
            Bound = 2,
            Connected = 4,
        }

        #endregion

        #region Fields

        /// <summary>
        /// Created in <see cref="CreateWaitHandle"/>, Disposed in <see cref="Dispose"/>.
        /// </summary>
        Common.Extensions.WaitHandle.DisposableWaitHandle WaitHandle;

        /// <summary>
        /// The date and time when the Connection was started.
        /// </summary>
        protected System.DateTime LasRemoteConnectionStartedDateTime;

        /// <summary>
        /// The date and time when the Connection was started.
        /// </summary>
        protected System.DateTime LastRemoteConnectionCompletedDateTime;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the amount of time taken to connect to the <see cref="RemoteEndPoint"/>
        /// </summary>
        public System.TimeSpan RemoteConnectionTime { get { return LastRemoteConnectionCompletedDateTime - LasRemoteConnectionStartedDateTime; } }

        /// <summary>
        /// The <see cref="System.Net.NetworkInformation.NetworkInterface"/> assoicated with the NetworkConnection.
        /// </summary>
        public System.Net.NetworkInformation.NetworkInterface NetworkInterface { get; protected set; }

        /// <summary>
        /// The <see cref="System.Net.Sockets.Socket"/> assoicated with the NetworkConnection.
        /// </summary>
        public System.Net.Sockets.Socket ConnectionSocket { get; protected set; }

        /// <summary>
        /// Indicates if the NetworkConnection has a <see cref="NetworkInterface"/> which is not null.
        /// </summary>
        public bool HasNetworkInterface { get { return NetworkInterface != null; } }

        /// <summary>
        /// The <see cref="System.Net.EndPoint"/> from which this NetworkConnection connects to the <see cref="RemoteEndPoint"/>
        /// </summary>
        public System.Net.EndPoint LocalEndPoint { get; protected set; }

        /// <summary>
        /// Indicates if this NetworkConnection has a <see cref="LocalEndPoint"/> which is not null.
        /// </summary>
        public bool HasLocalEndPoint { get { return LocalEndPoint != null; } }

        /// <summary>
        /// The <see cref="System.Net.EndPoint"/> from which this NetworkConnection is connected to via the <see cref="LocalEndPoint"/>
        /// </summary>
        public System.Net.EndPoint RemoteEndPoint { get; protected set; }

        /// <summary>
        /// Indicates if this NetworkConnection has a <see cref="RemoteEndPoint"/> which is not null.
        /// </summary>
        public bool HasRemoteEndPoint { get { return RemoteEndPoint != null; } }

        /// <summary>
        /// Indicates the <see cref="NetworkConnectionState"/> assoicated with this NetworkConnection
        /// </summary>
        protected NetworkConnectionState NetworkConnectionFlags
        {
            get { return (NetworkConnectionState)Flags; }
            set { Flags = (long)value; }
        }

        #endregion

        #region Constructor

        public NetworkConnection()
            : base() { }

        public NetworkConnection(string name, bool shouldDispose)
            : base(name, shouldDispose) { }

        public NetworkConnection(System.Net.Sockets.Socket existingSocket, bool ownsHandle, bool shouldDispose)
            : this("System.Net.Socket-" + ownsHandle.ToString(), shouldDispose)
        {
            if (existingSocket == null) throw new System.ArgumentNullException("existingSocket");

            //Assign the ConnectionSocket
            ConnectionSocket = existingSocket;

            //Flag Initialized.
            FlagInitialized();

            //Assign the NetworkInterface
            NetworkInterface = Common.Extensions.NetworkInterface.NetworkInterfaceExtensions.GetNetworkInterface(ConnectionSocket);

            //Create a WaitHandle 
            CreateWaitHandle(ConnectionSocket.Handle, ownsHandle);

            //Check IsBound
            if (ConnectionSocket.IsBound)
            {
                //Flag Bound.
                FlagBound();

                //Serialize and Assign LocalEndPoint
                LocalEndPoint = existingSocket.LocalEndPoint;
            }

            //Check Connected
            if (ConnectionSocket.Connected)
            {
                //Sample the clock
                LasRemoteConnectionStartedDateTime = System.DateTime.UtcNow;

                //Serialize and Assign RemoteEndPoint
                RemoteEndPoint = existingSocket.RemoteEndPoint;

                //Call Connect to FlagConnected and call base logic.
                Connect();

                //Sample the clock
                LastRemoteConnectionCompletedDateTime = System.DateTime.UtcNow;
            }
        }

        #endregion

        #region NetworkChange Event Handlers

        void NetworkChange_NetworkAvailabilityChanged(object sender, System.Net.NetworkInformation.NetworkAvailabilityEventArgs e)
        {
            Refresh();
        }

        void NetworkChange_NetworkAddressChanged(object sender, System.EventArgs e)
        {
            Refresh();
        }

        #endregion

        #region Bound

        protected void FlagBound()
        {
            //Indicate Bound
            Flags |= (long)NetworkConnectionState.Bound;
        }

        protected void UnFlagBound()
        {
            //Indicate not Bound
            Flags &= (long)NetworkConnectionState.Bound;
        }

        #endregion

        #region Initialize

        protected void FlagInitialized()
        {
            //Indicate Connected
            Flags |= (long)NetworkConnectionState.Initialized;
        }

        protected void UnFlagInitialized()
        {
            //Indicate Not Connected
            Flags &= (long)NetworkConnectionState.Initialized;
        }

        public virtual void Initialize(bool registerForEvents)
        {
            //Check not already Initialized.
            if (false == NetworkConnectionFlags.HasFlag(NetworkConnectionState.Initialized))
            {
                //Indicate Initialized
                FlagInitialized();

                if (registerForEvents)
                {
                    //Attach events
                    System.Net.NetworkInformation.NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

                    System.Net.NetworkInformation.NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
                }
            }
        }

        #endregion

        #region Refresh

        public override void Refresh()
        {
            base.Refresh();
        }

        #endregion

        #region CreateConnectionSocket

        public void CreateConnectionSocket(System.Net.Sockets.AddressFamily addressFamily, System.Net.Sockets.SocketType socketType, System.Net.Sockets.ProtocolType protocolType)
        {
            if (ConnectionSocket == null)
            {
                ConnectionSocket = new System.Net.Sockets.Socket(addressFamily, socketType, protocolType);

                CreateWaitHandle(ConnectionSocket.Handle, true);
            }
        }

        #endregion

        #region CreateWaitHandle

        public void CreateWaitHandle(System.IntPtr handle, bool ownsHandle)
        {
            if (WaitHandle == null)
            {
                WaitHandle = new Common.Extensions.WaitHandle.DisposableWaitHandle(handle, ownsHandle);
            }
        }

        #endregion

        #region Connect

        protected void FlagConnected()
        {
            //Indicate Connected
            Flags |= (long)NetworkConnectionState.Connected;
        }

        protected void UnFlagConnected()
        {
            //Indicate Not Connected
            Flags &= (long)NetworkConnectionState.Connected;
        }

        public override void Connect()
        {
            //Check not already Connected.
            if (false == NetworkConnectionFlags.HasFlag(NetworkConnectionState.Connected))
            {
                //Check IsEstablished
                if (IsEstablished) return;

                if (NetworkInterface == null) throw new System.InvalidOperationException("NetworkInterface must be assigned before calling Connect.");

                if (LocalEndPoint == null) throw new System.InvalidOperationException("LocalEndPoint must be assigned before calling Connect.");

                if (RemoteEndPoint == null) throw new System.InvalidOperationException("RemoteEndPoint must be assigned before calling Connect.");

                //Set established
                base.Connect();

                //Indicate Connected
                FlagConnected();

                //Refresh the connection
                Refresh();
            }
        }

        /// <summary>
        /// Creates the <see cref="CreateConnectionSocket"/> using the specified options and connects the socket.
        /// Assigns <see cref="LocalEndPoint"/> and <see cref="RemoteEndPoint"/>
        /// </summary>
        /// <param name="addressFamily"></param>
        /// <param name="socketType"></param>
        /// <param name="protocolType"></param>
        /// <param name="addressList"></param>
        /// <param name="port"></param>
        public virtual void Connect(System.Net.Sockets.AddressFamily addressFamily, System.Net.Sockets.SocketType socketType, System.Net.Sockets.ProtocolType protocolType, System.Net.IPAddress[] addressList, int port)
        {
            try
            {
                //Create the socket
                CreateConnectionSocket(addressFamily, socketType, protocolType);

                //Sample the clock
                LasRemoteConnectionStartedDateTime = System.DateTime.UtcNow;

                //Connect the socket
                ConnectionSocket.Connect(addressList, port);

                //Sample the clock
                LastRemoteConnectionCompletedDateTime = System.DateTime.UtcNow;
            }
            finally
            {
                //Assign the NetworkInterface
                NetworkInterface = Common.Extensions.NetworkInterface.NetworkInterfaceExtensions.GetNetworkInterface(ConnectionSocket);

                //Assign the LocalEndPoint
                LocalEndPoint = (System.Net.IPEndPoint)ConnectionSocket.LocalEndPoint;

                //Assign the RemoteEndPoint
                RemoteEndPoint = (System.Net.IPEndPoint)ConnectionSocket.RemoteEndPoint;

                //Call Connect to FlagConnected and call base logic.
                Connect();
            }
        }

        #endregion

        #region Disconnect

        public virtual void Disconnect(bool allowReuse = false)
        {
            //Check not already Connected.
            if (((NetworkConnectionState)Flags).HasFlag(NetworkConnectionState.Connected))
            {
                ConnectionSocket.Disconnect(allowReuse);

                base.Disconnect();

                UnFlagConnected();

                Refresh();
            }
        }

        public override void Disconnect()
        {
            Disconnect(false);
        }

        #endregion

        #region Dispose

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            using (WaitHandle)
            {
                System.Net.NetworkInformation.NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;

                System.Net.NetworkInformation.NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged;

                ConnectionSocket = null;

                LocalEndPoint = RemoteEndPoint = null;

                NetworkInterface = null;
            }
        }

        #endregion

        System.Collections.Generic.IEnumerable<System.Net.Sockets.Socket> Common.ISocketReference.GetReferencedSockets()
        {
            yield return ConnectionSocket;
        }
    }

    #endregion
}

If you need 'Connection' base class you can reference this

#region Copyright
/*
This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/

 Julius.Friedman@gmail.com / (SR. Software Engineer ASTI Transportation Inc. http://www.asti-trans.com)   
 * v//
 */
#endregion


namespace Media.Sockets
{
    #region Connection

    /// <summary>
    /// Provides a base class to facilitate the concept of a Connection.
    /// </summary>
    public abstract class Connection : Common.BaseDisposable
    {
        #region Statics

        /// <summary>
        /// A string with the format of:
        ///     `TypeName Id Flags Name`
        /// </summary>
        const string FormatString = "{0} {1} ({2}) {3}";

        #endregion

        #region Properties

        /// <summary>
        /// The unique identifier assoicated with this instance.
        /// </summary>
        public readonly System.Guid Id = System.Guid.NewGuid();

        /// <summary>
        /// The date and time the instance was created.
        /// </summary>
        public readonly System.DateTime Created = System.DateTime.UtcNow;

        /// <summary>
        /// The name assigned to this instance.
        /// </summary>
        public readonly string Name;

        /// <summary>
        /// Indicates if <see cref="Disconnect"/> will be called when disposing.
        /// </summary>
        public bool IsPersistent
        {
            get
            {
                return ShouldDispose == false;
            }
            set
            {
                ShouldDispose = value == false;
            }
        }

        /// <summary>
        /// Provided for derived implementations
        /// </summary>
        protected long Flags { get; set; }

        /// <summary>
        /// Indicates if the Connection is established.
        /// </summary>
        public virtual bool IsEstablished { get; protected set; }

        /// <summary>
        /// The date and time the Connection was established.
        /// </summary>
        public System.DateTime EstablishedDateTime { get; protected set; }

        /// <summary>
        /// The amount of time the connection has been established.
        /// </summary>
        public System.TimeSpan TimeEstablished { get { return IsEstablished ? System.DateTime.UtcNow - EstablishedDateTime : System.TimeSpan.Zero; } }

        #endregion

        #region Connect

        /// <summary>
        /// If <see cref="IsDisposed"/> is false, Sets <see cref="IsEstablished"/> to true.
        /// </summary>
        public virtual void Connect()
        {
            if (IsDisposed) return;

            EstablishedDateTime = System.DateTime.UtcNow;

            IsEstablished = true;
        }

        #endregion

        #region Disconnect

        /// <summary>
        /// If <see cref="IsDisposed"/> is false, Sets <see cref="IsEstablished"/> to false.
        /// </summary>
        public virtual void Disconnect()
        {
            if (IsDisposed) return;

            IsEstablished = false;
        }

        #endregion

        #region Refresh

        /// <summary>
        /// Refreshes the details of the Connection.
        /// Throws a <see cref="System.ObjectDisposedException"/> if <see cref="IsDisposed"/> is true.
        /// </summary>
        public virtual void Refresh()
        {
            CheckDisposed();
        }

        #endregion

        #region Dispose

        /// <summary>
        /// If <see cref="IsDisposed"/> is True the call returns immediately.
        /// Calls <see cref="Disconnect"/> if <see cref="IsPersistent"/> is False and calls <see cref="Common.BaseDisposable.Dispose"/>
        /// </summary>
        public override void Dispose()
        {
            if (IsDisposed) return;

            if (false == IsPersistent) Disconnect();

            base.Dispose();
        }

        #endregion

        #region Constructor

        public Connection(string name, bool shouldDispose)
            : base(shouldDispose)
        {
            Name = name;
        }

        public Connection()
            : this(string.Empty, true) { }

        #endregion

        #region ToString

        public override string ToString()
        {
            return string.Format(FormatString, GetType().Name.ToString(), Id.ToString(), Flags, Name);
        }

        #endregion

    }

    #endregion
}
Jay
  • 3,276
  • 1
  • 28
  • 38
0

You can reference this code for an example of the IPNetworkConnection which implemented the other classes.

#region Copyright
/*
This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/      
 * v//
 */
#endregion

namespace Media.Sockets
{
#region IPNetworkConnection

/// <summary>
/// Represents a <see cref="NetworkConnection"/> which utilizes the IP Protocol.
/// </summary>
public class IPNetworkConnection : NetworkConnection
{
    #region Statics

    public static System.Net.NetworkInformation.IPGlobalProperties IPGlobalProperties
    {
        get { return System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties(); }
    }

    #region CreateIPHostEntry

    public static System.Net.IPHostEntry CreateIPHostEntry(System.Net.IPAddress address, string hostName, params string[] aliases)
    {
        return CreateIPHostEntry(Common.Extensions.Object.ObjectExtensions.ToArray<System.Net.IPAddress>(address),
            hostName,
            aliases);
    }

    public static System.Net.IPHostEntry CreateIPHostEntry(string hostName, params System.Net.IPAddress[] address)
    {
        return CreateIPHostEntry(address, hostName, null);
    }

    public static System.Net.IPHostEntry CreateIPHostEntry(System.Net.IPAddress[] addresses, string hostName, params string[] aliases)
    {
        return new System.Net.IPHostEntry()
        {
            AddressList = Common.Extensions.Array.ArrayExtensions.IsNullOrEmpty(addresses) ? Common.Extensions.Object.ObjectExtensions.ToArray<System.Net.IPAddress>(System.Net.IPAddress.None) : addresses,
            Aliases = Common.Extensions.Array.ArrayExtensions.IsNullOrEmpty(aliases) ? Common.Extensions.Object.ObjectExtensions.ToArray<string>(string.Empty) : aliases,
            HostName = hostName ?? string.Empty
        };
    }

    #endregion

    #endregion

    #region Properties

    /// <summary>
    /// Gets the <see cref="System.Net.NetworkInformation.IPInterfaceProperties"/> assoicated with the <see cref="NetworkInterface"/>
    /// </summary>
    public System.Net.NetworkInformation.IPInterfaceProperties IPInterfaceProperties
    {
        get { return HasNetworkInterface ? NetworkInterface.GetIPProperties() : null; }
    }

    /// <summary>
    /// Indicates if the <see cref="RemoteIPEndPoint"/> has a <see cref="System.Net.NetworkInformation.NetworkInterface"/> on this computer.
    /// </summary>
    public bool IsLocalConnection { get { return HasRemoteIPEndPoint && Common.Extensions.NetworkInterface.NetworkInterfaceExtensions.GetNetworkInterface(RemoteIPEndPoint) != null; } }

    /// <summary>
    /// Indicates if the <see cref="RemoteIPEndPoint"/> is from within the same network as this computer.
    /// </summary>
    public bool IsIntranetConnection
    {
        get { return false == IsLocalConnection && Common.Extensions.IPAddress.IPAddressExtensions.IsOnIntranet(RemoteIPEndPoint.Address); }
    }

    #region Local        

    /// <summary>
    /// The <see cref="System.Net.IPHostEntry"/> assoicated with the <see cref="LocalIPEndPoint"/>
    /// </summary>
    public System.Net.IPHostEntry LocalIPHostEntry { get; protected set; }

    /// <summary>
    /// Indicates if the <see cref="LocalIPHostEntry"/> is not null.
    /// </summary>
    public bool HasLocalIPHostEntry { get { return LocalIPHostEntry != null; } }

    /// <summary>
    /// Gets or sets the <see cref="LocalEndPoint"/>.
    /// 
    /// If the <see cref="LocalEndPoint"/> is not a <see cref="System.Net.IPEndPoint"/> a <see cref="System.InvalidOperationException"/> will be thrown.
    /// </summary>
    public System.Net.IPEndPoint LocalIPEndPoint
    {
        get { return (System.Net.IPEndPoint)LocalEndPoint; }
        set
        {
            if (false == LocalEndPoint is System.Net.IPEndPoint) throw new System.InvalidOperationException("LocalEndPoint is not a System.Net.IPEndPoint");

            LocalEndPoint = value;
        }
    }

    /// <summary>
    /// Indicates if the <see cref="LocalIPEndPoint"/> is not null.
    /// </summary>
    public bool HasLocalIPEndPoint { get { return LocalIPEndPoint != null; } }

    #endregion

    #region Dhcp

    /// <summary>
    /// Gets the <see cref="System.Net.NetworkInformation.IPAddressCollection"/> assoicated with the <see cref="IPInterfaceProperties"/>
    /// </summary>
    public virtual System.Net.NetworkInformation.IPAddressCollection DhcpServerAddresses
    {
        get
        {
            return IPInterfaceProperties.DhcpServerAddresses;
        }
    }

    /// <summary>
    /// Indicates if the IPNetworkConnection utilized Dhcp
    /// </summary>
    public bool UsesDhcp
    {
        get
        {
            return DhcpServerAddresses.Count > 0; /* && DhcpLeaseLifetime != System.TimeSpan.MinValue;*/
        }
    }

    //Could make a Superset class of to unify paths..
    //System.Net.NetworkInformation.IPAddressInformationCollection ipAddressCollection;

    /// <summary>
    /// If <see cref="UsesDhcp"/> the amount of time of the IPAddress is leased according the <see cref="System.Net.NetworkInformation.IPAddressInformation"/> assoicated with the <see cref="LocalIPEndPoint"/>.
    /// 
    /// If the <see cref="LocalIPEndPoint"/> is not found in the leased <see cref="System.Net.NetworkInformation.IPAddressInformation"/> then <see cref="Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan"/> is returned.
    /// </summary>
    public System.TimeSpan DhcpLeaseLifetime
    {
        get
        {
            //If there is no Dhcp server the DhcpLeaveLifeTime is 0
            if (false == UsesDhcp) return System.TimeSpan.Zero;

            //Check Multicast if the address IsMulticast
            if (Common.Extensions.IPAddress.IPAddressExtensions.IsMulticast(LocalIPEndPoint.Address))
            {
                System.Net.NetworkInformation.MulticastIPAddressInformationCollection multicastIPAddressInformationCollection = IPInterfaceProperties.MulticastAddresses;

                foreach (System.Net.NetworkInformation.MulticastIPAddressInformation multicastIPAddressInformation in multicastIPAddressInformationCollection)
                {
                    if (multicastIPAddressInformation.Address.Equals(LocalIPEndPoint.Address))
                    {
                        return System.TimeSpan.FromSeconds(multicastIPAddressInformation.DhcpLeaseLifetime);
                    }
                }
            }
            else //Check Unicast otherwise
            {
                System.Net.NetworkInformation.UnicastIPAddressInformationCollection unicastIPAddressInformationCollection = IPInterfaceProperties.UnicastAddresses;

                foreach (System.Net.NetworkInformation.UnicastIPAddressInformation unicastIPAddressInformation in unicastIPAddressInformationCollection)
                {
                    if (unicastIPAddressInformation.Address.Equals(LocalIPEndPoint.Address))
                    {
                        return System.TimeSpan.FromSeconds(unicastIPAddressInformation.DhcpLeaseLifetime);
                    }
                }
            }

            //Could not find a an IPAddress which matched the LocalIPEndPoint, indicate infinite timeout.
            return Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan;
        }
    }

    /// <summary>
    /// If <see cref="UsesDhcp"/> Gets the number of seconds remaining during which this address is valid.
    /// 
    /// If the <see cref="LocalIPEndPoint"/> is not found in the leased <see cref="System.Net.NetworkInformation.IPAddressInformation"/> then <see cref="Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan"/> is returned.
    /// </summary>
    public System.TimeSpan DhcpAddressValidLifetime
    {
        get
        {
            //If there is no Dhcp server the DhcpLeaveLifeTime is 0
            if (false == UsesDhcp) return System.TimeSpan.Zero;

            //Check Multicast if the address IsMulticast
            if (Common.Extensions.IPAddress.IPAddressExtensions.IsMulticast(LocalIPEndPoint.Address))
            {
                System.Net.NetworkInformation.MulticastIPAddressInformationCollection multicastIPAddressInformationCollection = IPInterfaceProperties.MulticastAddresses;

                foreach (System.Net.NetworkInformation.MulticastIPAddressInformation multicastIPAddressInformation in multicastIPAddressInformationCollection)
                {
                    if (multicastIPAddressInformation.Address.Equals(LocalIPEndPoint.Address))
                    {
                        return System.TimeSpan.FromSeconds(multicastIPAddressInformation.AddressValidLifetime);
                    }
                }
            }
            else //Check Unicast otherwise
            {
                System.Net.NetworkInformation.UnicastIPAddressInformationCollection unicastIPAddressInformationCollection = IPInterfaceProperties.UnicastAddresses;

                foreach (System.Net.NetworkInformation.UnicastIPAddressInformation unicastIPAddressInformation in unicastIPAddressInformationCollection)
                {
                    if (unicastIPAddressInformation.Address.Equals(LocalIPEndPoint.Address))
                    {
                        return System.TimeSpan.FromSeconds(unicastIPAddressInformation.AddressValidLifetime);
                    }
                }
            }

            //Could not find a an IPAddress which matched the LocalIPEndPoint, indicate infinite timeout.
            return Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan;
        }
    }

    /// <summary>
    /// If <see cref="UsesDhcp"/> Gets the number of seconds remaining during which this address is the preferred address.
    /// 
    /// If the <see cref="LocalIPEndPoint"/> is not found in the leased <see cref="System.Net.NetworkInformation.IPAddressInformation"/> then <see cref="Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan"/> is returned.
    /// </summary>
    public System.TimeSpan DhcpAddressPreferredLifetime
    {
        get
        {
            //If there is no Dhcp server the DhcpLeaveLifeTime is 0
            if (false == UsesDhcp) return System.TimeSpan.Zero;

            //Check Multicast if the address IsMulticast
            if (Common.Extensions.IPAddress.IPAddressExtensions.IsMulticast(LocalIPEndPoint.Address))
            {
                System.Net.NetworkInformation.MulticastIPAddressInformationCollection multicastIPAddressInformationCollection = IPInterfaceProperties.MulticastAddresses;

                foreach (System.Net.NetworkInformation.MulticastIPAddressInformation multicastIPAddressInformation in multicastIPAddressInformationCollection)
                {
                    if (multicastIPAddressInformation.Address.Equals(LocalIPEndPoint.Address))
                    {
                        return System.TimeSpan.FromSeconds(multicastIPAddressInformation.AddressPreferredLifetime);
                    }
                }
            }
            else //Check Unicast otherwise
            {
                System.Net.NetworkInformation.UnicastIPAddressInformationCollection unicastIPAddressInformationCollection = IPInterfaceProperties.UnicastAddresses;

                foreach (System.Net.NetworkInformation.UnicastIPAddressInformation unicastIPAddressInformation in unicastIPAddressInformationCollection)
                {
                    if (unicastIPAddressInformation.Address.Equals(LocalIPEndPoint.Address))
                    {
                        return System.TimeSpan.FromSeconds(unicastIPAddressInformation.AddressPreferredLifetime);
                    }
                }
            }

            //Could not find a an IPAddress which matched the LocalIPEndPoint, indicate infinite timeout.
            return Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan;
        }
    }

    #endregion

    #region Remote

    /// <summary>
    /// Provides information about the <see cref="RemoteEndPoint"/>.Address
    /// </summary>
    public System.Net.NetworkInformation.IPAddressInformation RemoteAddressInformation { get; protected set; }

    /// <summary>
    /// Indicates if the <see cref="RemoteAddressInformation"/> is not null.
    /// </summary>
    public bool HasRemoteAddressInformation { get { return RemoteAddressInformation != null; } }

    /// <summary>
    /// The <see cref="System.Net.IPHostEntry"/> assoicated with the <see cref="RemoteIPEndPoint"/>
    /// </summary>
    public System.Net.IPHostEntry RemoteIPHostEntry { get; protected set; }

    /// <summary>
    /// Indicates if the <see cref="RemoteIPHostEntry"/> is not null.
    /// </summary>
    public bool HasRemoteIPHostEntry { get { return RemoteIPHostEntry != null; } }

    /// <summary>
    /// Gets or sets the <see cref="RemoteEndPoint"/>.
    /// 
    /// If the <see cref="RemoteEndPoint"/> is not a <see cref="System.Net.IPEndPoint"/> a <see cref="System.InvalidOperationException"/> will be thrown.
    /// </summary>
    public System.Net.IPEndPoint RemoteIPEndPoint
    {
        get { return (System.Net.IPEndPoint)RemoteEndPoint; }
        set
        {
            if (false == RemoteEndPoint is System.Net.IPEndPoint) throw new System.InvalidOperationException("RemoteEndPoint is not a System.Net.IPEndPoint");

            RemoteEndPoint = value;
        }
    }

    /// <summary>
    /// Indicates if the <see cref="RemoteEndPoint"/> is a <see cref="System.Net.IPEndPoint"/>
    /// </summary>
    public bool HasRemoteIPEndPoint { get { return RemoteIPEndPoint != null; } }

    #endregion

    #endregion

    #region Constructor

    /// <summary>
    /// Creates a new NetworkConnection with the given.
    /// </summary>
    /// <param name="remoteIPHostEntry">given</param>
    public IPNetworkConnection(System.Net.IPHostEntry remoteIPHostEntry)
        : base()
    {
        if (remoteIPHostEntry == null) throw new System.ArgumentNullException("remoteIPHostEntry");

        RemoteIPHostEntry = remoteIPHostEntry;            
    }

    /// <summary>
    /// Creates a new NetworkConnection by resolving the given using <see cref="System.Net.Dns.GetHostEntry"/>
    /// </summary>
    /// <param name="hostNameOrAddress">given</param>
    public IPNetworkConnection(string hostNameOrAddress) :
        this(System.Net.Dns.GetHostEntry(hostNameOrAddress))
    {
        RemoteAddressInformation = new IPAddressInformation(System.Net.IPAddress.None, true, false);
    }

    /// <summary>
    /// Creates a new NetworkConnection and <see cref="new System.Net.IPHostEntry"/> using the given address.
    /// </summary>
    /// <param name="address">The address</param>
    public IPNetworkConnection(System.Net.IPAddress address) :
        this(CreateIPHostEntry(string.Empty, address))
    {
        RemoteAddressInformation = new IPAddressInformation(System.Net.IPAddress.None, false, false);
    }

    public IPNetworkConnection(System.Uri uri) : this(uri.DnsSafeHost) { }

    #endregion

    #region Refresh

    /// <summary>
    /// If <see cref="HasNetworkInterface"/> is True And <see cref="HasLocalIPEndPoint"/> then <see cref="NetworkInterface"/> is updated using <see cref="Common.Extensions.NetworkInterface.NetworkInterfaceExtensions.GetNetworkInterface"/>
    /// </summary>
    public void RefreshNetworkInterface()
    {
        if (HasNetworkInterface && HasLocalIPEndPoint)
        {                
            NetworkInterface = Common.Extensions.NetworkInterface.NetworkInterfaceExtensions.GetNetworkInterface(LocalIPEndPoint);
        }
    }

    /// <summary>
    /// If <see cref="HasRemoteAddressInformation"/> is True And <see cref="RemoteAddressInformation.IsDnsEligible"/> then the <see cref="RemoteIPHostEntry"/> is updated using <see cref="System.Net.Dns.GetHostEntry"/>
    /// </summary>
    public void RefreshRemoteIPHostEntry()
    {
        if (HasRemoteAddressInformation && RemoteAddressInformation.IsDnsEligible)
        {
            RemoteIPHostEntry = System.Net.Dns.GetHostEntry(RemoteIPEndPoint.Address);
        }
    }

    public override void Refresh()
    {
        if (IsDisposed) return;

        base.Refresh();

        RefreshNetworkInterface();

        RefreshRemoteIPHostEntry();
    }

    #endregion

    #region Connect

    public void Connect(int addressIndex, System.Net.NetworkInformation.NetworkInterface networkInterface, int port = 0)
    {
        if (ConnectionSocket == null) throw new System.InvalidOperationException("There must be a ConnectionSocket assigned before calling Connect.");

        if (addressIndex < 0) throw new System.IndexOutOfRangeException("addressIndex must be > 0 and < HostEntry.AddressList.Length");

        if (networkInterface == null) throw new System.ArgumentNullException("networkInterface");

        NetworkInterface = networkInterface;

        RemoteEndPoint = new System.Net.IPEndPoint(RemoteIPHostEntry.AddressList[addressIndex], port);

        Connect();

        LocalEndPoint = ConnectionSocket.LocalEndPoint;

        RemoteAddressInformation = new IPAddressInformation(RemoteIPEndPoint.Address, RemoteAddressInformation.IsDnsEligible, RemoteAddressInformation.IsTransient);
    }

    #endregion

    #region Dispose

    public override void Dispose()
    {
        base.Dispose();

        RemoteIPHostEntry = null;

        LocalEndPoint = RemoteEndPoint = null;

        NetworkInterface = null;
    }

    #endregion
}

#endregion
}
Jay
  • 3,276
  • 1
  • 28
  • 38
0

This is only one of many possible implementations and should also outline how one would create a 'Translator' and subsequently a 'TranslatedConnection'. One could obviously (or not so) outline Send and Receive methods either within the base class as well as within an interface to allow cross network communication if desired.

The class 'Connection' defines a suitable base for any type of connection. 'NetworkConnection' adds requirements for the Network, finally an 'IPNetworkConnection' to implement the logic required for the IP Protocol. This can also be extended to 'TcpNetworkConnection' and then to 'TcpIpNetworkConnection' or in any other way desired for instance 'SerialConnection' or 'EthernetConnection' and then build a class to allow the cross media communication e.g. 'SerialToEthernetNetworkConnection' inter alia.

For example:

 #region Copyright
/*
This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/

 Julius.Friedman@gmail.com / (SR. Software Engineer ASTI Transportation Inc. http://www.asti-trans.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:
 * 
 * 
 * JuliusFriedman@gmail.com should be contacted for further details.

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.
 * 
 * v//
 */
#endregion

namespace Media.Sockets
{
    #region TcpNetworkConnection

    public class TcpNetworkConnection : NetworkConnection
    {
        #region Statics

        public static System.Net.NetworkInformation.TcpConnectionInformation[] TcpConnectionInformation
        {
            get { return IPNetworkConnection.IPGlobalProperties.GetActiveTcpConnections(); }
        }

        #endregion

        public TcpNetworkConnection(string name, bool shouldDispose) : base(name, shouldDispose) { }
    }

    #endregion
}

#region Copyright
/*
This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/

 Julius.Friedman@gmail.com / (SR. Software Engineer ASTI Transportation Inc. http://www.asti-trans.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:
 * 
 * 
 * JuliusFriedman@gmail.com should be contacted for further details.

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.
 * 
 * v//
 */
#endregion

namespace Media.Sockets
{
    #region TcpNetworkConnection

    public class TcpIPNetworkConnection : IPNetworkConnection
    {
        #region Statics

        public static System.Net.NetworkInformation.TcpConnectionInformation[] TcpConnectionInformation
        {
            get { return IPNetworkConnection.IPGlobalProperties.GetActiveTcpConnections(); }
        }

        #endregion

        public TcpIPNetworkConnection() : base(string.Empty) { }
    }

    #endregion
}

Are both valid implementations but serve different purposes.

Jay
  • 3,276
  • 1
  • 28
  • 38