在JDK 1.7中阻塞网络NIO期间的IOException

时间:2013-10-21 15:51:56

标签: java java-7 nio

我只是在学习NIO,这是我编写的一个简短示例,用于测试如何阻止阻塞NIO:

class TestBlockingNio {
    private static final boolean INTERRUPT_VIA_THREAD_INTERRUPT = true;

    /**
     * Prevent the socket from being GC'ed
     */
    static Socket socket;

    private static SocketChannel connect(final int port) {
        while (true) {
            try {
                final SocketChannel channel = SocketChannel.open(new InetSocketAddress(port));
                channel.configureBlocking(true);
                return channel;
            } catch (final IOException ioe) {
                try {
                    Thread.sleep(1000);
                } catch (final InterruptedException ie) {
                }
                continue;
            }
        }
    }

    private static byte[] newBuffer(final int length) {
        final byte buffer[] = new byte[length];
        for (int i = 0; i < length; i++) {
            buffer[i] = (byte) 'A';
        }
        return buffer;
    }

    public static void main(final String args[]) throws IOException, InterruptedException {
        final int portNumber = 10000;

        new Thread("Reader") {
            public void run() {
                try {
                    final ServerSocket serverSocket = new ServerSocket(portNumber);
                    socket = serverSocket.accept();
                    /*
                     * Fully ignore any input from the socket
                     */
                } catch (final IOException ioe) {
                    ioe.printStackTrace();
                }
            }

        }.start();

        final SocketChannel channel = connect(portNumber);

        final Thread main = Thread.currentThread();
        final Thread interruptor = new Thread("Inerruptor") {
            public void run() {
                System.out.println("Press Enter to interrupt I/O ");
                while (true) {
                    try {
                        System.in.read();
                    } catch (final IOException ioe) {
                        ioe.printStackTrace();
                    }
                    System.out.println("Interrupting...");
                    if (INTERRUPT_VIA_THREAD_INTERRUPT) {
                        main.interrupt();
                    } else {
                        try {
                            channel.close();
                        } catch (final IOException ioe) {
                            System.out.println(ioe.getMessage());
                        }
                    }
                }
            }
        };
        interruptor.setDaemon(true);
        interruptor.start();

        final ByteBuffer buffer = ByteBuffer.allocate(32768);    
        int i = 0;

        try {
            while (true) {
                buffer.clear();
                buffer.put(newBuffer(buffer.capacity()));
                buffer.flip();
                channel.write(buffer);
                System.out.print('X');
                if (++i % 80 == 0) {
                    System.out.println();
                    Thread.sleep(100);
                }
            }
        } catch (final ClosedByInterruptException cbie) {
            System.out.println("Closed via Thread.interrupt()");
        } catch (final AsynchronousCloseException ace) {
            System.out.println("Closed via Channel.close()");
        }
    }
}

在上面的例子中,我正在写一个SocketChannel,但没有人从另一边读取,所以写操作最终会挂起。

此示例在JDK-1.6运行时效果很好,具有以下输出:

    Press Enter to interrupt I/O 
    XXXX
    Interrupting...
    Closed via Thread.interrupt()

- 意味着只有128k的数据被写入TCP套接字的缓冲区。 然而,当由JDK-1.7(1.7.0_25-b15和1.7.0-u40-b37)运行时,使用IOException完全相同的代码:

    Press Enter to interrupt I/O 
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXX
    Exception in thread "main" java.io.IOException: Broken pipe
        at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
        at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
        at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
        at sun.nio.ch.IOUtil.write(IOUtil.java:65)
        at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:487)
        at com.example.TestBlockingNio.main(TestBlockingNio.java:109)

有人可以解释这种不同的行为吗?

1 个答案:

答案 0 :(得分:0)

显然,特定JVM版本如何将服务器套接字绑定到端口存在差异。

事实证明我有第三方进程在127.0.0.1:10000收听, 但在BindException期间从未导致SocketChannel.open(new InetSocketAddress(10000))

同时,1.6-VM使用en0成功连接到自身, 1.7-VM使用lo0连接到外部进程。 一旦该进程被终止,JVM行为在不同版本中再次保持一致。