问题描述如下:
鄙人在写nio简单例子的时候出现了很奇怪的问题,服务器端部分代码如下:
while (selector.select() > 0) 
{
  Set<SelectionKey> readyKeys = selector.selectedKeys();
   Iterator it = readyKeys.iterator();
   System.out.println("while out");
   while (it.hasNext()) 
   {
      System.out.println("while in");
      SelectionKey key = (SelectionKey) it.next();
      it.remove();
      if (key.isAcceptable()) 
{
   System.out.println("Key is Acceptable");
   socket = (SocketChannel) ssc.accept();
   socket.configureBlocking(false);
   socket.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
}
if (key.isReadable()) {

    System.out.println("Key is readable");
    socket = (SocketChannel) key.channel();
    ByteBuffer buf = ByteBuffer.allocate(25);
    buf.clear();
    socket.read(buf);
    buf.flip();
    Charset charset = Charset.forName("us-ascii");
    CharsetDecoder decoder = charset.newDecoder();
    CharBuffer charBuffer = decoder.decode(buf);
    String result = charBuffer.toString();
    System.out.println("Receive Data:" + result);
    key.cancel();//如果这个注释的话,就会进行无限循环
}

}客户端代码:public static int sendMessage(SocketChannel client) {
String msg = null;
ByteBuffer bytebuf=ByteBuffer.allocate(1024);
int nBytes = 0;
try {
msg = "It's message from client!";
System.out.println("msg is "+msg);


bytebuf.clear();
bytebuf.put(msg.getBytes());
bytebuf.flip();
nBytes = client.write(bytebuf);
System.out.println("It's message from client!".getBytes().length);


client.close(); } catch (IOException e) {
e.printStackTrace();
}
return nBytes;
 }客户端和服务器端的部分代码就是上面的,客户端发送数据给服务器端,服务器按道理执行了
SelectionKey key = (SelectionKey) it.next();
it.remove();的代码之后注销了该键,如果执行完读操作之后,只要加上key.cancel()之后才可以正常读取一条数据。it.remove()之后为什么selector.select() > 0依然成立,运行代码的打印结果:
while out
while in
Key is readable
Receive Data:
while out
while in
Key is readable
Receive Data:
while out
while in
Key is readable
Receive Data:。。
求高手解答。

解决方案 »

  1.   

    关键在于服务端在读取数据的时候,没有判断是否读到流末尾。
    楼主的socket.read(buf);一直返回-1,你没有进行判断处理,加上这个判断就不会死循环了。int length = socket.read(buf);
    if(length == -1){
    System.out.println("读到流末尾,说明客户端断开连接了。");
    key.cancel();
    key.channel().close();
    break;
    }
      

  2.   


    将管道关闭的前提是,另一方已经主动断开连接了。
    楼主你的代码客户端发生数据后,调用 client.close(); 把socket给关闭了。
    所以服务端那边在读数据的时候,如果判断为-1,说明你客户端都主动断开连接了,那么自然需要把监听取消和管道关闭了。你前面的问题就是因为没有做这个判断,所以才会陷入一个死循环一直在读数据。
      

  3.   

    其实最想问的问题是,key.cancel()函数,其实没有把通道关闭是吧,这个函数具体作用是什么?看api没看懂。
      

  4.   


    public abstract void cancel()
            使SelectionKey对象失效。
            该方法把SelectionKey对象加入到与它关联的Selector对象的cancelled-keys集合中。当程序下一次执行Selector的select()方法时,该方法会把SelectionKey对象从3个集合中删除。
      

  5.   

    API上的说明:
    每次向选择器注册通道时就会创建一个选择键。通过调用某个键的 cancel 方法、关闭其通道,或者通过关闭其选择器来取消 该键之前,它一直保持有效。取消某个键不会立即从其选择器中移除它;相反,会将该键添加到选择器的已取消键集,以便在下一次进行选择操作时移除它。可通过调用某个键的 isValid 方法来测试其有效性。
      

  6.   

    http://blog.csdn.net/u011277203/article/details/9223545