• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

NIO入门

武飞扬头像
kbp1235
帮助3

NIO:客户端发送的连接请求都会注册到多路复用器selector上,多路复用 器轮询到连接有IO请求就进行处理

一、nio非阻塞代码

  1.  
    public static void main(String[] args) throws IOException {
  2.  
    //1、打开ServerSocketChannel
  3.  
    ServerSocketChannel ssc = ServerSocketChannel.open();
  4.  
     
  5.  
    ssc.configureBlocking(false);
  6.  
    //2、绑定监听地址InetSocketAddress
  7.  
    ssc.socket().bind(new InetSocketAddress(9000));
  8.  
    //3、创建Selector,并启动线程
  9.  
    Selector selector = Selector.open();
  10.  
    //4、把ServerSocketChannel注册到Selector,监听
  11.  
    ssc.register(selector, SelectionKey.OP_ACCEPT);
  12.  
     
  13.  
    while (true) {
  14.  
    int select = selector.select();
  15.  
    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
  16.  
    //5 Selector轮询指定的key
  17.  
    while (it.hasNext()) {
  18.  
    SelectionKey key = it.next();
  19.  
    it.remove();
  20.  
    handle(key);
  21.  
    }
  22.  
    }
  23.  
    }
  24.  
     
  25.  
    private static void handle(SelectionKey key) throws IOException {
  26.  
    if (key.isAcceptable()) {
  27.  
    System.out.println("有客户端连接事件发生了。。");
  28.  
    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
  29.  
    //6、处理新的客户端接入
  30.  
    SocketChannel sc = ssc.accept();
  31.  
    //7、设置新的socket参数
  32.  
    sc.configureBlocking(false);
  33.  
    //8、将新接入的客户端连接注册到Reactor线程的多路复用器上,读取客户端发送的网络消息
  34.  
    sc.register(key.selector(), SelectionKey.OP_READ);
  35.  
    } else if (key.isReadable()) {
  36.  
    System.out.println("有客户端数据可读事件发生了。。");
  37.  
    SocketChannel sc = (SocketChannel) key.channel();
  38.  
    ByteBuffer buffer = ByteBuffer.allocate(1024);
  39.  
    //9、异步读取客户端请求消息到缓冲区
  40.  
    int len = sc.read(buffer);
  41.  
    if (len != -1) {
  42.  
    System.out.println("读取到客户端发送的数据:" new String(buffer.array(), 0, len));
  43.  
    }
  44.  
    ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes());
  45.  
    //10 将消息异步发送给客户端
  46.  
    sc.write(bufferToWrite);
  47.  
    key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
  48.  
    } else if (key.isWritable()) {
  49.  
    SocketChannel sc = (SocketChannel) key.channel();
  50.  
    System.out.println("write事件");
  51.  
    key.interestOps(SelectionKey.OP_READ);
  52.  
    }
  53.  
    }
学新通

打开cmd窗口,输入以下命令,实现客户端和服务端交互:

  1.  
    telnet localhost 9000
  2.  
    ctrl ]
  3.  
    send 123
学新通

二、nio服务端代码时序图

上面服务端代码启动时序图:

学新通

三、核心代码底层调用

  1.  
    1 Selector.open() //创建多路复用器 (底层调用的epoll_create)
  2.  
    2 socketChannel.register() //将channel注册到多路复用器上 (底层调用的 epoll_ctl)
  3.  
    3 selector.select() //阻塞等待需要处理的事件发生 (底层调用的epoll_wait)

几个核心函数

int epoll_create(int size);

1、创建一个epoll实例,并返回一个非负数作为文件描述符,size代表表可能会容纳size个描述符

 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

2、使用文件描述符epfd引用的epoll实例,对目标文件描述符fd执行op操作。

epfd: epoll对应的文件描述符 fd: socket对应的文件描述符 参数op有以下几个值: EPOLL_CTL_ADD:注册新的fd到epfd中,并关联事件event; EPOLL_CTL_MOD:修改已经注册的fd的监听事件; EPOLL_CTL_DEL:从epfd中移除fd,并且忽略掉绑定的event,这时event可以为null; events有很多可选值,这里只举例最常见的几个: EPOLLIN :表示对应的文件描述符是可读的; EPOLLOUT:表示对应的文件描述符是可写的; EPOLLERR:表示对应的文件描述符发生了错误;

1 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

3、等待文件描述符epfd上的事件。

epfd: Epoll对应的文件描述符,

events: 调用者所有可用事件的集合,

maxevents: 最多等到多少个事件就返回, timeout: 超时时间。

总结:nio就是通过操作系统内核函数创建socket,获取文件描述符,再创建一个Selector ,获取Epoll文件描述符,将socket文件描述符绑定到Selector的文件描述符上,进行事件的异步通知;就实现了使用一条线程,并且不需要太多的无效的遍历 ,将事件处理交给操作系统内核,事件通知无需遍历,大大提高了效率。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhfhib
系列文章
更多 icon
同类精品
更多 icon
继续加载