Cannot read from stream (IOException: non-blocking

2019-09-16 09:04发布

问题:

Prerequisites: I'm using Xamarin to write a mobile app which should exchange small message chunks with a java server. I'm using the .NET implementation of Bouncy Castle for sending data over TLS, since I'm restricted to a specific cipher suite (TLS_ECDH_anon_WITH_AES_256_CBC_SHA) which is not supported by default for Android phones above API Level 23.

The problem: If I only try to send data via the following code, everything is fine. But if I try to also read the response back, the stream hangs for some seconds and then throws the exception System.IO.IOException: Unable to read data from the transport connection: Operation on non-blocking socket would block. However, as you can see in the example below, I'm initializing Bouncy Castle's TlsClientProtocol with the "blocking"-constructor (docu says it is blocking if a stream is given), so the socket should not be non-blocking.

Furthermore, the server receives the data almost instantly, but only if no reading from the client will follow in code. If a .Read(..) or .DataAvailable check comes afterwards, the server receives the data after the exception occurred or it does not receive anything.

Purged/simplified code version:

Clientside Xamarin app:

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

Server side java application (I have no access to it, but i got the info that it is implemented that way):

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

Some of the failed solution attempts so far:

  • Manually set client.Client.Blocking = false -- nothing changed
  • The exception says the socket is non-blocking, therefore I tried to wait in a loop via protocol.Stream.DataAvailable -- It was waiting forever, but after I quit the app, the server received the message (during the loop nothing was received by the server)
  • I wrote my own java server to test this behaviour on a localhost -- same results
  • I tried to use BeginSend / BeginRead -- same results

So, I'm actually starting to tear my hairs out of my head. Any help is appreciated!

EDIT: I fortunately found the solution, it was just a dumb error I made, see my answer below.

回答1:

It turned out, that the exception message was somehow misleading. The socket was in blocking mode and the mistake was ... how should I call it ... dumb. I simply forgot to append a newline (\n). This was also the reason why the java server did not receive anything until the TcpClient was disposed - it was waiting for the newline.