我使用Java套接字开发了一个应用程序。我正在字节数组的帮助下与此应用程序交换消息。我有一条名为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软件包正确附带了此消息。因此,发件人方面没有问题。我100%确定消息已正确到达,但read方法似乎未按正确的顺序读取,并且这种情况并非总是会发生。我找不到造成此问题的正当理由。
当我在两台不同的计算机上尝试相同的代码时,我注意到问题仅出在一个。这些计算机上的jdk版本不同。我看了两个jdk版本之间的版本差异。当Jdk版本是“JDK 8u202”时,我遇到了它无法正常工作的情况。当我用jdk 8u271尝试时,没有问题。也许与此有关,但我不确定。因为我没有有效的证据。
我愿意接受各种想法和建议。它确实正在成为我遇到过的最有趣的问题。
谢谢您的帮助。
编辑:我发现了类似的问题。
Blocking Queue Take out of Order
编辑:
好的,我已经阅读了下面给出的所有答案。感谢您为我提供不同的观点。我将尝试补充一些缺失的信息。
其实我有2个线程。线程1(SocketReader)负责读取套接字。它使用Chunk类包装读取的信息,并将其放在另一个线程2中的队列中。因此,队列在线程2中。线程2(MessageDecoder)正在使用阻塞队列。除这些以外,没有其他线程。实际上,这是“生产者消费者设计模式”的简单示例。
是的,发送了其他消息,但是其他消息占用的空间少于512个字节。因此,我可以一口气阅读。我没有遇到任何排序问题。
MessageDecoder.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.javapublic 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
的锁定也不公平,因此您不能保证元素的顺序。有关此here的更多信息
我建议改用ArrayBlockingQueue
。像LinkedBloquingQueue
一样,不能保证顺序,但是提供了稍微不同的锁定机制。
This class supports an optional fairness policy for ordering waiting producer and consumer threads. By default, this ordering is not guaranteed. However, a queue constructed with fairness set to true grants threads access in FIFO order. Fairness generally decreases throughput but reduces variability and avoids starvation.
为了设置公平,您必须使用以下构造函数对其进行初始化:
因此,例如:
ArrayBlockingQueue<Chunk> fairQueue = new ArrayBlockingQueue<>(1000, true);
/*.....*/
Chunk c = new Chunk(arr);
fairQueue.add(c);
如文档所述,这应按FIFO
顺序授予线程访问权限,以确保元素的检索是一致的,同时避免LinkedBloquingQueue
的锁定机制中可能发生的抢劫。
关于java - Java Socket InputStream读取数据,但返回顺序错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65754514/