我只是在学习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)
有人可以解释这种不同的行为吗?
答案 0 :(得分:0)
显然,特定JVM版本如何将服务器套接字绑定到端口存在差异。
事实证明我有第三方进程在127.0.0.1:10000收听,
但在BindException
期间从未导致SocketChannel.open(new InetSocketAddress(10000))
。
同时,1.6-VM使用en0
成功连接到自身,
1.7-VM使用lo0
连接到外部进程。
一旦该进程被终止,JVM行为在不同版本中再次保持一致。