【问题标题】:Socket disconnect behavior套接字断开行为
【发布时间】:2011-05-14 21:22:26
【问题描述】:

我有一个小方法可以禁用监听传入连接的套接字。

    /// <summary>
    /// Stops and disables the service
    /// </summary>
    public void Disable() {
        if (Running) {
            try {
                thread.Abort();
            }
            catch (System.Threading.ThreadAbortException) {
                // This catch is not raised.
                // We can simply process our closing of the socket and nullify the thread
            }
            finally {
                socket.Close();
                socket = null;
                thread = null;
                if (socket == null && thread == null) {
                    m_Running = false;
                    OnDisabled(this, new EventArgs());
                }
            }
        }
    }

我的问题是,即使在我调用 Close() 并取消套接字之后,客户端仍然保持连接。我使用 netstat -a 进行了检查,它显示客户端仍处于连接状态。

TCP    127.0.0.1:2161         activate:7777          ESTABLISHED
TCP    127.0.0.1:7777         activate:2161          ESTABLISHED

7777 是我的主机套接字侦听的端口。所以我的问题是,关闭主机套接字后,为什么客户端套接字没有断开连接。它们如何保持连接到 null 且不再侦听的套接字?

一些附加信息

    /// <summary>
    /// Enables and runs the service
    /// </summary>
    public void Enable() {
        if (!Running) {
            ThreadStart start = new ThreadStart(RunServiceAsync);
            thread = new Thread(start);
            thread.IsBackground = true;
            thread.Start();
            m_Running = true;
            OnEnabled(this, new EventArgs());
        }
    }

上面的方法就是线程是如何创建的。一切正常,线程,连接;唯一的问题是当我关闭套接字(主机)并取消它时客户端仍然连接到它。

问题是,一旦主机套接字关闭并设置为空,客户端连接到什么?他们不应该断开和失去与主机的连接,因为主机套接字已关闭吗?

这是帮助的完整代码

// *********************************************************************

// [DCOM 制作] // [版权 (C) DCOM Productions 保留所有权利。] // ***************************************

