自我修复的SslStream

时间:2014-08-29 18:52:31

标签: c# .net networking

我正在编写一项需要维持与远程服务器的长时间运行SSL连接的服务。我需要这台服务器进行自我修复,即如果它因任何原因断开连接,那么下次写入它将重新连接。我试过这个:

bool isConnected = client.Connected && client.Client.Poll(0, SelectMode.SelectWrite) && stream.CanWrite;

if (!isConnected )
{
    this.connected = false;
    GetConnection();
}

stream.Write(bytes, 0, bytes.Length);
stream.Flush();

但我发现它并没有像我期望的那样行事。如果我通过禁用我的wifi来模拟网络中断,我仍然能够使用stream.Write()写入流大约20秒。然后,当我尝试写入它时,client.Connected,client.Client.Poll()或stream.CanWrite()都不返回false,但是当我写入流时,我得到套接字异常。最后,如果我尝试重新创建连接,我会遇到以下异常:远程主机强行关闭现有连接。

我很感激任何帮助创建一个可以承受网络故障的长时间运行的SslStream。谢谢!

2 个答案:

答案 0 :(得分:3)

从10.000英尺的角度来看:

关闭wifi后仍然可以写入流的原因是因为有一个网络缓冲区正在保存数据进行传输,stream.Write / stream.Flush成功意味着网络接口(TCP / IP堆栈)已接受数据并已进行缓冲以进行传输,而不是数据已达到目标。

TCP / IP协议栈需要一段时间才能注意到完全媒体断开连接(连接丢失/重置),因为即使没有物理链路,TCP / IP也会将此视为网络中的临时问题并将继续重试一段时间(网络可能在某个时刻丢弃数据包,堆栈将继续重试)

如果您以相反的方式考虑这一点,如果网络打嗝(这种情况在互联网上经常发生),您将不会希望所有程序都失败,因此TCP / IP会花时间通知应用程序连接失效的图层(重试几次并等待一段合理的时间后)

当SslStream发生故障并继续发送数据时,您始终可以重新连接到服务器,尽管您会发现并不像这样容易,因为有几种情况您发送服务器并且服务器收到服务器的其他人没有收到数据数据,你根本没有从服务器收到任何确认......所以根据你的需要,单靠自我修复是不够的。

自我修复很容易实现,数据一致性和可靠性更难,通常要求服务器准备好支持某种可靠的消息传递机制,以确保所有数据都已发送和接收。

答案 1 :(得分:0)

SSL的基础协议是TCP。 TCP通常仅在应用程序希望它传送数据时发送数据,或者如果需要通过发送ACK来回复从另一方接收的数据。这意味着,在您尝试发送任何数据之前,不会发现像丢失链接这样的断开连接。但你不会立即注意到,因为:

  • 对套接字的写入只会将数据传递到操作系统内核,并且如果此传递成功则返回成功。
  • 然后,内核将尝试将数据传递给对等方,并等待来自客户端的ACK。
  • 如果没有得到任何ACK,它将再次重试以传递数据,并且只有在一些不成功的重试之后,内核才会声明连接断开。
  • 只有在连接被内核标记为中断后,下一次写入或读取才会将错误从内核返回到用户空间,就像在执行写操作时返回EPIPE一样。

这意味着,如果您想要预先知道连接是否仍然存在,则必须确保在连接上进行常规数据交换。在TCP级别,您可以设置TCP_KEEPALIVE,但这可能会在交换数据包之间使用几个小时的间隔。在SSL层,您可能会尝试使用臭名昭着的心跳扩展,但大多数同行不会理解它。最后一种选择是在您自己的应用程序中实现某种心跳。

至于自我修复:重新连接时,您将获得新的TCP连接,并且还需要执行完整的SSL握手,因为最后一次SSL连接未完全关闭,因此无法恢复。服务器不知道这个新连接只是旧连接的延续,因此您必须在客户端和服务器上的应用层内实现跨越多个TCP连接的某种元连接。在这个元连接中,您需要有自己的数据跟踪来检测,哪些数据真正从对等方接受,哪些数据只是发送但从未明确接受,因为连接断开。听起来像TCP上的一种TCP。