【问题标题】:Android N - DataOutputStream.writeInt() behaviour changed?Android N - DataOutputStream.writeInt() 行为改变了吗?
【发布时间】:2016-09-11 19:23:01
【问题描述】:

我有一个处理套接字连接的简单类:

public class SimpleConnection {

    // Socket, input and output streams
    protected Socket mSocket;

    protected DataInputStream mIn;

    protected DataOutputStream mOut;

    public boolean createConnection(String ip, int port) {
        SocketAddress socketAddress = new InetSocketAddress(ip, port);
        mSocket = new Socket();
        try {
            mSocket.connect(socketAddress, 3000);
            mIn = new DataInputStream(mSocket.getInputStream());
            mOut = new DataOutputStream(mSocket.getOutputStream());
        } catch (IOException e) {
            return false;
        }

        return true;
    }

    public boolean sendData(byte[] data) {
        try {
            mOut.writeInt(data.length);
            mOut.write(data);
            mOut.flush();
        } catch (Exception e) {
            e.printStackTrace();
            closeSocket();
            return false;
        }
        return true;
    }
}

这在 Android N 之前一直有效。对于 Android N,mOut.writeInt(data.length) 只发送四个零,而不是 data.length 的长度。这会导致服务器误解消息并导致整个程序无法运行。

我能够通过将整数转换为字节[4]来“解决”问题:

byte[] len = Utilities.intToByteArray(data.length);
mOut.write(len);

intToByteArray 显示为here

我的问题是:为什么writeInt 不再在 Android N 上运行?在其他 Android 版本上,此代码运行良好。

我使用带有 Java 8、gradle 2.1.3 和 Android buildtools 24.0.2 的最新 Android Studio。

编辑: 接收部分在 Qt 中是这样的:

void readData(QTcpSocket* client_) {
  while (client_->bytesAvailable()) {
    int expected_length_;
    QDataStream s(client_);
    s >> expected_length_;
    qLog(Debug) << expected_length_;

    // Read data with expected_length_
    QBuffer buffer_;
    buffer_.write(client_->read(expected_length_));
  }
}

expected_length_0,修复后它是 15。有趣的是,client_-&gt;bytesAvailable() 在 Android N 上是 1writeInt 变体。

我用nc -p 1234 -l 0.0.0.0 | xxd做了另一个测试:

▶ nc -p 1234 -l 0.0.0.0 | xxd
00000000: 0000 000f 0815 1001 aa01 0808 8edb 0110  ................
00000010: 0118 00                                  ...

这是两种变体的输出...所以writeInt() 似乎按预期工作,但为什么它适用于 Android

编辑2:

在分析流量后,我发现整数被拆分为多个 TCP 帧。我更改了服务器代码以检查是否client_-&gt;bytesAvailable() &gt;= 4,然后才从套接字读取整数。这解决了问题,writeInt() 变体现在也可以工作了。

但是为什么行为突然改变了呢?

【问题讨论】:

  • 那么您可以保证数组的长度实际上不是0?声称内部方法被破坏是一种指责。
  • 是的,data.length15 而不是 0
  • 这难以置信。如果这种方法被破坏了一半,JDK 将无法工作。显示您的接收代码。
  • 我完全了解 zip 关于 Qt 但肯定 s &gt;&gt; expected_length 进行 ASCII 转换?而且您还没有显示实际读取那么多字节的部分,这是大多数这种性质的代码失败的地方。在 Java 中,您需要使用 DataInputStream.readFully() 或相应的循环:否则您将与发送者不同步。
  • 我编辑了服务器端代码。 expected_length_ 为 0,因此 buffer_ 没有数据。问题是,服务器代码和客户端代码可以运行数月甚至数年。只有Android N,它突然停止工作。 Android N 在为 int 发送完整的四个字节之前是否已刷新数据?

标签: java android sockets


【解决方案1】:

分析流量后发现writeInt()刷新数据

它完全按照您的指示去做。 DataOutputStream没有缓存,下面也没有BufferedOutputStream,所以writeInt()向网络写入了四个字节。

过早。

在 TCP 中没有“过早”这样的东西。 TCP 不保证数据包化或分段。如果要控制这种所谓的“过早刷新”,请使用mOut = new DataOutputStream(new BufferedOutputStream(Socket.getOutputStream()));,并在写入数据后自行刷新。

所以有些帧只有一两个字节,这就是为什么 bytesAvailable 只有 1。

  • 这是您的主要问题。你在滥用available()。它不是消息结束指示符。请参阅 Javadoc。
  • 您也没有检查是否实际读取了长度字的四个字节。
  • 据我所知,您也没有检查是否已收到所有数据字节。

一般来说,您必须循环,直到获得预期的所有内容。

服务器代码 QDataStream 仅读取一两个字节的 int。添加if (client_-&gt;bytesAvailable() &lt; 4) break; 并等待更多数据后,它可以工作。但我仍然不明白为什么行为会改变。

它可以随时更改。您的代码出错了,因为它依赖于几个无效的假设。

【讨论】:

  • 感谢您的回答,我现在明白我做错了什么。我按照您的建议更改了mOut 以添加BufferedOutputStream,并在服务器代码中正确检查了长度。
  • 哇,非常感谢您的回答!甚至我也面临同样的问题(该应用程序在 Marshmallow n 上运行,而不是在 Nougat 上运行)。这真是太棒了!
猜你喜欢
  • 2011-02-13
  • 1970-01-01
  • 2020-07-15
  • 2015-12-16
  • 1970-01-01
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
  • 2020-08-27
相关资源
最近更新 更多