简体   繁体   English

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

[英]Java Socket InputStream reads data but return in the wrong order

I developed an application using Java socket.我使用 Java 套接字开发了一个应用程序。 I am exchanging messages with this application with the help of byte arrays.我在字节 arrays 的帮助下与此应用程序交换消息。 I have a message named M1, 1979 bytes long.我有一条名为 M1 的消息,长度为 1979 字节。 My socket buffer length is 512 bytes.我的套接字缓冲区长度是 512 字节。 I read this message in 4 parts, each with 512 bytes, but the last one is of course 443 bytes.我分 4 部分阅读这条消息,每部分有 512 个字节,但最后一个当然是 443 个字节。 I will name these parts like A, B, C, and D. So ABCD is a valid message of mine respectively.我将这些部分命名为 A、B、C 和 D。所以 ABCD 分别是我的有效消息。

I have a loop with a thread which is like below.我有一个带有线程的循环,如下所示。

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);
  }
}
    

I'm filling the queue with the code above.我正在用上面的代码填充队列。 When the message sending starts, I see the queue fill up in ABCD form but sometimes I put the data in the queue as a BACD.当消息发送开始时,我看到队列以 ABCD 形式填满,但有时我将数据作为 BACD 放入队列中。 But I know that this is impossible because the TCP connection guarantees the order.但我知道这是不可能的,因为 TCP 连接保证了顺序。

I looked at the dumps with Wireshark.我用 Wireshark 查看了转储。 This message comes correctly with a single tcp package.此消息正确带有单个 tcp package。 So there is no problem on the sender side.所以发送方没有问题。 I am 100% sure that the message has arrived correctly but the read method does not seem to read in the correct order and this situation doesn't always happen.我 100% 确定消息已正确到达,但 read 方法似乎没有按正确的顺序读取,而且这种情况并不总是发生。 I could not find a valid reason for what caused this.我找不到造成这种情况的正当理由。

When I tried the same code on two different computers I noticed that the problem was in only one.当我在两台不同的计算机上尝试相同的代码时,我注意到问题只出在一台计算机上。 The jdk versions on these computers are different.这些电脑上的jdk版本不同。 I looked at the version differences between the two jdk versions.我查看了两个 jdk 版本之间的版本差异。 When the Jdk version is "JDK 8u202", I am getting the situation where it works incorrectly.当 Jdk 版本是“JDK 8u202”时,我遇到了它工作不正常的情况。 When I tried it with jdk 8u271, there was no problem.当我用 jdk 8u271 尝试时,没有问题。 Maybe it is related to that but I wasn't sure.也许它与此有关,但我不确定。 Because I have no valid evidence.因为我没有有效的证据。

I am open to all kinds of ideas and suggestions.我对各种想法和建议持开放态度。 It's really on its way to being the most interesting problem I've ever encountered.它真的正在成为我遇到过的最有趣的问题。 Thank you for your help.谢谢您的帮助。

EDIT: I found similar question.编辑:我发现了类似的问题。 Blocking Queue Take out of Order 阻塞队列乱序

EDIT: Ok, I have read all the answers given below.编辑:好的,我已经阅读了下面给出的所有答案。 Thank you for providing different perspectives for me.谢谢你为我提供了不同的视角。 I will try to supplement some missing information.我将尝试补充一些缺失的信息。

Actually I have 2 threads.其实我有2个线程。 Thread 1(SocketReader) is responsible for reading socket.线程 1(SocketReader) 负责读取套接字。 It wraps the information it reads with a Chunk class and puts it on the queue in the other Thread 2. So queue is in Thread 2. Thread 2(MessageDecoder) is consuming the blocking queue.它用块 class 包装它读取的信息,并将其放在另一个线程 2 的队列中。因此队列在线程 2 中。线程 2(消息解码器)正在使用阻塞队列。 There are no threads other than these.除了这些,没有其他线程。 Actually this is a simple example of a "producer consumer design patter".其实这是一个“生产者消费者设计模式”的简单例子。

And yes, other messages are sent, but other messages take up less than 512 bytes.是的,发送了其他消息,但其他消息占用不到 512 字节。 Therefore, I can read in one go.因此,我可以读入一本 go。 I do not encounter any sort problem.我没有遇到任何排序问题。

MessageDecoder.java消息解码器.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 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);
            }
        }
    }
}

Even if it's a FIFO queue, the locking of the LinkedBloquingQueue is unfair, so you can't guarantee the ordering of elements.即使是FIFO队列, LinkedBloquingQueue的加锁也是不公平的,所以不能保证元素的顺序。 More info regarding this here关于这里的更多信息

I'd suggest using an ArrayBlockingQueue instead.我建议改用ArrayBlockingQueue Like the LinkedBloquingQueue , the order is not guaranteed but offers a slightly different locking mechanism.LinkedBloquingQueue一样,不能保证顺序,但提供了稍微不同的锁定机制。

This class supports an optional fairness policy for ordering waiting producer and consumer threads.此 class 支持对等待生产者和消费者线程进行排序的可选公平策略。 By default, this ordering is not guaranteed .默认情况下,不保证此排序 However, a queue constructed with fairness set to true grants threads access in FIFO order .但是,公平性设置为 true 的队列以 FIFO 顺序授予线程访问权限 Fairness generally decreases throughput but reduces variability and avoids starvation.公平性通常会降低吞吐量,但会降低可变性并避免饥饿。

In order to set fairness , you must initialize it using this constructor:为了设置fairness ,您必须使用以下构造函数对其进行初始化:

在此处输入图像描述

So, for example:因此,例如:

   ArrayBlockingQueue<Chunk> fairQueue = new ArrayBlockingQueue<>(1000, true);
   /*.....*/
   Chunk c = new Chunk(arr);
   fairQueue.add(c);

As the docs state, this should grant thread access in FIFO order, guaranteeing the retrievement of the elements to be consistent while avoiding possible locking robbery happening in LinkedBloquingQueue 's lock mechanism.正如文档 state 一样,这应该以FIFO顺序授予线程访问权限,保证元素的检索是一致的,同时避免在LinkedBloquingQueue的锁定机制中发生可能的锁定抢劫

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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