我有一台服务器和很多客户端。服务器需要知道客户端何时断开连接(不发送TCP FIN),以便它没有挂起连接和与此客户端关联的其他一次性对象。
无论如何,我阅读this并决定在链接博客中添加“应用程序协议的keepalive消息”(仅包含头字节)和“假定最差的显式定时器”方法。
当客户端连接时(顺便说一下,我正在使用TcpListener和TcpClient),服务器启动一个倒计时30秒的System.Threading.Timer。每当服务器从该客户端收到某些内容时,它会重置计时器。当计时器达到0时,它会断开用户的连接并处理它需要处理的任何内容。客户端应用程序也有一个计时器,当用户没有发送任何东西15秒(服务器的一半值,只是为了确定)时,它会发送keepalive消息。
我的问题是,有更简单的方法来实现这一目标吗?也许在TcpClient上有一些选项?我尝试使用TcpClient.ReceiveTimeout,但这似乎不适用于ReadAsync。
答案 0 :(得分: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);
}
}