zoukankan      html  css  js  c++  java
  • java nio详解

    一.

    分布式rpc框架有很多,比如dubbo,netty,还有很多其他的产品。但他们大部分都是基于nio的,

    nio是非阻塞的io,那么它的内部机制是怎么实现的呢。

    1.由一个专门的线程处理所有IO事件,并负责分发。

    2.事件驱动机制,事件到来的时候触发操作,不需要阻塞的监视事件。

    3.线程之前通过wait,notify通信,减少线程切换。

    上图是nio的通信模型。

    其中:

    服务端和客户端各自维护一个管理通道的对象,我们称之为selector,它可以监控一个或多个通道上的事件。

    采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。

    二.

    nio server端简单实现:

    package com.nio;

    import java.io.IOException;
    import java.net.InetSocketAddress;
    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;

    /**
    * Created by viruser on 2017/6/8.
    */
    public class NioServer {
    // 通道管理器
    private Selector selector;

    /**
    * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
    * @param port 绑定的端口号
    * @throws IOException
    */
    public void initServer(int port) throws IOException {
    // 获得一个ServerSocketChannel通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    // 设置通道为非阻塞
    serverSocketChannel.configureBlocking(false);
    // 将该通道对应的ServerSocket绑定到port端口
    serverSocketChannel.bind(new InetSocketAddress(port));
    // 获得一个通道管理器
    this.selector = Selector.open();
    // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
    // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
    * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
    * @throws IOException
    */
    public void listen() throws IOException {
    System.out.println("服务端启动成功!");
    // 轮询访问selector
    while (true) {
    // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
    selector.select();
    // 获得selector中选中的项的迭代器,选中的项为注册的事件
    Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
    while (ite.hasNext()) {
    SelectionKey key = (SelectionKey) ite.next();
    // 删除已选的key,以防重复处理
    ite.remove();

    if (key.isAcceptable()) {// 客户端请求连接事件
    ServerSocketChannel server = (ServerSocketChannel) key.channel();
    // 获得和客户端连接的通道
    SocketChannel channel = server.accept();
    // 设置成非阻塞
    channel.configureBlocking(false);

    // 在这里可以给客户端发送信息哦
    channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息")
    .getBytes("utf-8")));
    // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
    channel.register(this.selector, SelectionKey.OP_READ);

    } else if (key.isReadable()) {// 获得了可读的事件
    read(key);
    }

    }

    }
    }

    /**
    * 处理读取客户端发来的信息 的事件
    *
    * @param key
    * @throws IOException
    */
    public void read(SelectionKey key) throws IOException {
    // 服务器可读取消息:得到事件发生的Socket通道
    SocketChannel channel = (SocketChannel) key.channel();
    // 创建读取的缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(512);
    channel.read(buffer);
    byte[] data = buffer.array();
    String msg = new String(data).trim();
    System.out.println("服务端收到信息:" + msg);
    ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));
    channel.write(outBuffer);// 将消息回送给客户端
    }

    /**
    * 启动服务端测试
    *
    * @throws IOException
    */
    public static void main(String[] args) throws IOException {
    NioServer server = new NioServer();
    server.initServer(8000);
    server.listen();
    }

    }

    客户端:
    package com.nio;

    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;

    /**
    * Created by viruser on 2017/6/8.
    */
    public class NioClient {
    //通道管理器
    private Selector selector;

    /**
    * 获得一个Socket通道,并对该通道做一些初始化的工作
    * @param ip 连接的服务器的ip
    * @param port 连接的服务器的端口号
    * @throws IOException
    */
    public void initClient(String ip,int port) throws IOException {
    // 获得一个Socket通道
    SocketChannel channel = SocketChannel.open();
    // 设置通道为非阻塞
    channel.configureBlocking(false);
    // 获得一个通道管理器
    this.selector = Selector.open();

    // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
    //用channel.finishConnect();才能完成连接
    channel.connect(new InetSocketAddress(ip,port));
    //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
    channel.register(selector, SelectionKey.OP_CONNECT);
    }

    /**
    * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
    * @throws IOException
    */
    public void connect() throws IOException {
    // 轮询访问selector
    while (true) {
    // 选择一组可以进行I/O操作的事件,放在selector中,客户端的该方法不会阻塞,
    //这里和服务端的方法不一样,查看api注释可以知道,当至少一个通道被选中时,
    //selector的wakeup方法被调用,方法返回,而对于客户端来说,通道一直是被选中的
    selector.select();
    // 获得selector中选中的项的迭代器
    Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
    while (ite.hasNext()) {
    SelectionKey key = (SelectionKey) ite.next();
    // 删除已选的key,以防重复处理
    ite.remove();
    // 连接事件发生
    if (key.isConnectable()) {
    SocketChannel channel = (SocketChannel) key.channel();
    // 如果正在连接,则完成连接
    if(channel.isConnectionPending()){
    channel.finishConnect();
    }
    // 设置成非阻塞
    channel.configureBlocking(false);
    //在这里可以给服务端发送信息哦
    channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes("utf-8")));
    //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
    channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件
    } else if (key.isReadable()) {
    read(key);
    }
    }
    }
    }
    /**
    * 处理读取服务端发来的信息 的事件
    * @param key
    * @throws IOException
    */
    public void read(SelectionKey key) throws IOException{
    //和服务端的read方法一样
    // 服务器可读取消息:得到事件发生的Socket通道
    SocketChannel channel = (SocketChannel) key.channel();
    // 创建读取的缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(512);
    channel.read(buffer);
    byte[] data = buffer.array();
    String msg = new String(data).trim();
    System.out.println("客户端收到信息:" + msg);
    ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));
    channel.write(outBuffer);// 将消息回送给客户端
    }


    /**
    * 启动客户端测试
    * @throws IOException
    */
    public static void main(String[] args) throws IOException {
    NioClient client = new NioClient();
    client.initClient("localhost",8000);
    client.connect();
    }

    }
  • 相关阅读:
    ios 数据类型转换 UIImage转换为NSData NSData转换为NSString
    iOS UI 12 block传值
    iOS UI 11 单例
    iOS UI 08 uitableview 自定义cell
    iOS UI 07 uitableviewi3
    iOS UI 07 uitableviewi2
    iOS UI 07 uitableview
    iOS UI 05 传值
    iOS UI 04 轨道和动画
    iOS UI 03 事件和手势
  • 原文地址:https://www.cnblogs.com/zhangjwcode/p/6963828.html
Copyright © 2011-2022 走看看