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.