欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

【Netty】NIO 选择器 ( Selector ) 通道 ( Channel ) 缓冲区 ( Buffer ) 网络通信案例

发布时间:2025/6/17 编程问答 51 豆豆
生活随笔 收集整理的这篇文章主要介绍了 【Netty】NIO 选择器 ( Selector ) 通道 ( Channel ) 缓冲区 ( Buffer ) 网络通信案例 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

        • I . NIO 通信 服务器端 流程说明
        • II . NIO 通信 服务器端代码
        • III . NIO 通信 客户端 流程说明
        • IV . NIO 通信 客户端代码
        • V . NIO 通信 示例运行





I . NIO 通信 服务器端 流程说明



NIO 网络通信 服务器端 操作流程 , 与 BIO 原理类似 , 基本流程是 启动服务器套接字通道 , 创建选择器 , 将服务器套接字通道注册给选择器 , 监听客户端连接事件 , 客户端连接成功后 , 创建套接字通道 , 将新创建的通道注册给选择器 , 然后监听该通道的读取事件 ;


启动 -> 创建选择器 ->
创建服务器通道 -> 注册服务器通道 -> 监听连接 ->
创建客户端通道 -> 注册客户端通道 -> 监听数据读取/客户端连接



1 . 创建 服务器套接字通道 ( ServerSocketChannel ) :


① 创建通道 : 调用 ServerSocketChannel.open() 创建 , 创建后需要绑定本地端口号 , 需要获取 ServerSocket 用于绑定端口号 ;

② 获取服务器套接字 : 可以通过服务器套接字通道的 serverSocketChannel.socket() 方法获取 ServerSocket ;

③ 绑定本地端口号 : 调用 serverSocket.bind(new InetSocketAddress(8888)) 方法为该 ServerSocket 绑定 8888 端口号 ;

④ 设置非阻塞网络通信模式 : 并设置该通道网络通信模式为非阻塞模式 serverSocketChannel.configureBlocking(false) , 注意这里设置了非阻塞模式 , 其 对应的客户端套接字通道 SocketChannel 也要设置非阻塞模式 , 否则会报 IllegalBlockingModeException 异常 ;



2 . 创建选择器并注册通道 :


① 创建 选择器 ( Selector ) : 调用 Selector 的静态方法 open() , 即可创建一个 选择器 , Selector.open() ;

② 将 服务器套接字通道 注册给 选择器 ( Selector ) : serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT) , 监听通道的 SelectionKey.OP_ACCEPT 事件 , 如果有客户端连接服务器 , 就会触发该事件 , 生成相应的 SelectionKey , 并放入 选择器 ( Selector ) 的集合中 , 如果 选择器 ( Selector ) 正在调用 select 方法 ( 333 种 阻塞 / 非阻塞 监听方法 ) 监听 , 那么就会解除阻塞 ( 如果之前发生监听阻塞 ) , 返回触发事件的个数 ;



3 . 选择器 ( Selector ) 阻塞监听 : 这里调用 selector.select() 方法 , 阻塞监听 , 如果有事件发生 , 就会返回触发事件的个数 , 之后再遍历 SelectionKey 集合 , 依次处理触发的事件 ; 阻塞监听代码逻辑如下 :

while (true){if(selector.select() <= 0){continue;}//监听到触发事件, 处理对应的 SelectionKey 事件 }

4 . 处理客户端连接事件 :


① 判定 SelectionKey 的事件是否是连接事件 : 调用 key.isAcceptable() 方法 , 如果返回 true , 说明该事件是客户端连接事件 ;

② 创建 套接字通道 : 为该客户端创建一个对应的 SocketChannel 通道 , 调用 serverSocketChannel.accept() 方法 , 可以创建该客户端对应的 SocketChannel 通道 , 该方法是非阻塞的 , 因为该事件触发时已经知道有客户端连接 , 这里只是响应客户端的连接 ;

③ 设置非阻塞网络通信模式 : : sc.configureBlocking(false) 设置该通道是非阻塞通道 , 否则会报 IllegalBlockingModeException 异常 ;

④ 将通道注册给选择器 : 注册通道给选择器 , 并监听数据读取事件 , 同时设置通道对应的缓冲区 , 通道与客户端之间使用缓冲区进行交互 ; sc.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024)) ;



5 . 处理数据读取事件 :


① 判定 SelectionKey 的事件是否是 读取 事件 : 调用 key.isReadable() 方法 , 如果返回 true , 说明该事件是 数据读取 事件 ;

② 获取通道 : 调用 SelectionKey 的 (SocketChannel) key.channel() 方法 , 获取该 SelectionKey 对应的通道 ;

③ 获取缓冲区 : 调用 (ByteBuffer) key.attachment() 获取对应的注册给 选择器 的缓冲区 ;

④ 读取缓冲区的数据 : 通道 socketChannel.read(byteBuffer) 方法 , 可以将数据读取数据到该缓冲区中 , 之后可以从缓冲区中获取数据 ;





II . NIO 通信 服务器端代码



服务器端代码 :

