NIO的奇怪事件 非常奇怪! 救救我!_!

下面一个客户端代码(NioClient)一个服务器端代码(NioServer)
先运行NioServer 再运行NioClient 第一次正常 但隐藏着3个奇怪的地方:

1:
一关闭NioClient(NioServer还开着), NioServer又会发现一个SelectionKey.OP_READ事件,就是原先那个处理过的事件,都不知道为什么?
2:
一关闭NioServer(NioClient还开着),NioClient又会发现无数个SelectionKey.OP_READ事件(永远不会停止),都是原先那个处理过的事件,又都不知道为什么?
3:
关闭所有的重新开始;先开一个NioServer和一个NioClient(都不要关掉),再开个NioClient,这时第二个开的NioClient会发现无数个SelectionKey.OP_READ事件(永远不会停止),也都是原先那个处理过的事件,又又都不知道为什么?

气炸了 希望高手指点 超谢


import java.io.*;
import java.util.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.channels.*;

public class NioServer
{
Charset charset=Charset.forName("gbk");
CharsetDecoder decoder=charset.newDecoder();
CharsetEncoder encoder=charset.newEncoder();
Selector selector;
ServerSocketChannel server;
ByteBuffer bBuffer=ByteBuffer.allocate(1024);
CharBuffer cBuffer=CharBuffer.allocate(1024);

public NioServer(int port)
{
try
{
selector=Selector.open();
server = ServerSocketChannel.open();
server.configureBlocking(false);
InetSocketAddress isa=new InetSocketAddress(
InetAddress.getLocalHost(),port);
server.socket().bind(isa);
server.register(selector,SelectionKey.OP_ACCEPT);
System.out.println("服务器启动!");
service();
}
catch(IOException e)
{
System.out.println(e);
}
}
public void service()throws IOException
{
while(true)
{
if(selector.select()<=0)
continue;
Set readyKeys=selector.selectedKeys();
Iterator i=readyKeys.iterator();
while(i.hasNext())
{
SelectionKey key=(SelectionKey)i.next();
i.remove();
if(key.isAcceptable())
{
System.out.println("isAcceptable()");
ServerSocketChannel keyChannel=(ServerSocketChannel)
key.channel();
SocketChannel sc=(SocketChannel)keyChannel.accept();
sc.configureBlocking(false);
sc.register(selector,SelectionKey.OP_READ);
}
else if(key.isReadable())
{
System.out.println("isReadable()");
SocketChannel keyChannel=(SocketChannel)key.channel();
keyChannel.read(bBuffer);
bBuffer.flip();
decoder.decode(bBuffer, cBuffer, false);
cBuffer.flip();
System.out.println("客户端:"+cBuffer+"服务器端:响应!");
String s="响应!";
keyChannel.write(encoder.encode(cBuffer.wrap(s)));
}
else if(key.isConnectable())
{
System.out.println("isConnectable()");
}
else if(key.isWritable())
{
System.out.println("isWritable()");
}
else if(key.isValid())
{
System.out.println("key.isValid()");
}
}
}
}

public static void main(String sun[])
{
new NioServer(10000);
}
}


import java.io.*;
import java.util.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.channels.*;

public class NioClient
{
Charset charset=Charset.forName("gbk");
CharsetDecoder decoder=charset.newDecoder();
CharsetEncoder encoder=charset.newEncoder();
Selector selector;
SocketChannel client;
ByteBuffer bBuffer=ByteBuffer.allocate(1024);
CharBuffer cBuffer=CharBuffer.allocate(1024);

public NioClient(int port)
{
try
{
selector=Selector.open();
InetSocketAddress isa=new InetSocketAddress(
InetAddress.getLocalHost(),port);
client=SocketChannel.open();
client.configureBlocking(false);
client.connect(isa);
client.register(selector,SelectionKey.OP_CONNECT |
SelectionKey.OP_READ);
System.out.println("客户端启动!");
service();
}
catch(IOException e)
{
System.out.println(e);
}
}
public void service()throws IOException
{
while(true)
{
if(selector.select()<=0)
continue;
Set readyKeys=selector.selectedKeys();
System.out.println("事件:"+readyKeys.size());
Iterator i=readyKeys.iterator();
while(i.hasNext())
{
SelectionKey key=(SelectionKey)i.next();
i.remove();
if(key.isAcceptable())
{
System.out.println("isAcceptable()");
}
else if(key.isReadable())
{
System.out.println("isReadable()");
SocketChannel keyChannel=(SocketChannel)key.channel();
keyChannel.read(bBuffer);
bBuffer.flip();
decoder.decode(bBuffer, cBuffer, false);
cBuffer.flip();
System.out.println(cBuffer);
}
else if(key.isConnectable())
{
System.out.println("isConnectable()");
SocketChannel keyChannel=(SocketChannel)key.channel();
if(keyChannel.isConnectionPending())
keyChannel.finishConnect();
String s="请求!";
keyChannel.write(encoder.encode(cBuffer.wrap(s)));
}
else if(key.isWritable())
{
System.out.println("isWritable()");
}
else if(key.isValid())
{
System.out.println("key.isValid()");
}
}
}
}

public static void main(String sun[])
{
new NioClient(10000);
}
}

