Netty三大核心组件之Selector

矫情吗;* 2023-06-18 09:58 83阅读 0赞

之前进行socket编程时,accept方法会一直阻塞,直到有客户端请求的到来,并返回socket进行相应的处理。整个过程是流水线的,处理完一个请求,才能去获取并处理后面的请求,当然也可以把获取socket和处理socket的过程分开,一个线程负责accept,一个线程池负责处理请求。

但NIO提供了更好的解决方案,采用选择器(Selector)返回已经准备好的socket,并按顺序处理,基于通道(Channel)和缓冲区(Buffer)来进行数据的传输。

Selector

这里出来一个新概念,selector,具体是一个什么样的东西?

想想一个场景:在一个养鸡场,有这么一个人,每天的工作就是不停检查几个特殊的鸡笼,如果有鸡进来,有鸡出去,有鸡生蛋,有鸡生病等等,就把相应的情况记录下来,如果鸡场的负责人想知道情况,只需要询问那个人即可。

在这里,这个人就相当Selector,每个鸡笼相当于一个SocketChannel,每个线程通过一个Selector可以管理多个SocketChannel。

format_png

为了实现Selector管理多个SocketChannel,必须将具体的SocketChannel对象注册到Selector,并声明需要监听的事件(这样Selector才知道需要记录什么数据),一共有4种事件:

1、connect:客户端连接服务端事件,对应值为SelectionKey.OPCONNECT(8)

2、accept:服务端接收客户端连接事件,对应值为SelectionKey.OPACCEPT(16)

3、read:读事件,对应值为SelectionKey.OPREAD(1)

4、write:写事件,对应值为SelectionKey.OPWRITE(4)

这个很好理解,每次请求到达服务器,都是从connect开始,connect成功后,服务端开始准备accept,准备就绪,开始读数据,并处理,最后写回数据返回。

所以,当SocketChannel有对应的事件发生时,Selector都可以观察到,并进行相应的处理。

ServerSocketChannel注册

  1. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

ServerSocketChannel的Operation Set只能是OP_ACCEPT,如果在注册的时候添加了OP_CONNECT、OP_WRITE或OP_READ会报异常。例如按照以下写法

  1. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT);

就会抛出下面的异常

  1. Exception in thread "main" java.lang.IllegalArgumentException
  2. at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:199)
  3. at java.nio.channels.SelectableChannel.register(SelectableChannel.java:280)
  4. at com.nio.sample.selector.SelectorServerSocketChannelSample.main(SelectorServerSocketChannelSample.java:27)

ServerSocketChannel的validOps可以看到只有OP_ACCEPT是合法的

  1. public final int validOps() {
  2. return SelectionKey.OP_ACCEPT;
  3. }

SocketChannel注册

  1. socketChannel.register(selector, SelectionKey.OP_CONNECT);

SocketChannel的Operation Set只能是OP_CONNECT、OP_WRITE和OP_READ,如果在注册的时候添加了OP_ACCEPT同样会报异常。

SocketChannel的validOps可以看到只有OP_READ、OP_WRITE、OP_CONNECT是合法的

  1. public final int validOps() {
  2. return (SelectionKey.OP_READ
  3. | SelectionKey.OP_WRITE
  4. | SelectionKey.OP_CONNECT);
  5. }

注册成功之后,我们通过一个demo实现,客户端和服务端交互:

服务端:

  1. public class Server {
  2. public static void main(String[] args) {
  3. try (
  4. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  5. Selector selector = Selector.open();
  6. ) {
  7. serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 8080));
  8. serverSocketChannel.configureBlocking(false);
  9. System.out.println("server 启动...");
  10. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  11. while (selector.select() > 0) {
  12. Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
  13. while(keyIterator.hasNext()) {
  14. SelectionKey key = keyIterator.next();
  15. if (key.isReadable()) {
  16. ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
  17. SocketChannel socketChannel = (SocketChannel) key.channel();
  18. int readBytes = socketChannel.read(buffer);
  19. if (readBytes > 0) {
  20. buffer.flip();
  21. byte[] bytes = new byte[buffer.remaining()];
  22. buffer.get(bytes);
  23. String body = new String(bytes, StandardCharsets.UTF_8);
  24. System.out.println("server 收到:" + body);
  25. }
  26. } else if(key.isAcceptable()) {
  27. SocketChannel socketChannel = serverSocketChannel.accept();
  28. socketChannel.configureBlocking(false);
  29. socketChannel.register(selector, SelectionKey.OP_READ);
  30. }
  31. keyIterator.remove();
  32. }
  33. }
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }

客户端:

  1. public class Client {
  2. public static void main(String[] args) {
  3. try (
  4. SocketChannel socketChannel = SocketChannel.open();
  5. ) {
  6. socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
  7. System.out.println("client 启动...");
  8. ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
  9. buffer.put("hi, 这是client".getBytes(StandardCharsets.UTF_8));
  10. buffer.flip();
  11. socketChannel.write(buffer);
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }

发表评论

表情:
评论列表 (有 0 条评论,83人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Netty 核心组件

    Netty 核心组件 1. 前言 本节我们主要从整体上了解 Netty 有哪些核心组件,很多同学学习完 Netty 虽然会使用,但是只知道如何自定义 Handler

    相关 Netty核心组件

    Netty的核心组件   Channel   回调   Future   事件   ChannelHandler   这些模块代表了不同的类型:资源(事件)、逻