Selector.select()读取SelectionKey.isReadable后不再阻塞重复读消息问题
问题原因与修复方案
核心问题分析
你的代码存在两个关键问题,导致selectorIO.select()无法阻塞并重复读取旧消息:
ByteBuffer未在每次循环前重置:
仅在方法开头调用了一次buffer.clear(),进入循环后未重新清空缓冲区。第一次读取完成后,缓冲区的位置和限制未重置,后续读取操作会重复使用缓冲区里的旧数据,即便没有新消息也会输出之前的内容。未处理连接关闭与读取返回值的情况:
客户端发送完消息后,若未关闭连接,channel.read(buffer)会返回0(无新数据);若客户端关闭连接,read会返回-1。这两种情况都未处理,导致SelectionKey一直处于可读状态,selector.select()会立即返回,触发重复的读事件。
修复后的代码
public void readData() { ByteBuffer buffer = ByteBuffer.allocate(8); while(true) { try { int numberOfKeys = selectorIO.select(); if (numberOfKeys == 0) { continue; } Set<SelectionKey> keys = selectorIO.selectedKeys(); Iterator<SelectionKey> itr = keys.iterator(); while(itr.hasNext()) { SelectionKey key = itr.next(); itr.remove(); if(key.isReadable()) { SocketChannel channel = (SocketChannel)key.channel(); buffer.clear(); int numread = channel.read(buffer); if (numread == -1) { key.cancel(); channel.close(); continue; } if (numread == 0) { continue; } buffer.flip(); String s = new String(buffer.array(), 0, numread); System.out.println(s); System.out.println(numread); buffer.flip(); int numwrote = channel.write(buffer); System.out.println(numwrote + " Bytes written"); } } } catch(Exception e) { e.printStackTrace(); } } }
关键修复点说明
- 每次读取前清空缓冲区:调用
channel.read(buffer)前执行buffer.clear(),确保缓冲区处于可写入状态,避免旧数据干扰。 - 处理
read返回值:- 当
numread == -1时,说明客户端已关闭连接,取消SelectionKey并关闭通道,避免无效读事件触发。 - 当
numread == 0时,说明当前无新数据可读,直接跳过后续处理,让selector重新进入阻塞状态。
- 当
- 正确构造字符串:使用
new String(buffer.array(), 0, numread)仅转换实际读取到的字节,避免缓冲区未填充部分的乱码。 - 提前移除
SelectionKey:处理key前调用itr.remove(),符合NIO规范,避免同一个key被重复处理。
内容的提问来源于stack exchange,提问作者Matthew
相关产品推荐
相关产品推荐

