3

我有一个 TCPClient,它创建了一个我在 DataAvailable 时读取的流。

每 20 秒 !DataAvailable 我使用 ACK 消息 ping 套接字以防止流关闭。

但我似乎得到了喜忧参半的结果。似乎每隔一次我打开流(基本上重新启动我的服务)我都会遇到传输错误。

这是我的 Connect 函数的简化版本:

client = new StreamClient();
client.Connect(new IPEndPoint(clientAddress, senderPort));
stream = client.GetStream();
bool status = SendMessage(seq, sync, MessageTypes.Init);

SendMessage 函数执行以下操作:

if (stream == null) return false;
stream.Write(TransmitBuffer, 0, TransmitMessageLength);

我的关闭功能:

if (stream != null)
{
    SendMessage(seq, sync, MessageTypes.Finish);
    stream.Close();
}
stream = null;

client.Close();
client = null;

由于套接字的性质,预计 SendMessage 调用偶尔会失败。

但有时,一旦我连接,一切运行正常,没有失败的消息。但其他时候 ACK 会失败。当 ACK 失败时,我调用 Close,这将强制进行 Connect 并验证套接字的另一端是否打开。如果那失败了,那么我知道结束了。但有时该调用不会失败,然后 20 秒后 ACK 会失败。

谁能给我一个意见,为什么会发生这种情况?等待 20 秒是否太长?我没有正确关闭插座的末端吗?

我正在处理的具体错误消息是:

Unable to write data to the transport connection: An established connection was aborted by the software in your host machine.

它发生在stream.Write(TransmitBuffer, 0, TransmitMessageLength);

4

4 回答 4

1

一般来说,将协议放在 TCP 之上是一个很大的错误。它只会使连接不那么可靠。TCP 已经非常有力地保证从一台机器发送的数据将被网络上的另一台机器接收。只有非常恶劣的外部环境才能使这种情况失败。诸如设备断电或计划外重启之类的事情。

除非其中一台机器故意关闭套接字,否则不应断开连接。当然,这应该始终以可预测的方式完成。事务的逻辑结束或机器正在签署的显式消息。

除了“阻止流关闭”之外,您没有给出将这个“ACK 协议”添加到连接逻辑的任何动机。我认为您在这里看到的是它不起作用,实际上并没有阻止流关闭。所以它仍然像在添加协议层之前那样出错,您仍然会遇到意外的“已建立的连接被中止”异常。

您如何降低可靠性的一个示例是您添加的 20 秒超时检查。TCP 不使用 20 秒超时。它使用指数退避算法来检查超时。通常它不会放弃,直到至少 45 秒过去了。因此,您将在TCP 执行此操作之前声明连接已失效。

很难就如何推进这一点提供建议。但显然不是通过添加协议,您尝试过它并没有奏效。您将不得不找出连接意外中断的原因。不幸的是,这确实需要大量的工作,您必须深入了解位于您的机器和服务器之间的网络设备和软件。有些人认为问题出在电线的另一端,因为那是最难诊断的地方。让网站的网络管理员参与您的问题是重要的第一步。

于 2013-01-21T16:13:06.033 回答
1

我在您的实现中看到的主要让我印象深刻的是,您似乎将 Stream 视为连接,但事实并非如此。您对 Stream 实例的检查应该改为对 TcpClient 实例的检查。我不确定这是否是您问题的根源,但它对我来说确实很奇怪。

而不是这个:

stream = client.GetStream();
if (stream != null)
{
    SendMessage(seq, sync, MessageTypes.Finish);
    stream.Close();
}
stream = null;

我通常会做更多这样的事情:

if (client != null)
{
    if (client.Connected)
    {
        client.GetStream().Close();
    }

    client.Close();
    client = null;
}

您应该在使用流之前检查 TcpClient.Connected,而不是流本身。
我要提到的另一件事是确保始终使用异步方法来连接、读取和写入 TcpClient。同步的更容易,但我的经验是依赖它们会给你带来麻烦。

于 2013-01-21T17:17:38.237 回答
1

我们在开发网络通信库时遇到了类似的问题。我们发现最可靠的解决方案是发送一些空数据(也就是保持活动状态),而不是使用 ACK。在这种情况下 byte[1] 具有零值。这将导致:

  1. 发送成功,接收端可以忽略数据。
  2. 发送失败,将立即导致连接关闭并重新建立。

这些结果中的任何一个都确保了连接始终处于可用状态。

于 2013-01-23T15:24:06.597 回答
1

为什么要发送 ACK?您应该发送 SYN。

而且你不应该发送 ACK、SYN、RST 或为此设置任何其他标志。您正在涉足 TCP 内部。您正在创建应用程序级协议,并且保持活动是其中的一部分,原因,iirc,即使有http://msdn.microsoft.com/en-us/library/windows/desktop/ee470551%28v=vs.85 %29.aspx连接将一直打开,直到您关闭它。

于 2013-01-26T07:24:20.477 回答