简易实现Java的NIO

NIO (New I/O): NIO是一种同步非阻塞的I/O模型,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。

public class NioServerHandle implements Runnable {
    private Selector selector;
    private ServerSocketChannel serverChannel;
    private volatile boolean started;

    public NioServerHandle(int port) {
        try {
            selector = Selector.open();
            serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false);
            serverChannel.socket().bind(new InetSocketAddress(port));
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            started = true;
            System.out.println("服务端已启动,端口号:" + port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        started = false;
    }

    @Override
    public void run() {
        //循环遍历selector
        while (started) {
            try {
                //阻塞,只有当至少一个注册的事件发生的时候才会继续
                selector.select();
                //获取当前有哪些事件可以使用
                Set keys = selector.selectedKeys();
                //转换为迭代器
                Iterator iter = keys.iterator();
                SelectionKey key = null;
                while (iter.hasNext()) {
                    key = iter.next();
                    iter.remove();
                    try {
                        handleInput(key);
                    } catch (IOException e) {
                        e.printStackTrace();
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //selector关闭后会自动释放里面管理的资源
        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //处理业务逻辑
    private void handleInput(SelectionKey key) throws IOException {
        if (key.isValid()) {
            //TODO:处理新接入的请求消息
            if (key.isAcceptable()) {
                //获取监听当前事件的channel
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                //通过ServerSocketChannel的accept创建SocketChannel实例
                //完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                SocketChannel sc = ssc.accept();
                System.out.println("=====socket channel建立连接");
                //设置为非阻塞
                sc.configureBlocking(false);
                //连接已经完成,开始监听读事件
                sc.register(selector, SelectionKey.OP_READ);
            }
            //TODO:读取消息事件
            if (key.isReadable()) {
                System.out.println("=======socket channel数据准备完成,可以开始读取=======");
                SocketChannel sc = (SocketChannel) key.channel();
                //创建ByteBuffer,并开辟一个1M的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取请求码流,返回读取到的字节数
                int readBytes = sc.read(buffer);
                //读取到字节,对字节进行编解码
                if (readBytes > 0) {
                    //将缓冲区当前的limit设置为position=0
                    //用于后续对缓冲区的读取操作
                    buffer.flip();
                    //根据缓冲区可读字节数创建字节数组
                    byte[] bytes = new byte[buffer.remaining()];
                    //将缓冲区可读字节数组复制到新建的数组中
                    buffer.get(bytes);
                    String message = new String(bytes, "UTF-8");
                    System.out.println("服务器接收到消息:" + message);
                    //处理数据
                    String result = response(message);
                    //发送应答消息
                    doWrite(sc, result);
                }
                //链路已关闭,释放资源
                else if (readBytes < 0) {
                    key.cancel();
                    sc.close();
                }
            }
        }
    }

    //发送应答消息
    private void doWrite(SocketChannel channel, String response) throws IOException {
        //将消息编码为字节数组
        byte[] bytes = response.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        buffer.put(bytes);
        //flip操作
        buffer.flip();
        //发送缓冲区的字节数组
        channel.write(buffer);
    }

    //返回给客户端的应答
    public String response(String msg) {
        return "Hello," + msg + ",Now is " + new java.util.Date(
                System.currentTimeMillis()).toString();
    }
}
public class NioClientHandle implements Runnable {
    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;

    private volatile boolean started;

    public NioClientHandle(String host, int port) {
        this.host = host;
        this.port = port;
        try {
            //创建选择器
            selector = Selector.open();
            //打开通道
            socketChannel = SocketChannel.open();
            //如果为true,则此通道将被置于阻塞模式
            //如果为false,则此通道将被置于非阻塞模式
            socketChannel.configureBlocking(false);
            started = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        started = false;
    }

    @Override
    public void run() {
        try {
            doConnect();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        //循环遍历selector
        while (started) {
            try {
                //阻塞,只有当至少一个注册的事件发生的时候才会继续
                selector.select();
                //获取当前有哪些事件可以使用
                Set keys = selector.selectedKeys();
                //转换为迭代器
                Iterator iter = keys.iterator();
                SelectionKey key = null;
                while (iter.hasNext()) {
                    key = iter.next();
                    iter.remove();
                    try {
                        handleInput(key);
                    } catch (IOException e) {
                        e.printStackTrace();
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //selector关闭后会自动释放里面管理的资源
        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //具体的事件处理方法
    private void handleInput(SelectionKey key) throws IOException {
        if (key.isValid()) {
            //获得监听当前事件的channel
            SocketChannel sc = (SocketChannel)key.channel();
            //TODO:连接事件
            if (key.isConnectable()) {
                if (sc.finishConnect()) {
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else {
                    System.exit(1);
                }
            }
            //TODO:有数据可读事件
            if (key.isReadable()) {
                //创建ByteBuffer,并开辟一个1M的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取请求码流,返回读取到的字节数
                int readBytes = sc.read(buffer);
                //读取到字节,对字节进行编解码
                if (readBytes > 0) {
                    //将缓冲区当前的limit设置为position, position = 0
                    //用于后续对缓冲区的读取操作
                    buffer.flip();
                    //根据缓冲区可读字节数创建字节数据
                    byte[] bytes = new byte[buffer.remaining()];
                    //将缓冲区可读字节数组复制到新建的数组中
                    buffer.get(bytes);
                    String result = new String(bytes, "UTF-8");
                    System.out.println("accept message: " + result);
                } else if (readBytes < 0) {
                    key.cancel();
                    sc.close();
                }
            }
         }
    }

    //发送消息
    private void doWrite(SocketChannel channel, String request) throws IOException {
        //将消息编码为字节数组
        byte[] bytes = request.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
    }

    //创建连接
    private void doConnect() throws IOException {
        if (socketChannel.connect(new InetSocketAddress(host, port))) {
            //连接完成,顺序执行
        } else {
            //连接还未完成,因此注册连接就绪事件,向selector表示关注这个事件
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }
    }

    //写数据对外暴露的API
    public void sendMsg(String msg) throws IOException {
        socketChannel.register(selector,SelectionKey.OP_READ);
        doWrite(socketChannel, msg);
    }
}
public class NioServer {
    private static NioServerHandle nioServerHandle;

    public static void start() {
        if (nioServerHandle != null) {
            nioServerHandle.stop();
        }
        nioServerHandle = new NioServerHandle(8888);
        new Thread(nioServerHandle, "Server").start();
    }

    public static void main(String[] args) {
        start();
    }
}
public class NioClient {
    private static NioClientHandle nioClientHandle;

    public static void start() {
        if (nioClientHandle != null) {
            nioClientHandle.stop();
        }
        nioClientHandle = new NioClientHandle("127.0.0.1", 8888);
        new Thread(nioClientHandle, "Client").start();
    }
    //向服务器发送消息
    public static boolean sendMsg(String msg) throws Exception {
        nioClientHandle.sendMsg(msg);
        return true;
    }

    public static void main(String[] args) throws Exception {
        start();
        Scanner scanner = new Scanner(System.in);
        while (NioClient.sendMsg(scanner.nextLine()));
    }
}

 

你可能感兴趣的:(Java)