我有一个处理套接字连接的简单类:
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()
变体现在也可以使用。
但为什么这种行为会突然改变?
答案 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;
并等待更多数据后,它可以正常工作。但我仍然不明白行为改变的原因。
它可以随时改变。你的代码破了,因为它依赖于几个无效的假设。