套接字关闭时能否发送信号?

3
我正在编写一种服务器应用程序(使用C++),它持有一个文件描述符集合fd_set,其中包含服务器正在通信的套接字的文件描述符,并且我希望在无需读取\写入时就能知道另一端关闭\关闭其中之一。我通常会在select()上阻塞(没有超时),等待我提供的两个fd_set的读就绪和异常情况。
我最初认为任何在另一侧关闭的套接字都会出现在exceptfds中,但随着我继续阅读,我了解到这并不一定是这种情况。select() man page并没有真正说明要监视哪些异常情况。此外,据我所知,被认为是异常条件因操作系统而异(我自己使用的是Ubnubt 11.10),我没有找到任何关于套接字被关闭\关闭(在另一侧)作为异常的参考资料。
此外,socket(7) man page提到套接字可能会发送两种类型的信号:
  1. SIGPIPE - 当写入已关闭的套接字时发送。
  2. SIGIO - 当发生I/O事件时发送。
不幸的是,并未说明何为I/O事件,而关闭可能并非每个人都认为是I/O事件(我自己就是很好的例子)。
因此,我的问题是-当套接字关闭时,我能否让它发送信号或将其插入到exceptfds中,或以其他方式主动提示我?
谢谢, Shay
5个回答

3
通常情况下,当远程对等方关闭套接字时,它变为可读(用于select()),当您从中读取时,会返回零字节。这个零字节读取表示另一端正常关闭了套接字。这不被视为异常情况。

“通常情况下”,您是指实现相关,还是指在进行适当的关闭/关闭调用时?因为我特别想在客户端应用程序未正确关闭时得到提示。在程序突然终止时执行套接字关闭是否与显式系统调用相同? - ShayPal5
我的意思是“通常”指远程对等方通过正常关闭而言。我使用过的每个伯克利套接字实现都是这样的。导致异常关闭的一种方法是在仍有数据需要读取的情况下关闭套接字。根据许多因素,您可能无法依赖套接字层来确定客户端是否已终止。最好使用应用程序级别的消息来完成这项工作(例如类似于SMTP的“QUIT”命令)。 - Greg Hewgill
谢谢!进一步的研究表明,我使用的套接字实现也是这样的行为,所以我现在可以指望关闭的套接字会出现在readfds中。你们的答案真的帮了我很多! - ShayPal5
2
我正在开发iOS应用,在这里看来套接字并没有表现出可读的状态。 - Itay Levin

2
在我的应用程序中,当调用select()(用于读事件)并在文件描述符上返回时,我会调用ioctl()来查看缓冲区中有多少可读数据。如果有零字节,则使用它来指示连接已关闭。实际上,我是从这篇Stack Overflow上的答案开始编写Linux套接字时发现了这一点。

为什么不直接使用recv()的返回值,因为你无论如何都需要调用它?这样可以少一次内核调用。 - Greg Hewgill
@GregHewgill 你说得很对。我在 SO 上读到了这个答案,当我刚开始学习 Linux 套接字时,我就认为这是最好的方法。不过,我不会删除这个答案,因为 OP 实际上指定了他想要在不读写套接字的情况下检测它。但就我个人而言,明天早上我会按照你建议的方式修改我的代码,因为这样更有意义。谢谢你。 - mathematician1975
嗯,整个问题在于我不确定关闭的套接字是否会在关闭时出现在readfs上。如果它们确实会出现,那么我将读取它们,然后在从recv()返回-1时关闭它们。在我的问题中,我的意思是我不想循环遍历整个套接字集并主动读取未提示为可读的套接字,只是为了确保它们没有在未通知我的情况下关闭(注销我的服务器)。 - ShayPal5

0

你也可以使用“适配器”来抽象化许多其他事件。

例如,从我的一个项目中...

    #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
}

如果您需要“Connection”基类,可以引用此内容。
#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
}

0
您可以参考这段代码,了解实现其他类的IPNetworkConnection示例。
#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
}

0

这只是许多可能实现之一,还应该概述如何创建“翻译器”和随后的“翻译连接”。如果需要,可以在基类中以及接口中明确发送和接收方法,以允许跨网络通信。

类“Connection”为任何类型的连接定义了一个合适的基础。 “NetworkConnection”添加了对网络的要求,最后,“IPNetworkConnection”实现了所需的IP协议逻辑。这也可以扩展到“TcpNetworkConnection”,然后扩展到“TcpIpNetworkConnection”,或者以任何其他方式进行扩展,例如“SerialConnection”或“EthernetConnection”,然后构建一个类来允许跨媒体通信,例如“SerialToEthernetNetworkConnection”等。

例如:

 #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
}

这两种实现都是有效的,但服务于不同的目的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接