package kim.hsl.nio.demo;import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set;public class Server {public static void main(String[] args) {try {//I . 创建 ServerSocketChannel 监听 8888 端口//创建 ServerSocketChannel, 等价于 BIO 中的 ServerSocketServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//绑定本地端口, 获取其内部封装的 ServerSocket, 绑定 ServerSocket 的 8888 端口ServerSocket serverSocket = serverSocketChannel.socket();serverSocket.bind(new InetSocketAddress(8888));//设置网络通信非阻塞模式serverSocketChannel.configureBlocking(false);//II . 创建选择器, 并注册监听事件//获取 选择器 ( Selector )Selector selector = Selector.open();//将 serverSocketChannel 通道注册给 选择器 ( Selector ), 这里注册连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//III . 选择器监听//选择器 ( Selector ) 开始监听while (true){//III . 1 . 判定事件触发 ://阻塞监听, 查看是否有事件触发, 如果有就在下面处理//如果没有 continue 终止循环, 继续下一次循环System.out.println("服务器端开始阻塞监听 8888 端口事件");if(selector.select() <= 0){continue;}//当前状态说明 ://如果能执行到该位置, 说明 selector.select(1000) 方法返回值大于 0//当前有 1 个或多个事件触发, 下面就是处理事件的逻辑//III . 2 . 处理事件集合 ://获取当前发生的事件的 SelectionKey 集合, 通过 SelectionKey 可以获取对应的 通道Set<SelectionKey> keys = selector.selectedKeys();//使用迭代器迭代, 涉及到删除操作Iterator<SelectionKey> keyIterator = keys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();//根据 SelectionKey 的事件类型, 处理对应通道的业务逻辑//III . 2 . ( 1 ) 客户端连接服务器, 服务器端需要执行 accept 操作if (key.isAcceptable()) {System.out.println("服务器端 选择器 ( Selector ) 监听到客户端连接事件");//创建通道 : 为该客户端创建一个对应的 SocketChannel 通道//不等待 : 当前已经知道有客户端连接服务器, 因此不需要阻塞等待//非阻塞方法 : ServerSocketChannel 的 accept() 是非阻塞的方法SocketChannel sc = serverSocketChannel.accept();//如果 ServerSocketChannel 是非阻塞的, 这里的 SocketChannel 也要设置成非阻塞的//否则会报 java.nio.channels.IllegalBlockingModeException 异常sc.configureBlocking(false);//注册通道 : 将 SocketChannel 通道注册给 选择器 ( Selector )//关注事件 : 关注事件时读取事件, 服务器端从该通道读取数据//关联缓冲区 :sc.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));}//III . 2 . ( 2 ) 客户端写出数据到服务器端, 服务器端需要读取数据if(key.isReadable()){System.out.println("服务器端 选择器 ( Selector ) 监听到客户发送数据事件");//获取 通道 ( Channel ) : 通过 SelectionKey 获取SocketChannel socketChannel = (SocketChannel) key.channel();//获取 缓冲区 ( Buffer ) : 获取到 通道 ( Channel ) 关联的 缓冲区 ( Buffer )ByteBuffer byteBuffer = (ByteBuffer) key.attachment();//读取客户端传输的数据 ;socketChannel.read(byteBuffer);System.out.println("客户端向服务器端发送数据 : " + new String(byteBuffer.array()));}//处理完毕后, 从 Set 集合中移除该 SelectionKeykeyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}} }



III . NIO 通信 客户端 流程说明



NIO 网络通信 客户端 操作流程 : 首先创建客户端套接字通道 , 设置该通道为非阻塞通信模式 , 连接服务器的指定端口号 , 连接成功后 , 写出数据到服务器中 ;


创建套接字通道 -> 连接服务器 -> 写出数据到服务器


1 . 创建套接字通道 : 调用 SocketChannel.open() 方法 , 即可获取套接字通道 ( SocketChannel ) , 之后将该通道设置为 非阻塞通信模式 socketChannel.configureBlocking(false) ;


2 . 连接服务器 : 首先设置服务器的地址和端口号 new InetSocketAddress(“127.0.0.1”, 8888) , 然后调用 socketChannel 的 connect(address) 方法 , 即可连接服务器 , 该操作是非阻塞的操作 , 此时需要确保连接成功以后 , 再向服务器发送数据 ;


3 . 写出数据到服务器 : 先创建 缓冲区 Buffer , 将数据放入缓冲区 , ByteBuffer.wrap(“Hello World”.getBytes()) , 然后调用 套接字通道 ( socketChannel ) 的 write(buffer) 方法 , 将数据写出到服务器中 ;





IV . NIO 通信 客户端代码



NIO 通信 客户端代码 :

package kim.hsl.nio.demo;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel;public class Client {public static void main(String[] args) {try {//1 . 客户端 SocketChannel : 先获取 SocketChannel, 相当于 BIO 中的 Socket, 设置非阻塞模式SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);//服务器地址 : 服务器的 IP 地址 和 端口号InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);//2 . 连接服务器 : 连接成功, 返回 true; 连接失败, 返回 false;boolean isConnect = socketChannel.connect(address);//没有连接成功if (!isConnect) {while (!socketChannel.finishConnect()){System.out.println("等待连接成功");}}//当前时刻状态分析 : 执行到该位置, 此时肯定是连接成功了System.out.println("服务器连接成功");//3 . 发送数据 : 如果连接成功 , 发送数据到服务器端ByteBuffer buffer = ByteBuffer.wrap("Hello World".getBytes());System.out.println("客户端向服务器端发送数据 \"Hello World\"");socketChannel.write(buffer);//目的是为了阻塞客户端, 不能让客户端退出System.in.read();} catch (IOException e) {e.printStackTrace();}} }



V . NIO 通信 示例运行



按照以下顺序操作


1 . 运行服务器端 : 服务器端运行后 , 选择器阻塞监听客户端的请求 , 主要是监听 客户端连接 和 数据读取 ( 服务器读取客户端发送的数据 ) 事件 ;


2 . 运行客户端 : 客户端运行后 , 连接服务器 , 然后向服务器写出 “Hello World” 字符串数据 ;


3 . 服务器端结果 : 服务器端监听到客户端连接 , 为客户端创建对应的通道 , 然后注册监听该通道的数据读取事件 , 之后继续监听客户端是否有数据写入 ;

总结

以上是生活随笔为你收集整理的【Netty】NIO 选择器 ( Selector ) 通道 ( Channel ) 缓冲区 ( Buffer ) 网络通信案例的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。