昨天和同事研究了一下,经过他的提醒明白了一些道理,你先加上这个试试。

bBuffer.clear(); //加上试试
cBuffer.clear(); //加上试试
System.out.println("isReadable()");
SocketChannel keyChannel=(SocketChannel)key.channel();
keyChannel.read(bBuffer);
bBuffer.flip();
decoder.decode(bBuffer, cBuffer, false);
cBuffer.flip();

多说一句,jdon最近很不稳定,老是有NullPointerException,板桥,你就不能解决一下啊,我开始怀疑你那书的质量了:)(开玩笑的)

to agilejava
你回答得有道理。

关于jdon论坛18到19日一些问题,是由于数据库服务器问题,网管给数据库服务器增加了防简单DDOS攻击的IPtables策略,但是影响了正常的访问,所以,访问量增加,就无法连接数据库服务器。




这得归功于我的同事,没有他的提示我是不会明白的:)

数据库的问题应该提示一下啊,要不很容易让人以为是程序有问题的,NPE是最让人头痛的问题了,应该怎么做我想不用我这个小程序员告诉你了吧:)

还得多说一句,你的书我同事已经买了,感觉很实用,我大体看了一下,不错:)

当connection断线时
都会有OP_READ的event产生
这时候read会回传-1
此时你可以呼叫channel.close();
去关闭这个connection

会这样的设计的原因是
让你的application可以知道一个channel断线的讯息
所以你没有执行close
则OP_READ的event就会一直发生

to popcorny:
我认为不是这样的,因为在测试时发现如果服务器关了,client根本检查不到这个问题,它认为这时不有数据可读,所以还是在一个劲儿的读数据,有时间你可以看看这个,一起研究:)

http://www.blogbus.com/blogbus/blog/diary.php?diaryid=145602

to popcorny:
我认为不是这样的,因为在测试时发现如果服务器关了,client根本检查不到这个问题,它认为这时还有数据可读,所以还是在一个劲儿的读数据,有时间你可以看看这个,一起研究:)

http://www.blogbus.com/blogbus/blog/diary.php?diaryid=145602

其实这看这个原码真的错误百出

第一个..
read完..原buffer要clear
write完..原buffer要呼叫clear或是compact
这个是很基本的
就是这边导致第二次read的时候出现IOException

第二个
由于while loop的内部没有去处理IOException
导致内部一有发生IOException
则while loop就终止
整个程序也应此结束
非常不reliable

第三个
没有正确处理断线讯息
read() == -1 则代表读到EOF
这时候就要呼叫channel.close()
若没有调用这个method..
则read event还是会不断产生

所以以上三点可以合理解释为什么会出现您的这三种怪现象
有关nio的正确使用方式
可以在这边找到范例

http://javaalmanac.com/egs/java.nio/pkg.html

这种应用并不是没有,像我们这儿做的就有长连接,如果断了还要重新连接,什么事都有可能的:)

非常感谢大家对我的帮助
谢谢 非常的 很感激 几乎救了我一命(开玩笑) 呵呵

应该是client已经发EOF了,服务器就会一直OP_READ啦

我觉得不是agileJava所说的那样
因为你在读完一个数据后对bBuffer执行了flip()方法,这样的话bBuffer的limit就被设定为当前读到的位置,所以他下次再读的话,因为你的bBuffer是根本些不进去数据的,所以通道里面的数据根本没有被读出来,所以这个key一直是有效的,因为你在读完一次后,下面的数据根本没有被读出来。

当然按照aglieJava的方法,在每次读数据之前先clear这样的话,bBuffer的limit值又回到了容量的值,所以他加上这句话之后,你的错误就消失,但是这个做法是又隐患的。因为如果他一次没有读到整个包的数据,那么你以前的数据就丢失了,除非你把它保存起来。

正解。