个人随笔
目录
实战:Selector选择器(NIO)
2020-01-10 23:31:12

在上一篇博文实战:ServerSocketChannel(模拟多路IO复用)我们用循环和列表实现了多路IO复用,现在我们用官方提供的Selector来实现同样的功能。

一 Selector(选择器)介绍

Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。

使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。

二 Selector(选择器)的使用方法介绍

1. Selector的创建

通过调用Selector.open()方法创建一个Selector对象,如下:

  1. Selector selector = Selector.open();

这里需要说明一下

2. 注册Channel到Selector

  1. channel.configureBlocking(false);
  2. SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

Channel必须是非阻塞的。所以FileChannel不适用Selector,因为FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承SelectableChannel。Socket channel可以正常使用。

SelectableChannel抽象类 有一个 configureBlocking() 方法用于使通道处于阻塞模式或非阻塞模式。

  1. abstract SelectableChannel configureBlocking(boolean block)

SelectableChannel抽象类的configureBlocking() 方法是由 AbstractSelectableChannel抽象类实现的,SocketChannel、ServerSocketChannel、DatagramChannel都是直接继承了 AbstractSelectableChannel抽象类

register() 方法的第二个参数。这是一个“ interest集合 ”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:

  • Connect
  • Accept
  • Read
  • Write

通道触发了一个事件意思是该事件已经就绪。比如某个Channel成功连接到另一个服务器称为“ 连接就绪 ”。一个Server Socket Channel准备好接收新进入的连接称为“ 接收就绪 ”。一个有数据可读的通道可以说是“ 读就绪 ”。等待写数据的通道可以说是“ 写就绪 ”。

这四种事件用SelectionKey的四个常量来表示:

  1. SelectionKey.OP_CONNECT
  2. SelectionKey.OP_ACCEPT
  3. SelectionKey.OP_READ
  4. SelectionKey.OP_WRITE

如果你对不止一种事件感兴趣,使用或运算符即可,如下:

  1. int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

select()方法介绍

在刚初始化的Selector对象中,这三个集合都是空的。 通过Selector的select()方法可以选择已经准备就绪的通道 (这些通道包含你感兴趣的的事件)。比如你对读就绪的通道感兴趣,那么select()方法就会返回读事件已经就绪的那些通道。

三、实战

这里模拟实战:ServerSocketChannel(模拟多路IO复用)这一篇博文里写一个功能一样的例子。

其实总体长得很相似

服务器端

  1. public class Server2 {
  2. private static ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  3. public static void main(String[] args) {
  4. try {
  5. //1、创建ServerSocketChannel
  6. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  7. //2、绑定我们的端口号
  8. serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 80));
  9. //3、设置连接模式为非阻塞式
  10. serverSocketChannel.configureBlocking(false);
  11. Selector selector = Selector.open();
  12. // 注册 channel,并且指定感兴趣的事件是 Accept
  13. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  14. while(true) {
  15. //4、非阻塞式不会阻塞
  16. int readyNum = selector.select();
  17. if (readyNum == 0) {
  18. continue;
  19. }
  20. //这里就表示有准备好的消息
  21. Set<SelectionKey> selectedKeys = selector.selectedKeys();
  22. Iterator<SelectionKey> it = selectedKeys.iterator();
  23. while(it.hasNext()) {
  24. SelectionKey key = it.next();
  25. if(key.isAcceptable()) {
  26. System.out.println("有新连接");
  27. // 接受连接
  28. // 创建新的连接,并且把连接注册到selector上,而且,
  29. // 声明这个channel只对读操作感兴趣。
  30. SocketChannel socketChannel = serverSocketChannel.accept();
  31. socketChannel.configureBlocking(false);
  32. socketChannel.register(selector, SelectionKey.OP_READ);
  33. } else if (key.isReadable()) {
  34. //// 通道可读
  35. SocketChannel socketChannel = (SocketChannel) key.channel();
  36. //清除缓存中的信息
  37. byteBuffer.clear();
  38. int read = socketChannel.read(byteBuffer);
  39. if(read>0) {
  40. //Buffer切换为读模式
  41. byteBuffer.flip();
  42. //设定编码
  43. Charset charset = Charset.forName("UTF-8");
  44. String receiveText = charset.newDecoder().decode(byteBuffer.asReadOnlyBuffer()).toString();
  45. System.out.println("receiveText:"+receiveText);
  46. }
  47. }else if (key.isWritable()) {
  48. // 通道可写
  49. }
  50. //写完后就移除
  51. it.remove();
  52. }
  53. }
  54. } catch (IOException e) {
  55. // TODO Auto-generated catch block
  56. e.printStackTrace();
  57. }
  58. }
  59. }

客户端

  1. public class Client {
  2. public static void main(String[] args) throws IOException {
  3. Socket socket = new Socket();
  4. socket.connect(new InetSocketAddress("127.0.0.1", 80));
  5. while(true) {
  6. Scanner scanner = new Scanner(System.in);
  7. socket.getOutputStream().write(scanner.next().getBytes());
  8. }
  9. }
  10. }

结束:具体原理以后再研究啦!

 502

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2