对于以下异常的记录:
<pre name="code" class="java"><pre name="code" class="java">java.io.IOException: 远程主机强迫关闭了一个现有的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(Unknown Source)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
at sun.nio.ch.IOUtil.read(Unknown Source)
at sun.nio.ch.SocketChannelImpl.read(Unknown Source)
at mysite.socket.MyServer$1.run(MyServer.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
一.以上异常的再现方法:
1.服务器开启socket监听,并通过nio的模式读,如下例:
int keysNum = selector.selectNow();
Iterator<SelectionKey> iterator= keysNum>0?selector.selectedKeys().iterator():null;
while(iterator!=null && iterator.hasNext() ){//while
SelectionKey sk = iterator.next();
iterator.remove();
if(sk.isValid()){
if(sk.isReadable()){
SocketBufferHandler scBufferHandler = (SocketBufferHandler)sk.attachment();
SocketChannel sc = scBufferHandler.getSocketChannel();
ByteBuffer buffer = scBufferHandler.getBuffer();
sc.read(buffer);//这个位置发生了异常
if(!buffer.hasRemaining()){
scBufferHandler.expand();
}
System.out.println(buffer);
}
}
}//while
2.客户端socket连接服务器,然后发送数据,发送完之后,客户端程度立刻停止,如下例:
ByteBuffer buf = ByteBuffer.allocate(100);
String str="hello world!!!";
buf.put(str.getBytes());
buf.flip();
for(int i=0;i<1000;i++){
while(buf.hasRemaining()){
socketChannel.write(buf);
}
buf.flip();
}
//这个close函数必须执行,否则会发生异常。
//socketChannel.close();
3.前面的循环会正常接收(具体循环到多少没有细算),但是至少最后一个会发生异常(大概)。
二.原因分析
常规的思想是认为:客户端发送完数据之后,不管客户端处于什么样的状态,包括异常状态,服务器都应该能正常接收到数据。但是上面的结果不如人意。
原因是什么???
三.nio循环读取
当执行socketChannel.close();方法时(即使客户端不执行write方法),虽然不报异常了,但是下面的方法竟然还是会循环。
if(sk.isReadable()){
......
}
这可能是因为当客户端执行close后,会发送FIN包,服务器端java认为这是isReadable的,于是就会陷入无限循环。下表对比了FIN包的特殊性。
mina的处理方法:
2017.08.28 =>