先决条件:我正在使用 Xamarin 编写一个移动应用程序,该应用程序应该与 java 服务器交换小消息块。
我使用 Bouncy Castle 的 .NET 实现通过 TLS 发送数据,因为我仅限于特定的密码套件 (TLS_ECDH_anon_WITH_AES_256_CBC_SHA),API 级别 23 以上的 Android 手机默认不支持该密码套件。
问题:如果我只尝试通过以下代码发送数据,一切都很好。但是如果我尝试读回响应,流会挂起几秒钟,然后抛出异常System.IO.IOException: Unable to read data from the transport connection: Operation on non-blocking socket would block.
但是,正如您在下面的示例中看到的,我使用“阻塞”构造函数初始化 Bouncy Castle 的 TlsClientProtocol(文档说如果给定流,它就会阻塞),因此套接字不应该是非阻塞的。
此外,服务器几乎立即接收数据,但前提是客户端不会在代码中读取数据。如果一个.Read(..)
or .DataAvailable
check是事后发生的,服务器在异常发生后才收到数据或者没有收到任何东西。
清除/简化的代码版本:
客户端 Xamarin 应用程序:
TcpClient client = new TcpClient() { ReceiveTimeout = 5000, SendTimeout = 5000 };
client.Connect(ip, port);
NetworkStream stream = client.GetStream();
TlsClientProtocol protocol =
new TlsClientProtocol(stream, new Org.BouncyCastle.Security.SecureRandom());
protocol.Connect(new CustomTlsClient()); // CustomTlsClient derives from DefaultTlsClient and is used to overwrite the CipherSuite
protocol.Stream.Write(data, 0, data.length);
protocol.Stream.Flush();
// Sending won't work too if the following line is present
protocol.Stream.Read(buffer, 0, buffer.Length);
服务器端java应用程序(我无法访问它,但我得到了它是这样实现的信息):
SSLServerSocket socket = (SSLServerSocket)SSLServerSocketFactory.getDefault().createServerSocket(port);
String[] enabledCipherSuites = new String[] { "TLS_ECDH_anon_WITH_AES_256_CBC_SHA" };
socket.setEnabledCipherSuites(enabledCipherSuites);
SSLSocket clientSocket = socket.accept();
clientSocket.startHandshake();
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String request = in.readLine(); // Works only if the client won't read afterwards
out.println(request);
迄今为止一些失败的解决方案尝试:
- 手动设置 client.Client.Blocking = false -- 没有任何改变
- 异常表示套接字是非阻塞的,因此我尝试通过 protocol.Stream.DataAvailable 在循环中等待——它一直在等待,但是在我退出应用程序后,服务器收到了消息(在循环期间没有收到任何消息)由服务器)
- 我编写了自己的 java 服务器来在本地主机上测试此行为 - 相同的结果
- 我尝试使用 BeginSend / BeginRead - 相同的结果
所以,我实际上已经开始拔掉我的头发了。任何帮助表示赞赏!
EDIT:幸运的是,我找到了解决方案,这只是我犯的一个愚蠢的错误,请参阅下面的答案。