nio高并发echo服务器核心是selector事件驱动与buffer高效管理:初始化非阻塞serversocketchannel并注册op_accept;accept时创建非阻塞socketchannel并注册op_read;read时用flip/decode处理粘包,clear/compact管理buffer;write未完成时追加op_write并暂存buffer,就绪后继续写完再恢复op_read。

用NIO写高并发Echo聊天服务器,核心是避免阻塞、复用线程、高效处理连接与读写。不依赖Tomcat或Netty,纯JDK NIO(java.nio.channels)即可实现万级并发连接,关键在Selector事件驱动模型和Buffer的合理使用。
初始化非阻塞ServerSocketChannel与Selector
启动时创建ServerSocketChannel并设为非阻塞,注册OP_ACCEPT事件到Selector:
- 调用ServerSocketChannel.open()获取通道,configureBlocking(false)关闭阻塞
- 绑定端口:socket().bind(new InetSocketAddress(port))
- 注册到Selector:channel.register(selector, SelectionKey.OP_ACCEPT)
- 后续所有I/O操作都基于selector.select()轮询就绪事件,不再调用accept()/read()/write()阻塞等待
处理ACCEPT事件:接收新连接并注册读事件
当selector检测到ServerSocketChannel就绪,说明有客户端connect,此时需主动accept并为新连接配置读事件:
- 调用serverChannel.accept()获取SocketChannel,同样设为configureBlocking(false)
- 为该SocketChannel分配一个ByteBuffer(如4KB),用于后续读取数据;可存入SelectionKey的attachment中便于复用
- 注册OP_READ事件:clientChannel.register(selector, OP_READ, attachment)
- 注意:不要在accept后立即read——此时TCP缓冲区可能为空,必须等下一次select触发READ就绪
处理READ事件:解析消息并触发ECHO逻辑
当某SocketChannel就绪READ,说明有数据到达,需从Buffer中提取完整消息(注意粘包/半包):
- 调用channel.read(buffer),返回值>0表示读到字节,=0表示无数据(但已就绪,可能是空心跳),-1表示对端关闭
- 切换buffer为读模式:buffer.flip(),然后用StandardCharsets.UTF_8.decode(buffer)转成字符串
- 简单Echo场景可直接将原消息回写;若需按行或定长协议,需自行缓冲未完成的数据(例如把剩余字节复制到新buffer开头)
- 清空buffer:buffer.clear(),为下次read准备;若消息较大,建议使用compact()保留未读部分
处理WRITE事件:异步回写响应并管理写状态
NIO写操作可能未一次完成(如网络慢、缓冲区满),不能直接write后就认为发送成功:
- 向channel.write(buffer)后,检查返回值是否等于buffer.remaining();若小于,则说明未写完,需注册OP_WRITE等待再次就绪
- 推荐做法:首次write后,若未写完,将channel的SelectionKey改为只关注OP_WRITE,并把待写buffer存在attachment中
- 当OP_WRITE就绪,继续write剩余内容;写完后,取消OP_WRITE,重新注册OP_READ,恢复读取新消息
- 避免频繁注册/取消事件,可用key.interestOps(ops | SelectionKey.OP_WRITE)追加,用&运算移除
整个流程围绕一个线程(或少量线程)+ Selector展开,没有每个连接一个线程的开销。消息编解码、连接生命周期、心跳保活等可在此骨架上扩展。不复杂但容易忽略缓冲区管理和事件状态切换。










