[英]Java Socket InputStream reads data but return in the wrong order
我使用 Java 套接字开发了一个应用程序。 我在字节 arrays 的帮助下与此应用程序交换消息。 我有一条名为 M1 的消息,长度为 1979 字节。 我的套接字缓冲区长度是 512 字节。 我分 4 部分阅读这条消息,每部分有 512 个字节,但最后一个当然是 443 个字节。 我将这些部分命名为 A、B、C 和 D。所以 ABCD 分别是我的有效消息。
我有一个带有线程的循环,如下所示。
BlockingQueue<Chunk> queue = new LinkedBlockingQueue<>();
InputStream in = socket.getInputStream()
byte[] buffer = new byte[512];
while(true) {
int readResult = in.read(buffer);
if(readResult != -1) {
byte[] arr = Arrays.copyOf(buffer, readResult);
Chunk c = new Chunk(arr);
queue.put(c);
}
}
我正在用上面的代码填充队列。 当消息发送开始时,我看到队列以 ABCD 形式填满,但有时我将数据作为 BACD 放入队列中。 但我知道这是不可能的,因为 TCP 连接保证了顺序。
我用 Wireshark 查看了转储。 此消息正确带有单个 tcp package。 所以发送方没有问题。 我 100% 确定消息已正确到达,但 read 方法似乎没有按正确的顺序读取,而且这种情况并不总是发生。 我找不到造成这种情况的正当理由。
当我在两台不同的计算机上尝试相同的代码时,我注意到问题只出在一台计算机上。 这些电脑上的jdk版本不同。 我查看了两个 jdk 版本之间的版本差异。 当 Jdk 版本是“JDK 8u202”时,我遇到了它工作不正常的情况。 当我用 jdk 8u271 尝试时,没有问题。 也许它与此有关,但我不确定。 因为我没有有效的证据。
我对各种想法和建议持开放态度。 它真的正在成为我遇到过的最有趣的问题。 谢谢您的帮助。
编辑:我发现了类似的问题。 阻塞队列乱序
编辑:好的,我已经阅读了下面给出的所有答案。 谢谢你为我提供了不同的视角。 我将尝试补充一些缺失的信息。
其实我有2个线程。 线程 1(SocketReader) 负责读取套接字。 它用块 class 包装它读取的信息,并将其放在另一个线程 2 的队列中。因此队列在线程 2 中。线程 2(消息解码器)正在使用阻塞队列。 除了这些,没有其他线程。 其实这是一个“生产者消费者设计模式”的简单例子。
是的,发送了其他消息,但其他消息占用不到 512 字节。 因此,我可以读入一本 go。 我没有遇到任何排序问题。
消息解码器.java
public class MessageDecoder implements Runnable{
private BlockingQueue<Chunk> queue = new LinkedBlockingQueue<>();
public MessageDecoder() {
}
public void run() {
while(true) {
Chunk c;
try {
c = queue.take();
System.out.println(c.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
decodeMessageChunk(c);
}
}
public void put(Chunk c) {
try {
queue.put(c);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
SocketReader.java
public class SocketReader implements Runnable{
private final MessageDecoder msgDec;
private final InputStream in;
byte[] buffer = new byte[512];
public SocketReader(InputStream in, MessageDecoder msgDec) {
this.in = in;
this.msgDec = msgDec;
}
public void run() {
while(true) {
int readResult = in.read(buffer);
if(readResult != -1) {
byte[] arr = Arrays.copyOf(buffer, readResult);
Chunk c = new Chunk(arr);
msgDec.put(c);
}
}
}
}
即使是FIFO
队列, LinkedBloquingQueue
的加锁也是不公平的,所以不能保证元素的顺序。 关于这里的更多信息
我建议改用ArrayBlockingQueue
。 与LinkedBloquingQueue
一样,不能保证顺序,但提供了稍微不同的锁定机制。
此 class 支持对等待生产者和消费者线程进行排序的可选公平策略。 默认情况下,不保证此排序。 但是,公平性设置为 true 的队列以 FIFO 顺序授予线程访问权限。 公平性通常会降低吞吐量,但会降低可变性并避免饥饿。
为了设置fairness ,您必须使用以下构造函数对其进行初始化:
因此,例如:
ArrayBlockingQueue<Chunk> fairQueue = new ArrayBlockingQueue<>(1000, true);
/*.....*/
Chunk c = new Chunk(arr);
fairQueue.add(c);
正如文档 state 一样,这应该以FIFO
顺序授予线程访问权限,保证元素的检索是一致的,同时避免在LinkedBloquingQueue
的锁定机制中发生可能的锁定抢劫。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.