无法使用Bouncy Castle和Xamarin以及Java服务器从流中读取数据(IOException:非阻塞套接字将阻塞)

无法使用Bouncy Castle和Xamarin以及Java服务器从流中读取数据(IOException:非阻塞套接字将阻塞),java,c#,ssl,xamarin,bouncycastle,Java,C#,Ssl,Xamarin,Bouncycastle,先决条件: 我正在使用Xamarin编写一个移动应用程序,它应该与java服务器交换小消息块。 我使用Bouncy Castle的.NET实现通过TLS发送数据,因为我被限制为特定的密码套件(TLS_ECDH_anon_WITH_AES_256_CBC_SHA),默认情况下API级别23以上的Android手机不支持该套件 问题:如果我只尝试通过以下代码发送数据,一切正常。但是,如果我尝试同时读取响应,流将挂起几秒钟,然后抛出异常System.IO.IOException:无法从传输连接读取数据

先决条件: 我正在使用Xamarin编写一个移动应用程序,它应该与java服务器交换小消息块。 我使用Bouncy Castle的.NET实现通过TLS发送数据,因为我被限制为特定的密码套件(TLS_ECDH_anon_WITH_AES_256_CBC_SHA),默认情况下API级别23以上的Android手机不支持该套件

问题:如果我只尝试通过以下代码发送数据,一切正常。但是,如果我尝试同时读取响应,流将挂起几秒钟,然后抛出异常
System.IO.IOException:无法从传输连接读取数据:非阻塞套接字上的操作将被阻塞。
但是,正如您在下面的示例中所看到的,我正在使用“阻塞”-构造函数(docu说,如果给定流,它就是阻塞的),因此套接字不应该是非阻塞的

此外,服务器几乎立即接收数据,但只有在代码中没有从客户端读取数据的情况下才会接收数据。如果随后出现
.Read(..)
.DataAvailable
检查,则服务器会在异常发生后接收数据或不接收任何内容

清除/简化代码版本:

客户端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应用程序(我无法访问它,但我得到的信息表明它是以这种方式实现的):

到目前为止,一些失败的解决方案尝试:

  • 手动设置client.client.Blocking=false--未更改任何内容
  • 异常表示套接字是非阻塞的,因此我尝试通过protocol.Stream.DataAvailable在循环中等待--它一直在等待,但在我退出应用程序后,服务器收到了消息(在循环过程中,服务器没有收到任何消息)
  • 我编写了自己的java服务器来在本地主机上测试这种行为——结果相同
  • 我尝试使用BeginSend/beginhead——同样的结果
所以,我真的开始发疯了。谢谢你的帮助


编辑:我幸运地找到了解决方案,这只是我犯的一个愚蠢的错误,请参见下面的答案。

结果是,异常消息在某种程度上具有误导性。套接字处于阻塞模式,错误是……我该如何称呼它……愚蠢。我只是忘了添加换行符(\n)。这也是java服务器在TcpClient被释放之前没有收到任何东西的原因-它正在等待换行

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);