【发布时间】: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_->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
编辑2:
在分析流量后,我发现整数被拆分为多个 TCP 帧。我更改了服务器代码以检查是否client_->bytesAvailable() >= 4,然后才从套接字读取整数。这解决了问题,writeInt() 变体现在也可以工作了。
但是为什么行为突然改变了呢?
【问题讨论】:
-
那么您可以保证数组的长度实际上不是
0?声称内部方法被破坏是一种指责。 -
是的,
data.length是15而不是0。 -
这难以置信。如果这种方法被破坏了一半,JDK 将无法工作。显示您的接收代码。
-
我完全了解 zip 关于 Qt 但肯定
s >> expected_length进行 ASCII 转换?而且您还没有显示实际读取那么多字节的部分,这是大多数这种性质的代码失败的地方。在 Java 中,您需要使用DataInputStream.readFully()或相应的循环:否则您将与发送者不同步。 -
我编辑了服务器端代码。
expected_length_为 0,因此buffer_没有数据。问题是,服务器代码和客户端代码可以运行数月甚至数年。只有Android N,它突然停止工作。 Android N 在为 int 发送完整的四个字节之前是否已刷新数据?