命名空间 CipherBox.Drivers { 使用系统; 使用 System.Collections.Generic; 使用 System.Linq; 使用 System.Text; 使用 System.Threading; 使用 System.Net.Sockets; 使用 System.Net; 使用 System.ComponentModel; 使用 CipherBox.Objects;

/// <summary>
/// Driver that manages the network connection between the master program and clients, also provides informational events
/// </summary>
public class NetworkDriver : IDriver {

    #region Fields

    private Socket socket;
    private Thread thread;

    #endregion

    #region Properties

    private int m_Port = 7777;
    /// <summary>
    /// Gets the port that the network runs on. The default port is 7777. 
    /// </summary>
    public int Port {
        get {
            return m_Port;
        }
    }

    #endregion

    #region Events

    /// <summary>
    /// Delegate for when a node connects to the service
    /// </summary>
    public delegate void NodeConnectedEventHandler(object sender, NetworkNodeEventArgs e);
    /// <summary>
    /// Triggers when an node connects to the service
    /// </summary>
    public event NodeConnectedEventHandler NodeConnected;
    /// <summary>
    /// Event callback for NodeConnected
    /// </summary>
    private void OnNodeConnected(object sender, NetworkNodeEventArgs e) {
        if (NodeConnected != null) {
            foreach (NodeConnectedEventHandler handler in NodeConnected.GetInvocationList()) {
                ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
                if (syncInvoke != null && syncInvoke.InvokeRequired) {
                    syncInvoke.Invoke(handler, new object[] { handler.Target, e });
                }
                else {
                    NodeConnected(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Delegate for when a node disconnects from the service
    /// </summary>
    public delegate void NodeDisconnectedEventHandler(object sender, NetworkNodeEventArgs e);
    /// <summary>
    /// Triggers when an node disconnects from the service
    /// </summary>
    public event NodeDisconnectedEventHandler NodeDisconnected;
    /// <summary>
    /// Event callback for NodeDisconnected
    /// </summary>
    private void OnNodeDisconnected(object sender, NetworkNodeEventArgs e) {
        if (NodeDisconnected != null) {
            foreach (NodeDisconnectedEventHandler handler in NodeDisconnected.GetInvocationList()) {
                ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
                if (syncInvoke != null && syncInvoke.InvokeRequired) {
                    syncInvoke.Invoke(handler, new object[] { handler.Target, e });
                }
                else {
                    NodeDisconnected(this, e);
                }
            }
        }
    }

    #endregion

    #region Methods

    private NetworkNode FillNode(Socket socket) {
        StringBuilder stream = new StringBuilder();
        byte[] buffer = new byte[4096];
        int bytesReceived = -1;
        do {
            try {
                bytesReceived = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
            }
            catch (System.Net.Sockets.SocketException) {
                return null;
            }
            finally {
                stream.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
            }
        } while (!stream.ToString().EndsWith("\r\n\r\n"));
        string[] packet = stream.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
        if (packet.Length == 9) {
            if (packet[0].ToLower() == "CipherBox".ToLower()) {
                NetworkNode node = new NetworkNode();
                node.Domain = packet[1];
                node.LocalIP = IPAddress.Parse(packet[2]);
                node.MachineName = packet[3];
                node.Platform = packet[4];
                node.RemoteIP = IPAddress.Parse(packet[5]);
                node.Workgroup = packet[6];
                node.Socket = socket;
                return node;
            }
            else {
                return null;
            }
        }
        else {
            return null;
        }
    }

    private bool IsDisconnected(Socket socket) {
        bool connected = false;
        try {
            connected = !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
        }
        catch (System.Net.Sockets.SocketException) {
            connected = false;
        }
        return !connected;
    }

    private void MonitorNode(NetworkNode node) {
        ParameterizedThreadStart start = new ParameterizedThreadStart(MonitorNodeAsync);
        Thread thread = new Thread(start);
        thread.IsBackground = true;
        thread.Start(node);
    }

    private void MonitorNodeAsync(object obj) {
        NetworkNode node = obj as NetworkNode;
        while (Running || node != null) {
            if (IsDisconnected(node.Socket)) {
                node.Socket.Shutdown(SocketShutdown.Both);
                node.Socket.Close();
                node.Socket = null;
                OnNodeDisconnected(null, new NetworkNodeEventArgs(node));
                return;
            }
            else {
                Thread.Sleep(1000);
            }
        }
    }

    private void RunServiceAsync() {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint localEP = new IPEndPoint(IPAddress.Any, Port);
        socket.Bind(localEP);
        socket.Listen(1);
        do {
            Socket client;
            try {
                client = socket.Accept();
            }
            catch (System.Net.Sockets.SocketException) {
                continue;
            }
            NetworkNode node = FillNode(client);
            if (node != null) {
                OnNodeConnected(null, new NetworkNodeEventArgs(node));
                MonitorNode(node);
            }
        } while (Running);
    }

    /// <summary>
    /// Sets the port that the network runs on
    /// </summary>
    /// <param name="port">The port to set</param>
    public void SetPort(int port) {
        m_Port = port;
    }

    #endregion

    #region IDriver Members

    /// <summary>
    /// Triggered when the network driver is disabled
    /// </summary>
    public event EventHandler<EventArgs>  Disabled;
    /// <summary>
    /// Event callback for Disabled
    /// </summary>
    private void OnDisabled(object sender, System.EventArgs e) {
        if (Disabled != null) {
            foreach (EventHandler<EventArgs> handler in Disabled.GetInvocationList()) {
                ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
                if (syncInvoke != null && syncInvoke.InvokeRequired) {
                    syncInvoke.Invoke(handler, new object[] { handler.Target, e });
                }
                else {
                    Disabled(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Triggered when the network driver is enabled
    /// </summary>
    public event EventHandler<EventArgs>  Enabled;
    /// <summary>
    /// Event callback for Enabled
    /// </summary>
    private void OnEnabled(object sender, System.EventArgs e) {
        if (Enabled != null) {
            foreach (EventHandler<EventArgs> handler in Enabled.GetInvocationList()) {
                ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
                if (syncInvoke != null && syncInvoke.InvokeRequired) {
                    syncInvoke.Invoke(handler, new object[] { handler.Target, e });
                }
                else {
                    Enabled(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Stops and disables the service
    /// </summary>
    public void Disable() {
        if (Running) {
            try {
                thread.Abort();
            }
            catch (System.Threading.ThreadAbortException) {
                // This catch is not raised.
                // We can simply process our closing of the socket and nullify the thread
            }
            finally {
                socket.Close();
                socket = null;
                thread = null;
                if (socket == null && thread == null) {
                    m_Running = false;
                    OnDisabled(this, new EventArgs());
                }
            }
        }
    }

    /// <summary>
    /// Enables and runs the service
    /// </summary>
    public void Enable() {
        if (!Running) {
            ThreadStart start = new ThreadStart(RunServiceAsync);
            thread = new Thread(start);
            thread.IsBackground = true;
            thread.Start();
            m_Running = true;
            OnEnabled(this, new EventArgs());
        }
    }

    private bool m_Running = false;
    /// <summary>
    /// Gets a System.Boolean value indicating whether the service is running or not
    /// </summary>
    public bool Running {
        get {
            return m_Running;
        }
    }

    #endregion
}

}

【问题讨论】:

  • 哇...这是什么thread 被中止?那是当前线程吗?如果不是,那你为什么要把它包装到ThreadAbortException 处理程序中? threadsocket 的范围是什么?我假设示例代码是针对“主机”的……你听得怎么样?你用Socket.Accept()吗?这会创建另一个套接字对象。关闭监听器就是这样做的 - 停止监听新连接。

标签: c# sockets


【解决方案1】:

你必须调用 socket.shutdown(Both) 参数可以是 Send、Receive 或 Both 取决于你喜欢如何结束连接。该函数向客户端发送必要的 TCP 消息以关闭连接。

【讨论】:

  • 我关闭的socket是主机,调用shutdown的时候会抛出异常,因为不是连接的。
  • 问题出在方法MonitorNodeAsync,当方法退出while循环时必须关闭连接,因为如果关闭监听套接字并不意味着必须关闭所有接受的连接.
【解决方案2】:

我通过将每个连接的引用存储在一个集合中并使用客户端套接字关闭所有连接来纠正了这个问题。

    private void DestructConnections() {
        foreach (Socket connection in connections) {
            connection.Shutdown(SocketShutdown.Both);
            connection.Close();
        }
        connections.Clear();
    }

【讨论】:

    【解决方案3】:

    建议在使用面向连接的套接字时,您应该在关闭连接之前调用 Shutdown。关机是为了通知通讯结束。关闭用于释放托管/非托管资源
    资源:
    http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown.aspx

    【讨论】:

      【解决方案4】:

      当您使用任何语言的套接字时,您会创建仅侦听和接受连接的服务器套接字,并且有许多 p2p 套接字来发送和/或接收数据。

      所以你的答案是在 Microsoft Windows、BSD、Linux 等中设计套接字子系统。

      【讨论】:

        猜你喜欢
        • 2019-03-05
        • 1970-01-01
        • 1970-01-01
        • 2012-12-10
        • 1970-01-01
        • 1970-01-01
        • 2011-04-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多