Android N - DataOutputStream.writeInt()行为发生了变化?

时间:2016-09-11 19:23:02

标签: java android sockets

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

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

我的问题是:为什么我的{N} writeInt不再在Android N上工作了?在其他Android版本上,此代码运行正常。

我使用最新的Android Studio与Java 8,gradle 2.1.3和Android buildtools 24.0.2。

编辑: 接收部分在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_->bytesAvailable()在Android N上的1变体为writeInt

我使用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&lt; = 6而不适用于Android 7?!??

EDIT2:

在分析流量后,我发现整数在多个TCP帧中被分割。我更改了服务器代码以检查client_->bytesAvailable() >= 4是否只读取套接字中的整数。这解决了问题,writeInt()变体现在也可以使用。

但为什么这种行为会突然改变?

1 个答案:

答案 0 :(得分:3)

  

在分析流量后,我发现writeInt()刷新了数据

它完全按照你的要求去做。 DataOutputStream未缓冲,并且其下没有BufferedOutputStream,因此writeInt()向网络写入了四个字节。

  

过早。

没有过早的&#39;在TCP中。 TCP不保证打包或分段。如果你想控制这个所谓的过早冲洗&#39;,请使用mOut = new DataOutputStream(new BufferedOutputStream(Socket.getOutputStream()));并在写完数据后自行冲洗。

  

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

  • 这是你的主要问题。你在滥用available()。它不是消息指示符的结尾。见Javadoc。
  • 你也没有检查过你是否真的读过长度字的四个字节。
  • 据我所知,您并未检查是否已收到所有数据字节。

一般来说,你必须循环,直到你得到所期望的一切。

  

服务器代码QDataStream只用一个或两个字节读取int。添加if (client_->bytesAvailable() < 4) break;并等待更多数据后,它可以正常工作。但我仍然不明白行为改变的原因。

它可以随时改变。你的代码破了,因为它依赖于几个无效的假设。