课程名称: 解锁网络编程之NIO的前世今生
课程章节:第4章 【应用】NIO网络编程实战、第5章 【掌握】NIO网络编程缺陷
课程讲师: 张小喜
课程内容:
服务器端补充对其他客户端的广播
private void broadCast(Selector selector, SocketChannel sourceChannel, String request) { /** * 获取到所有已接入的客户端channel */ Set<SelectionKey> selectionKeySet = selector.keys(); /** * 循环向所有channel广播信息 */ selectionKeySet.forEach(selectionKey -> { Channel targetChannel = selectionKey.channel(); // 剔除发消息的客户端 if (targetChannel instanceof SocketChannel && targetChannel != sourceChannel) { try { // 将信息发送到targetChannel客户端 ((SocketChannel) targetChannel).write( Charset.forName("UTF-8").encode(request)); } catch (IOException e) { e.printStackTrace(); } } }); }
客户端的写法
public void start(String nickname) throws IOException { /** * 连接服务器端 */ SocketChannel socketChannel = SocketChannel.open( new InetSocketAddress("127.0.0.1", 8000)); /** * 接收服务器端响应 */ // 新开线程,专门负责来接收服务器端的响应数据 // selector , socketChannel , 注册 Selector selector = Selector.open(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); new Thread(new NioClientHandler(selector)).start(); /** * 向服务器端发送数据 */ Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String request = scanner.nextLine(); if (request != null && request.length() > 0) { socketChannel.write( Charset.forName("UTF-8") .encode(nickname + " : " + request)); } } }
补充循环接受服务器端消息的线程
** * 客户端线程类,专门接收服务器端响应信息 */ public class NioClientHandler implements Runnable { private Selector selector; public NioClientHandler(Selector selector) { this.selector = selector; } @Override public void run() { try { for (;;) { int readyChannels = selector.select(); if (readyChannels == 0) continue; /** * 获取可用channel的集合 */ Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator iterator = selectionKeys.iterator(); while (iterator.hasNext()) { /** * selectionKey实例 */ SelectionKey selectionKey = (SelectionKey) iterator.next(); /** * **移除Set中的当前selectionKey** */ iterator.remove(); /** * 7. 根据就绪状态,调用对应方法处理业务逻辑 */ /** * 如果是 可读事件 */ if (selectionKey.isReadable()) { readHandler(selectionKey, selector); } } } } catch (IOException e) { e.printStackTrace(); } } /** * 可读事件处理器 */ private void readHandler(SelectionKey selectionKey, Selector selector) throws IOException { /** * 要从 selectionKey 中获取到已经就绪的channel */ SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); /** * 创建buffer */ ByteBuffer byteBuffer = ByteBuffer.allocate(1024); /** * 循环读取服务器端响应信息 */ String response = ""; while (socketChannel.read(byteBuffer) > 0) { /** * 切换buffer为读模式 */ byteBuffer.flip(); /** * 读取buffer中的内容 */ response += Charset.forName("UTF-8").decode(byteBuffer); } /** * 将channel再次注册到selector上,监听他的可读事件 */ socketChannel.register(selector, SelectionKey.OP_READ); /** * 将服务器端响应信息打印到本地 */ if (response.length() > 0) { System.out.println(response); } } }
课程收获:
通过对实战课的学习,对NIO的基本调用实现已经有了基本的了解。后续将根据工作中的场景看下是否进行应用