java nio简易聊天室广播服务核心是selector多路复用:服务端非阻塞监听连接与读写,按\n解析消息,用concurrenthashmap管理在线客户端并广播(排除发送者),客户端以阻塞socketchannel收发换行分隔文本。

用 Java NIO 实现简易聊天室广播服务,核心是用 Selector 管理多个客户端连接,避免阻塞式 I/O 的线程爆炸问题;所有消息由服务端接收后,再主动写回给除发送者外的所有在线客户端——即“广播”。关键在于非阻塞读写、连接管理、消息分界与线程安全的共享状态。
1. 服务端:基于 Selector 的多路复用监听
创建 ServerSocketChannel,设为非阻塞,注册到 Selector 上监听 OP_ACCEPT。每当有新连接就绪,调用 accept() 获取 SocketChannel,同样设为非阻塞,并注册 OP_READ。
注意:每个客户端 channel 需要绑定一个缓冲区(如 ByteBuffer)用于读取,建议用 SelectionKey.attach() 存储该 channel 对应的缓冲区和用户标识(如简单昵称)。
- 用
configureBlocking(false)确保所有 channel 非阻塞 - 每次
select()后遍历就绪 key,区分isAcceptable()和isReadable() - 读取时检查
read()返回值:-1 表示对端关闭,需取消 key 并关闭 channel
2. 消息协议:用换行符做简单分界
NIO 不保证一次 read() 读完一条完整消息。为简化,约定客户端每发一条消息都以 \n 结尾(如 "hello\n"),服务端在 ByteBuffer 中按行解析。
做法:将数据累积到一个 ByteArrayOutputStream 或循环检查 buffer 中的 \n 位置;找到后截取前缀作为一条消息,剩余字节移至 buffer 开头继续等待下一条。
- 避免直接用
String.decode()处理未满的 buffer - 可封装一个
LineBasedFrameDecoder类,类似 Netty 思路但极简 - 收到完整消息后,立即触发广播逻辑,不等更多数据
3. 广播实现:遍历所有活跃 channel 写入
维护一个 ConcurrentHashMap<socketchannel string> clients</socketchannel>,key 是 channel,value 是昵称(可选)。每当收到一条有效消息:
- 解析出发送者 channel 和内容
- 遍历
clients.keySet(),跳过当前发送者 - 对每个目标 channel 调用
write(),传入格式化后的字节数组(如"[user]: msg\n".getBytes(UTF_8)) - 若
write()返回 0(缓冲区满),可先注册OP_WRITE,等下次就绪再续写(简易版可暂不处理背压,直接丢弃或记录告警)
注意:write() 是非阻塞的,可能只写出部分字节,需检查返回值并手动管理剩余数据——简易版可限制单条消息长度(如 ≤1KB),确保一次写完。
4. 客户端:简单 SocketChannel + Scanner 循环发消息
客户端只需一个阻塞式 SocketChannel 连接服务端,另开一个线程用 Scanner(System.in) 读用户输入,拼上 \n 后写入 channel。
同时主线程或另一线程持续 read() 服务端广播的消息,用 StandardCharsets.UTF_8 解码并打印。
- 输入线程捕获
IOException(如服务端断连)后退出 - 读线程检测到
read()返回 -1,说明服务端关闭,应退出 - 无需复杂编解码,纯文本 + 换行即可满足简易需求
不复杂但容易忽略:channel 关闭时必须调用 key.cancel() 和 channel.close(),否则资源泄漏;广播前检查 channel 是否仍 isOpen() && isRegistered(),防止已断连 channel 导致 IOException 中断整个广播流程。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











