【问题标题】:Detecting dropped connections检测断开的连接
【发布时间】:2016-06-30 17:53:18
【问题描述】:

我有一台服务器和许多客户端。服务器需要知道客户端何时非正常断开连接(不发送 TCP FIN),以便它没有挂起的连接和与该客户端关联的其他一次性对象。

无论如何,我阅读了this 并决定从链接的博客中添加“向应用程序协议保留消息”(仅包含标头字节)和“假设最坏的显式计时器”方法。

当客户端连接时(顺便说一句,我正在使用 TcpListener 和 TcpClient),服务器启动一个 System.Threading.Timer 倒计时 30 秒。每当服务器从该客户端接收到某些内容时,它都会重置计时器。当计时器达到 0 时,它会断开用户并处理它需要处理的任何内容。客户端应用程序也有一个计时器,当用户在 15 秒内(服务器值的一半,只是为了确定)没有发送任何内容时,它会发送 keepalive 消息。

我的问题是,有没有更简单的方法来实现这一点?也许 TcpClient 上有一些选项?我尝试使用 TcpClient.ReceiveTimeout,但这似乎不适用于 ReadAsync。

【问题讨论】:

  • 正如斯蒂芬指出的那样,在应用程序协议中使用心跳消息是确保连接处于活动状态并且两个应用程序都正常运行的唯一可靠方法。请注意,许多工程师已经创建了一个心跳线程,即使应用程序线程发生故障,该线程也会继续运行。您需要确保心跳构成解决方案的操作结构的一部分。这是一个不平凡的问题。
  • 对于异步套接字操作,我已经转向这段代码,我已经成功使用了blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx我已经使用了SocketAwaitable
  • 谢谢!我将尝试该代码,但我不太明白您所说的“您需要确保心跳构成解决方案的操作结构的一部分”的意思。
  • 我的意思是,如果您的解决方案有一个或多个线程来发送消息并执行业务逻辑,以及一个单独的线程来发送心跳消息,那么最终可能会出现心跳正在发送消息,但应用程序线程出现故障。最终,您需要确保仅当您有理由确定应用程序正常运行并且没有线程出现故障(停止)时才发送心跳消息。心跳应该完全集成到解决方案中。
  • 啊,有道理。再次感谢!

标签: c# tcpclient


【解决方案1】:

As Stephen points out 在应用程序协议中使用心跳消息是确保连接处于活动状态并且两个应用程序都正常运行的唯一可靠方法。请注意,许多工程师创建了一个心跳线程,即使应用程序线程发生故障,它也会继续运行。

使用 here 类将解决您的异步套接字问题。

public sealed class SocketAwaitable : INotifyCompletion
{ 
    private readonly static Action SENTINEL = () => { };

    internal bool m_wasCompleted; 
    internal Action m_continuation; 
    internal SocketAsyncEventArgs m_eventArgs;

    public SocketAwaitable(SocketAsyncEventArgs eventArgs) 
    { 
        if (eventArgs == null) throw new ArgumentNullException("eventArgs"); 
        m_eventArgs = eventArgs; 
        eventArgs.Completed += delegate 
        { 
            var prev = m_continuation ?? Interlocked.CompareExchange(
                ref m_continuation, SENTINEL, null); 
            if (prev != null) prev(); 
        }; 
    }

    internal void Reset() 
    { 
        m_wasCompleted = false; 
        m_continuation = null; 
    }

    public SocketAwaitable GetAwaiter() { return this; }

    public bool IsCompleted { get { return m_wasCompleted; } }

    public void OnCompleted(Action continuation) 
    { 
        if (m_continuation == SENTINEL || 
            Interlocked.CompareExchange(
                ref m_continuation, continuation, null) == SENTINEL) 
        { 
            Task.Run(continuation); 
        } 
    }

    public void GetResult() 
    { 
        if (m_eventArgs.SocketError != SocketError.Success) 
            throw new SocketException((int)m_eventArgs.SocketError); 
    } 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    • 2020-07-07
    • 2013-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-25
    相关资源
    最近更新 更多