【发布时间】:2017-08-25 17:47:28
【问题描述】:
我正在编写一个 TcpListener 服务,它作为 Windows 服务运行,供应商软件客户端将连接到该服务。我无法更改客户的实施,必须遵守他们的规范。
我有接收消息并返回响应的部分正常工作。我遇到的问题是客户希望每 5 秒发送一次心跳消息 (S0000E),它会回复相同的消息。我不确定如何在我正在执行的异步/等待内容中添加该功能,以处理从客户端收到的真实消息。
开机
_serverListenerTask = Task.Run(() => AcceptClientsAsync(_listener, _cancellationToken.Token));
AcceptClientsAsync
static async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync()
.ConfigureAwait(false);
clientCounter++;
await ReceiveMessageAsync(client, clientCounter, ct);
}
}
ReceiveMessageAsync
static async Task ReceiveMessageAsync(TcpClient client, int clientIndex, CancellationToken ct)
{
Log.Info("New client ({0}) connected", clientIndex);
using (client)
{
var buffer = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buffer, 0, buffer.Length, ct);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
var bytesRead = amountReadTask.Result;
if (bytesRead == 0)
{
// Nothing was read
break;
}
// Snip... Handle message from buffer here
await stream.WriteAsync(responseBuffer, 0, responseBuffer.Length, ct)
.ConfigureAwait(false);
}
}
Log.Info("Client ({0}) disconnected", clientIndex);
}
我以为我可以向Task.WhenAny 添加一个心跳任务,但这会导致心跳总是触发,我永远无法读取响应。我还尝试在超时和读取任务之前发送心跳,这对发送很有用,但是我要么读取心跳响应而不是下一条消息,要么超时任务将完成并断开客户端。本质上,如果心跳交换成功,那么客户端不应在 15 秒延迟后断开连接。
【问题讨论】:
-
如何将一条消息与另一条消息分开?
-
我是否正确地阅读了这个协议,通常协议是客户端请求 -> 服务器响应,但在这个特定实例中,它的服务器心跳 -> 客户端心跳?协议中是否有特定的规则,例如是否允许/不允许您在响应未完成时发送心跳消息?明确定义此处涉及的消息(类型)以及围绕它们的规则(即“协议”)会有所帮助。
-
如果服务器选择在客户端选择发送请求的同一时刻发送心跳,这似乎也很自然。
-
您应该能够摆脱超时要求。如果您定期发送心跳,您将检测到断开的连接。
-
@Damien_The_Unbeliever 是的,通常客户端正在发送消息,服务器必须以确认消息作为响应来确认这些消息。但是,心跳预计会从服务器发送并从客户端返回。我不知道何时可以/不能发送是否有规则。不幸的是,没有关于这个传统供应商协议的文档。
标签: c# asynchronous async-await tcplistener