Java 8非阻塞读取是否具有竞争条件?

时间:2015-06-26 12:28:19

标签: java multithreading tcp java-8 nonblocking

现在这个问题困扰了我一段时间。

在我正在使用的工作应用程序中,我使用非阻塞模式的SocketChannel与嵌入式设备进行通信。 现在我收到零星的损坏数据。 在某些PC上它不会发生,现在它发生在我的身上。 但是当我在程序中改变太多时,问题就会消失。

这么多可能会产生影响。时机,网络接口硬件,win7,java版本,公司防火墙,......

数据读取归结为此代码:

byteBuffer.compact();
socketChannel.read(byteBuffer); // <<< problem here ?
byteBuffer.flip();
if( byteBuffer.hasRemaining() ){
    handleData( byteBuffer );
}

当选择器唤醒并且设置了兴趣op OP_READ时,它在与写入相同的线程中运行。

此代码是唯一引用byteBuffer的地方。 socketChannel在写入时仅用于同一个线程。

我检测了代码,因此我可以在错误发生时打印出最后几个read()调用的内容。同时我分析了Wireshark上的网络流量。我添加了很多断言来检查bytebuffer的完整性。

在Wireshark中,收到的流看起来不错。没有DUP-ACK或其他可疑的东西。最后一次read()调用与Wireshark中的数据完全匹配。

在Wireshark中,我看到许多小型TCP帧以10ms到达的间隔接收90字节的有效载荷数据。通常情况下,Java线程在刚到达时都会读取所有10ms的数据。

当遇到问题时,Java线程有点延迟,因为读取发生在300ms之后,并且读取返回类似~3000字节这似乎是合理的。但数据已损坏。

如果数据被复制到缓冲区并且同时接收到的数据已覆盖第一个数据,则数据看起来像。

现在我不知道如何继续。我不能创造一个小例子,因为这很少发生,我不知道所需的确切条件。

有人可以提示吗?

我怎样才能证明,它是Java lib还是不是?

看什么条件也很重要?

感谢 弗兰克

29日至2015年:

现在我能够建立一个复制的例子。

有一个Sender和一个Receiver计划。

发件人使用阻塞IO,首先等待连接,然后每2ms发送90个字节块。前4个字节是运行计数器,其余未设置。发件人使用setNoTcpDelay(true)。

Receiver正在使用非阻塞IO。首先它连接到发送者,然后只要选择键准备就绪,它就会读取通道。有时,read循环执行Thread.sleep(300)。

如果他们通过环回在同一台PC上运行,这对我来说一直都有用。如果我将发件人放到另一台通过LAN直接连接的PC上,它会触发错误。使用Wireshark检查,流量和发送的数据看起来不错。

要运行,首先在一台PC上启动发件人,然后(在编辑主机地址后)启动接收器。

只要它有效,它每2秒打印一行。如果失败,则打印有关最后5次read()调用的信息。

我发现是触发器:

  1. 发件人已配置setNoTcpDelay(true)
  2. 接收器有时会在执行read()之前有一个Thread.sleep(300)。
  3. 感谢 弗兰克

2 个答案:

答案 0 :(得分:1)

        buf.order(ByteOrder.BIG_ENDIAN);

这是默认值。删除它。

        buf.clear();

缓冲区已经为空,因为您刚刚分配了它。删除它。

        buf.limit(0);

在clear()之后以及初始分配之后,限制已经为零。删除它。

        while( true ) {

这里应该有一个select()调用。

            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            // ...
            if( key == keyData && key.isConnectable() ) {
                    ch.finishConnect();

此方法可以返回false。你没有处理那个案子。

            // ...
            if( key == keyData && key.isReadable() ) {

                    // ...
                    readPos += ch.read(buf);

完全错误。您完全忽略了read()返回-1的情况,这意味着对等方已断开连接。在这种情况下,您必须关闭频道。

            // without this Thread.sleep, it would not trigger the error

所以?这便士没掉了吗? 取消睡眠。这完全没有意义。 select()将阻止,直到数据到达。它不需要你的帮助。这种睡眠实际上是浪费时间。

            if( rnd.nextInt(20) == 0 ) {
                Thread.sleep(300);
            }

删除它。

            selector.select();

这应该位于循环的顶部,而不是底部。

答案 1 :(得分:0)

我原来是一个司机问题,至少看起来如此。

我使用USB转以太网适配器&#34; D-Link E-DUB100 Rev A &#34;。
由于wireshark显示正确的数据,我认为可以消除硬件可能的故障原因 但与此同时我尝试了#D; Link E-DUB100 Rev C1 &#34;问题就消失了 所以我认为这是D-Link为Rev A提供的驱动程序中的问题。对于Rev C1,它可能使用没有此问题的系统驱动程序。

所有人都花时间阅读我的问题。