使用Bouncy Castle和Xamarin和Java Server无法从流中读取(IOException:非阻塞套接字会阻塞) [英] Cannot read from stream (IOException: non-blocking socket would block) using Bouncy Castle with Xamarin and Java Server

查看:307
本文介绍了使用Bouncy Castle和Xamarin和Java Server无法从流中读取(IOException:非阻塞套接字会阻塞)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

问题:如果我仅尝试通过以下代码发送数据,一切都很好。但是,如果我尝试也读回响应,则流挂起几秒钟,然后引发异常 System.IO.IOException:无法从传输连接读取数据:在非阻塞套接字上进行操作将但是,正如您在下面的示例中看到的那样,我正在使用 blocking构造函数初始化Bouncy Castle的TlsClientProtocol(docu表示如果提供了流,它将阻塞),因此套接字



此外,服务器几乎立即接收到数据,但前提是没有来自客户端的读取将遵循代码。如果之后执行 .Read(..) .DataAvailable 的检查,则服务器在发生异常后接收数据



清除/简化的代码版本:



客户端Xamarin应用:

  TcpClient客户= new TcpClient(){ReceiveTimeout = 5000,SendTimeout = 5000}; 
client.Connect(ip,port);
NetworkStream stream = client.GetStream();

TlsClientProtocol协议=
新的TlsClientProtocol(stream,new Org.BouncyCastle.Security.SecureRandom());
protocol.Connect(new CustomTlsClient()); // CustomTlsClient派生自DefaultTlsClient,用于覆盖CipherSuite
协议。Stream.Write(data,0,data.length);
protocol.Stream.Flush();
//如果存在以下行,则发送也无法进行
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);

字符串请求= in.readLine(); //仅在客户端之后不再读取
out.println(request);时才有效

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




  • 手动设置client.Client.Blocking = false-没什么改变

  • 该异常表示套接字未阻塞,因此我尝试通过protocol.Stream.DataAvailable在循环中等待-它一直在等待,但是退出应用程序后,服务器收到了消息(在循环期间服务器未收到任何消息)

  • 我编写了自己的Java服务器以在本地主机上测试此行为-相同的结果

  • 我尝试使用BeginSend / BeginRead-相同的结果



所以,我实际上开始从头上扯掉我的头发。谢谢您的帮助!



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

解决方案

原来,异常消息在某种程度上具有误导性。套接字处于阻止模式,错误是……我该怎么称呼……愚蠢。我只是忘了添加一个换行符(\n)。这也是Java服务器直到TcpClient被处理之前一直没有收到任何消息的原因-它正在等待换行符。


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.

解决方案

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.

这篇关于使用Bouncy Castle和Xamarin和Java Server无法从流中读取(IOException:非阻塞套接字会阻塞)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