无法使用带有 Xamarin 和 Java 服务器的 Bouncy Castle 从流中读取(IOException:非阻塞套接字会阻塞)
Cannot read from stream (IOException: non-blocking socket would block) using Bouncy Castle with Xamarin and Java Server
先决条件:
我正在使用 Xamarin 编写一个移动应用程序,它应该与 java 服务器交换小消息块。
我正在使用 Bouncy Castle 的 .NET 实现通过 TLS 发送数据,因为我仅限于特定的密码套件 (TLS_ECDH_anon_WITH_AES_256_CBC_SHA),Android 以上的手机默认不支持 API 23级.
问题:如果我只尝试通过以下代码发送数据,一切都很好。但是如果我也尝试读取响应,流会挂起几秒钟然后抛出异常 System.IO.IOException: Unable to read data from the transport connection: Operation on non-blocking socket would block.
但是,正如您在下面的示例中看到的,我正在初始化 Bouncy Castle 的 TlsClientProtocol "blocking"-构造函数(docu 说如果给定一个流它就会阻塞),所以套接字不应该是非阻塞的。
此外,服务器几乎立即接收到数据,但前提是没有来自客户端的读取会在代码中跟进。如果随后出现.Read(..)
或.DataAvailable
检查,则服务器在异常发生后接收数据或没有接收到任何数据。
Purged/simplified代码版本:
客户端 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 -- 结果相同
所以,我实际上已经开始抓狂了。感谢您的帮助!
编辑:幸运的是我找到了解决方案,这只是我犯的一个愚蠢的错误,请参阅下面的答案。
事实证明,异常消息在某种程度上具有误导性。套接字处于阻塞模式,错误是……我该怎么称呼它……愚蠢。我只是忘了添加换行符 (\n)。这也是为什么 java 服务器在处理 TcpClient 之前没有收到任何东西的原因 - 它正在等待换行符。
先决条件: 我正在使用 Xamarin 编写一个移动应用程序,它应该与 java 服务器交换小消息块。 我正在使用 Bouncy Castle 的 .NET 实现通过 TLS 发送数据,因为我仅限于特定的密码套件 (TLS_ECDH_anon_WITH_AES_256_CBC_SHA),Android 以上的手机默认不支持 API 23级.
问题:如果我只尝试通过以下代码发送数据,一切都很好。但是如果我也尝试读取响应,流会挂起几秒钟然后抛出异常 System.IO.IOException: Unable to read data from the transport connection: Operation on non-blocking socket would block.
但是,正如您在下面的示例中看到的,我正在初始化 Bouncy Castle 的 TlsClientProtocol "blocking"-构造函数(docu 说如果给定一个流它就会阻塞),所以套接字不应该是非阻塞的。
此外,服务器几乎立即接收到数据,但前提是没有来自客户端的读取会在代码中跟进。如果随后出现.Read(..)
或.DataAvailable
检查,则服务器在异常发生后接收数据或没有接收到任何数据。
Purged/simplified代码版本:
客户端 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 -- 结果相同
所以,我实际上已经开始抓狂了。感谢您的帮助!
编辑:幸运的是我找到了解决方案,这只是我犯的一个愚蠢的错误,请参阅下面的答案。
事实证明,异常消息在某种程度上具有误导性。套接字处于阻塞模式,错误是……我该怎么称呼它……愚蠢。我只是忘了添加换行符 (\n)。这也是为什么 java 服务器在处理 TcpClient 之前没有收到任何东西的原因 - 它正在等待换行符。