定制跟模板网站有什么不一样,山东电力建设河北分公司网站,网站开发的实训报告,网页设计心得体会正文【README】
本文总结自B站《尚硅谷netty》#xff0c;很不错#xff1b; 【1】选择器Selector#xff08;多路复用器#xff09;
【1.1】基本介绍
1#xff09;Java 的 NIO#xff0c;用非阻塞的 IO 方式。可以用一个线程#xff0c;处理多个客户端连接#xff0c;就…【README】
本文总结自B站《尚硅谷netty》很不错 【1】选择器Selector多路复用器
【1.1】基本介绍
1Java 的 NIO用非阻塞的 IO 方式。可以用一个线程处理多个客户端连接就会使用到Selector(选择器)
2Selector 能够检测多个注册的通道上是否有事件发生(注意:多个Channel以事件的方式可以注册到同一个Selector)如果有事件发生便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管理多个通道也就是管理多个连接和请求
3只有在 连接/通道 真正有读写事件发生时才会进行读写就大大地减少了系统开销并且不必为每个连接都创建一个线程不用去维护多个线程
4避免了多线程之间的上下文切换导致的开销
5特点再说明:
5.1 Netty 的 IO 线程NioEventLoop 聚合了 Selector(选择器也叫多路复用器*)可以同时并发处理成百上千个客户端连接5.2当线程从某客户端Socket 通道进行读写数据时若没有数据可用时该线程可以进行其他任务即线程不会在没有数据可用的客户端连接上阻塞而是处理其他客户端请求5.3 线程通常将非阻塞IO 的空闲时间用于在其他通道上执行 IO 操作所以单独的线程可以管理多个输入和输出通道5.4由于读写操作都是非阻塞的这就可以充分提升IO线程的运行效率避免由于频繁I/O阻塞导致的线程挂起5.5一个 I/O 线程可以并发处理N个客户端连接和读写操作这从根本上解决了传统同步阻塞I/O一连接一线程模型架构的性能、弹性伸缩能力和可靠性都得到了极大的提升【1.2】Selector类相关方法
1Selector 类是一个抽象类, 常用方法和说明如下:
public abstract class Selector implements Closeable {public static Selector open();//得到一个选择器对象public int select(long timeout);// 返回IO操作准备就绪的通道的key个数// 监控所有注册的通道当其中有 IO 操作可以进行时将对应的 SelectionKey 加入到内部集合中并返回参数用来设置超时时间public SetSelectionKey selectedKeys();//从内部集合中得到所有的 SelectionKey
}
【补充】
补充1 SelectionKey 与 Channel 是映射关系通过SelectionKey 可以获取到 Channel 补充2 SelectionKey 是 Selector的一个重要对象【注意事项】 1 NIO中的 ServerSocketChannel功能类似ServerSocketSocketChannel功能类似Socket 2 selector 相关方法说明
selector.select()//阻塞
selector.select(1000);//阻塞1000毫秒在1000毫秒后返回
selector.wakeup();// 唤醒selector
selector.selectNow();// 不阻塞立马返还 【1.3】 SelectionKey在NIO 体系
1NIO 非阻塞网络编程原理分析图 NIO 非阻塞网络编程相关的(Selector、SelectionKey、ServerScoketChannel和SocketChannel) 关系梳理图 【图解】 服务器采用NIO非阻塞处理客户端请求步骤
Step1当客户端连接时会通过ServerSocketChannel 生成 SocketChannelStep2 把 SocketChannel注册到Selector上register(Selector sel, int ops)1个selector上可以注册多个SocketChannel Step3注册后返回一个SelectionKey会和该Selector 关联(集合)Step4 Selector 监听通道Channel的select方法返回有事件发生的通道个数.Step5进一步得到各个SelectionKey (有事件发生)Step6再通过SelectionKey反向获取SocketChannel , 通过方法channel() 得到Step7可以通过得到的channel , 完成业务处理【2】基于NIO实现服务器端和客户端之间的数据简单通讯
1服务器代码
/*** Description 编写一个 NIO 入门案例* 实现服务器端和客户端之间的数据简单通讯非阻塞* author xiao tang* version 1.0.0* createTime 2022年08月17日*/
public class NIOServer024 {public static void main(String[] args) throws IOException {// 创建 ServerSocketChannel - ServerSocketServerSocketChannel serverSocketChannel ServerSocketChannel.open();// 得到选择器 selectorSelector selector Selector.open();// 绑定端口 6666 在服务器端监听serverSocketChannel.socket().bind(new InetSocketAddress(6666));// 设置为非阻塞serverSocketChannel.configureBlocking(false);// 把 serverSocketChannel 注册到 Selector关心事件为 OP_ACCEPTserverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 循环等待客户端连接while (true) {if (selector.select(3000) 0) { // 没有事件发生System.out.println(服务器等待1秒无客户端连接);continue;}// 如果返回值大于0, 表示监听到有事件发生// 1.获取相关的 SelectionKey 集合// 2. selector.selectedKeys 获取关注事件的集合// 3. 通过 selectionKeys 反向获取通道SetSelectionKey selectionKeys selector.selectedKeys();// 4. 遍历 selectionKeys 使用迭代器遍历IteratorSelectionKey it selectionKeys.iterator();while (it.hasNext()) {// 获取到 SelectionKeySelectionKey key it.next();// 根据key 对应的通道发生的事件做不同处理if (key.isAcceptable()) { // 如果是 OP_ACCEPT表示有新的客户端连接服务器则需要产生新的 channel// 给客户端生成一个 SocketChannelSocketChannel socketChannel serverSocketChannel.accept();// 需要把 SocketChannel 设置为 非阻塞socketChannel.configureBlocking(false);System.out.println(客户端连接成功生成一个 socketChannel hashcode socketChannel.hashCode());// 把 SocketChannel 注册到 Selector 上监听 OP_READ 事件socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));}if (key.isReadable()) {// 只读// 通过key反向获取 ChannelSocketChannel channel (SocketChannel) key.channel();// 获取到该 Channel 关联的 bufferByteBuffer buffer (ByteBuffer) key.attachment();// 把channel里的读入到 ByteBufferchannel.read(buffer);System.out.println(客户端 new String(buffer.array(), StandardCharsets.UTF_8));}// 手动从集合中移除 selectionKey防止重复操作it.remove();}}}
}
2客户端
/*** Description NIO 客户端* author xiao tang* version 1.0.0* createTime 2022年08月17日*/
public class NIOClient025 {public static void main(String[] args) throws IOException {// 得到一个通道SocketChannel socketChannel SocketChannel.open();// 设置非阻塞socketChannel.configureBlocking(false);// 提供服务器端的ip 和 端口InetSocketAddress inetSocketAddress new InetSocketAddress(127.0.0.1, 6666);// 连接服务器if (!socketChannel.connect(inetSocketAddress)) {while(!socketChannel.finishConnect()) {System.out.println(连接需要时间客户端不会阻塞可以做其他工作);}}// 若连接到服务器成功则发送数据到服务器String text hello 成都;ByteBuffer byteBuffer ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8));// 发送数据把buffer中的数据写入到 channelsocketChannel.write(byteBuffer);System.out.println(任意键结束);System.in.read();}
}
【演示效果】