微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Android Emulator DataInputStream 无法完全读取流

如何解决Android Emulator DataInputStream 无法完全读取流

我正在开发一个 Android 应用程序,其中数据通过 Socket 连接传输到应用程序。我注意到,一旦“有效负载”变得越来越大,EOFException 越来越有可能发生,但我无法识别出清晰的模式。请注意,到目前为止,我只能在 QEMU Android 模拟器上验证这一点,在真正的 Android 设备或 Genymotion 模拟器上似乎不存在此问题。

版本信息:

  • 模拟器:30.0.5-6306047 (HAXM 7.5.2)enter code here
  • Android 版本:5.1 API 22(修订版 2)
  • 主机:Windows 10 专业版

考虑以下最小示例。 SocketServer 在端口 12345 上启动。用户套接字客户端)可以选择多个字节,这个数字被发送到“服务器”,在那里生成字节并将其写入套接输出流。客户端尝试使用 DataInputStream#readFully(byte[]) 读取响应(“payload”)。

public class MainActivity extends AppCompatActivity {

    Button sendBtn;
    EditText bytesTf;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        bytesTf = findViewById(R.id.byteAmount);
        sendBtn = findViewById(R.id.send_it);

        runSocketServer();

        sendBtn.setonClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int bytes = Integer.parseInt(bytesTf.getText().toString());
                runSocketClient(bytes);
            }
        });
    }

    private void runSocketClient(final int numberOfBytes) {
        Runnable client = new Runnable() {
            @Override
            public void run() {
                // Change to "10.0.2.2" if server is not running on Android
                String host = "localhost";
                try (Socket socket = new Socket(host,12345)) {
                    DataOutputStream dataOut = new DataOutputStream(socket.getoutputStream());
                    DataInputStream dataIn = new DataInputStream(socket.getInputStream());
                    dataOut.writeInt(numberOfBytes);
                    dataOut.flush();

                    byte[] data = new byte[numberOfBytes];
                    // Exception occurs here:
                    dataIn.readFully(data);
                    Log.i("Test",data.length + " bytes: successfully read");
                } catch(IOException ex) {
                    Log.i("Test",numberOfBytes + " bytes: " + ex);
                    ex.printstacktrace();
                }
            }
        };

        new Thread(client).start();
    }

    private void runSocketServer() {
        Runnable server = new Runnable() {
            @Override
            public void run() {
                try {
                    ServerSocket serverSocket = new ServerSocket(12345);
                    System.out.println("Listening to port " + serverSocket.getLocalPort());
                    while (true) {
                        Socket socket = serverSocket.accept();
                        DataInputStream dataIn = new DataInputStream(socket.getInputStream());
                        DataOutputStream dataOut = new DataOutputStream(socket.getoutputStream());

                        int amountOfBytesToGenerate = dataIn.readInt();
                        byte[] randomBytes = new byte[amountOfBytesToGenerate];
                        new Random().nextBytes(randomBytes);

                        System.out.println("Sending " + amountOfBytesToGenerate + " bytes to client " + socket.toString());

                        dataOut.write(randomBytes);
                        dataOut.flush();

                        socket.close();
                    }
                } catch(Exception ex) {
                    ex.printstacktrace();
                }
            }
        };

        new Thread(server).start();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main,menu);
        return true;
    }

    @Override
    public boolean onoptionsItemSelected(MenuItem item) {
        return super.onoptionsItemSelected(item);
    }
}

https://github.com/GitGraf/StreamExceptionExample

为简单起见,本示例中的服务器和客户端都运行在同一个模拟 Android 设备上。通过此设置,EOFException 似乎永远不会发生,无论 numberOfBytes 设置为 10 还是 1.000.000。

在现实世界中,服务器运行在同一网络中的 Windows 服务器上。要模拟这一点,请继续从 runSocketServer() 方法删除代码,并将其放入独立 Java 应用程序的 main-method 中。在您的开发机器上启动该应用程序,将 host 中的 runSocketClient() 变量更改为“10.0.2.2”并再次运行该应用程序。

通过这种设置,如果有效负载的大小增加套接字连接似乎会越来越频繁地失败。

1024 bytes: java.io.EOFException
java.io.EOFException
     at libcore.io.Streams.readFully(Streams.java:83)
     at java.io.DataInputStream.readFully(DataInputStream.java:99)
     at java.io.DataInputStream.readFully(DataInputStream.java:95)
     at com.example.streamreadingtest.MainActivity$2.run(MainActivity.java:64)
     at java.lang.Thread.run(Thread.java:818)

我想弄清楚这种偶尔出现且不稳定的 EOFException 的根本原因,并找到正确修复它的方法。我已经尝试过尽可能远离 readFully() 并使用 read() 代替,这似乎已经有很大帮助了。但是,我认为 Android SDK 的 DataInputStream 类也大量使用 readFully() 进行内部调用

如果您需要更多信息,请告诉我。


编辑:我已从使用 QEMU android 模拟器切换到 Genymotion,并且 EOFException 不会出现在那里,如下面的屏幕截图所示(左边是 quemu,右边是 genymotion)。 https://github.com/GitGraf/AndroidEOF

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。