繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM