| 1) 编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞) |
| 2) 实现多人群聊 |
| 3) 服务器端:可以监测用户上线,离线,并实现消息转发功能 |
| 4) 客户端:通过channel 可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到) |
| public class GroupChatServer { |
| |
| private Selector selector; |
| private ServerSocketChannel listenChannel; |
| private static final int PORT = 6667; |
| |
| |
| |
| public GroupChatServer() { |
| |
| try { |
| |
| |
| selector = Selector.open(); |
| |
| listenChannel = ServerSocketChannel.open(); |
| |
| listenChannel.socket().bind(new InetSocketAddress(PORT)); |
| |
| listenChannel.configureBlocking(false); |
| |
| listenChannel.register(selector, SelectionKey.OP_ACCEPT); |
| |
| }catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| } |
| |
| |
| public void listen() { |
| |
| System.out.println("监听线程: " + Thread.currentThread().getName()); |
| try { |
| |
| |
| while (true) { |
| |
| int count = selector.select(); |
| if(count > 0) { |
| |
| |
| Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); |
| while (iterator.hasNext()) { |
| |
| SelectionKey key = iterator.next(); |
| |
| |
| if(key.isAcceptable()) { |
| SocketChannel sc = listenChannel.accept(); |
| sc.configureBlocking(false); |
| |
| sc.register(selector, SelectionKey.OP_READ); |
| |
| |
| System.out.println(sc.getRemoteAddress() + " 上线 "); |
| |
| } |
| if(key.isReadable()) { |
| |
| |
| readData(key); |
| |
| } |
| |
| iterator.remove(); |
| } |
| |
| } else { |
| System.out.println("等待...."); |
| } |
| } |
| |
| }catch (Exception e) { |
| e.printStackTrace(); |
| |
| }finally { |
| |
| |
| } |
| } |
| |
| |
| private void readData(SelectionKey key) { |
| |
| |
| SocketChannel channel = null; |
| |
| try { |
| |
| channel = (SocketChannel) key.channel(); |
| |
| ByteBuffer buffer = ByteBuffer.allocate(1024); |
| |
| int count = channel.read(buffer); |
| |
| if(count > 0) { |
| |
| String msg = new String(buffer.array()); |
| |
| System.out.println("form 客户端: " + msg); |
| |
| |
| sendInfoToOtherClients(msg, channel); |
| } |
| |
| }catch (IOException e) { |
| try { |
| System.out.println(channel.getRemoteAddress() + " 离线了.."); |
| |
| key.cancel(); |
| |
| channel.close(); |
| }catch (IOException e2) { |
| e2.printStackTrace();; |
| } |
| } |
| } |
| |
| |
| private void sendInfoToOtherClients(String msg, SocketChannel self ) throws IOException{ |
| |
| System.out.println("服务器转发消息中..."); |
| System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName()); |
| |
| for(SelectionKey key: selector.keys()) { |
| |
| |
| Channel targetChannel = key.channel(); |
| |
| |
| if(targetChannel instanceof SocketChannel && targetChannel != self) { |
| |
| |
| SocketChannel dest = (SocketChannel)targetChannel; |
| |
| ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes()); |
| |
| dest.write(buffer); |
| } |
| } |
| |
| } |
| |
| public static void main(String[] args) { |
| |
| |
| GroupChatServer groupChatServer = new GroupChatServer(); |
| groupChatServer.listen(); |
| } |
| } |
| public class GroupChatClient { |
| |
| |
| private final String HOST = "127.0.0.1"; |
| private final int PORT = 6667; |
| private Selector selector; |
| private SocketChannel socketChannel; |
| private String username; |
| |
| |
| public GroupChatClient() throws IOException { |
| |
| selector = Selector.open(); |
| |
| socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT)); |
| |
| socketChannel.configureBlocking(false); |
| |
| socketChannel.register(selector, SelectionKey.OP_READ); |
| |
| username = socketChannel.getLocalAddress().toString().substring(1); |
| System.out.println(username + " is ok..."); |
| |
| } |
| |
| |
| public void sendInfo(String info) { |
| |
| info = username + " 说:" + info; |
| |
| try { |
| socketChannel.write(ByteBuffer.wrap(info.getBytes())); |
| }catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| |
| public void readInfo() { |
| |
| try { |
| |
| int readChannels = selector.select(); |
| if(readChannels > 0) { |
| |
| Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); |
| while (iterator.hasNext()) { |
| |
| SelectionKey key = iterator.next(); |
| if(key.isReadable()) { |
| |
| SocketChannel sc = (SocketChannel) key.channel(); |
| |
| ByteBuffer buffer = ByteBuffer.allocate(1024); |
| |
| sc.read(buffer); |
| |
| String msg = new String(buffer.array()); |
| System.out.println(msg.trim()); |
| } |
| } |
| iterator.remove(); |
| } else { |
| |
| |
| } |
| |
| }catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public static void main(String[] args) throws Exception { |
| |
| |
| GroupChatClient chatClient = new GroupChatClient(); |
| |
| |
| new Thread() { |
| public void run() { |
| |
| while (true) { |
| chatClient.readInfo(); |
| try { |
| Thread.currentThread().sleep(3000); |
| }catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| }.start(); |
| |
| |
| Scanner scanner = new Scanner(System.in); |
| |
| while (scanner.hasNextLine()) { |
| String s = scanner.nextLine(); |
| chatClient.sendInfo(s); |
| } |
| } |
| |
| } |
-
测试:先启动服务端,再启动2个客户端

-
在1个客户端中发送消息,其他客户端可以接收
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决