java - Java Socket InputStream读取数据,但返回顺序错误

标签 java sockets

我使用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.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的锁定也不公平,因此您不能保证元素的顺序。有关此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.


为了设置公平,您必须使用以下构造函数对其进行初始化:
enter image description here
因此,例如:
   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/

相关文章:

java - 如何在java中获取结果集的长度或大小

java - Java SE 应用程序中的 Hibernate 并发问题

Android 的幻灯片菜单库的 Java 实现

sockets - 使用 NetworkStream 类时检测客户端 TCP 断开连接

android - 移动实时同步(如 Trello) : sockets?

c - sin_addr.s_addr = INADDR_ANY;需要 htonl 吗?

java - 如何从 BigQuery 检索数据并将数据插入到 mySQL?

Java/反射 - 错误在哪里?

c# - 设计一个sqlite server客户端程序

ios - 您可以使用 iOS 在本地主机上设置监听器套接字吗?