Mono上的SslStream未完全关闭



我目前正在使用一个服务器应用程序,我们设计该应用程序用于与Xamarin移动应用程序通信。我们使用的是一个旧的消息库,它与TcpClient建立连接,并保持连接打开(每3秒发送一条心跳消息)。我们通过用SslStream包装TcpClient流,将SSL添加到库中。我们已经在Windows上运行了服务器应用程序,它运行得很好,但我们的最终目标是BeagleBoneBlack上的Mono

然而,当我们在移动应用程序端关闭流和客户端,然后尝试重新启动新连接时,SslStream.AuthenticateAsServer(...)将不会在服务器上完成。但是,如果我完全关闭移动应用程序,服务器将抛出异常。这时,我重新打开应用程序,可以重新连接而不会出现任何问题。

因此,无论是应用程序还是服务器端,似乎都没有关闭低级别的东西。奇怪的是,当服务器在windows上运行时,我在两者上运行完全相同的代码,而且我没有问题。

这是我关闭/处理流的代码

public async Task Disconnect()
{
if (!UseAsync)
{
semaphore.Wait();
}
try
{
if (UseSSL)
{
SslStream?.Close();
}
Client?.GetStream()?.Close();
Client?.Close();
}
catch (Exception ex) // Assuming we had an exception from trying to close the sslstream
{
logger.Error(ex, "Did not close/dispose correctly: {0}", ex.ToString());
}
finally
{
SslStream = null;
Client = null;
if (!UseAsync)
{
semaphore.Release();
}
}
}

编辑:这应该不重要,因为问题似乎在服务器的某个地方,客户端和服务器的ssl代码几乎相同,但如果有人问,这里是客户端断开连接代码

public async Task Disconnect()
{
try
{
if (UseSSL)
{
_sslStream?.Close();
}
Client?.GetStream()?.Close();
Client?.Close();
}
catch // Assuming we had an exception from trying to close the sslstream
{
// Ignore exceptions since we've already closed them
}
finally
{
_sslStream = null;
Client = null;
}
}

编辑2

还应该注意的是,我至少发现了一份错误报告,看起来与我正在处理的问题相同。从这个错误报告中看,它似乎从未得到解决,但我发现其他报告似乎反映了mono框架中的类似问题,并得到了解决。此外,我还添加了一些代码,以便在连接后从客户端发送一些"伪"数据——这似乎没有影响。

编辑3:我最终在客户端上收到此异常

System.IO.IOException: The authentication or decryption has failed. ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: The authentication or decryption has failed.
at Mono.Security.Protocol.Tls.RecordProtocol.EndReceiveRecord (System.IAsyncResult asyncResult) [0x0003a] in /Users/builder/data/lanes/3511/501e63ce/source/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/RecordProtocol.cs:430 
at Mono.Security.Protocol.Tls.SslClientStream.SafeEndReceiveRecord (System.IAsyncResult ar, System.Boolean ignoreEmpty) [0x00000] in /Users/builder/data/lanes/3511/501e63ce/source/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs:256 
at Mono.Security.Protocol.Tls.SslClientStream.NegotiateAsyncWorker (System.IAsyncResult result) [0x00360] in /Users/builder/data/lanes/3511/501e63ce/source/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs:533 

嗯。。。回答这个问题我几乎觉得很傻,但我觉得其他人可能会因为同样的无知而陷入我的境地,所以希望这能帮助有类似架构的人。

发生的事情是,我创建了一个自签名证书,并将其放在我的项目文件夹中,因此在我的所有"服务器"实例上都使用了相同的证书。实际情况是,由于SSL会话缓存的一些内部缓存,在连接到一个设备后,它会缓存其信息,然后在下次连接时重用该信息。结果是,由于自签名证书对所有设备使用相同的主机名,一旦它在连接到设备A后尝试重新连接到设备B,它就会尝试重复使用设备A的缓存信息(据我所知)。这最初没有意义的原因是,我可以一次又一次地连接和断开多个不同的Windows服务器,但只有在连接到Mono服务器时,我才看到这个问题。因此,无论是Windows还是Mono的SSLStream实现中,这似乎仍然是一个错误(因为在完美的世界里,它们是完全相同的),但不幸的是,我没有时间深入编译后的源代码来找到它。坦率地说,这可能并不重要,因为我所做的一切无论如何都打破了SSL连接的整个概念

最终,我创建了一个函数,使用Mono.Security为每个设备以编程方式生成一个唯一的证书,然后提供一种机制,为客户端提供唯一的主机名(即使客户端直接连接到IP地址)。

最新更新