博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
nio服务器端
阅读量:6908 次
发布时间:2019-06-27

本文共 5536 字,大约阅读时间需要 18 分钟。

hot3.png

使用异步处理IO的方式,使用一个线程,处理大量的链接。

先对NIO原理和通信模型做一些了解。

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

2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。 
3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。 

Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:

 

事件名 对应值
服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
读事件 SelectionKey.OP_READ(1)
写事件 SelectionKey.OP_WRITE(4)

服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:

0184183e-286c-34f1-9742-4adaa28b7003.jpg

package priv.lee.paradise.nioserver.listen;import priv.lee.paradise.nioserver.distribute.DoGetOperater;import priv.lee.paradise.nioserver.distribute.DoPostOperater;import priv.lee.paradise.nioserver.util.SocketUtil;import java.nio.channels.SocketChannel;/** * Created by li on 2016/12/12. * 当所监听的端口有连接进来的时候,可通过端口监听器通知观察者进行下一步操作。 */class ListenerObserver {    /**     * 当有新连接进来的时候,会通知该类调用静态方法。     * 每传入一个新的socketChannel会新建一个线程对其进行处理。     *     * @param socketChannel 客户端和服务器的嵌套字。     */    static void acceptNotify(SocketChannel socketChannel) {        String requestInfo = SocketUtil.getRequest(socketChannel);        System.out.println(requestInfo);        if (requestInfo.contains("GET")) {            DoGetOperater doGetOperater = new DoGetOperater(socketChannel, requestInfo);            doGetOperater.doGet();        }        if (requestInfo.contains("POST")) {            DoPostOperater doPostOperater = new DoPostOperater(requestInfo, socketChannel);            doPostOperater.doPost();        }    }}
package priv.lee.paradise.nioserver.listen;import java.net.InetSocketAddress;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 li on 2016/12/11. * 使用nio创建的端口监听器。本类不再使用静态方法。 */public class PortListener {    private int port;    private Selector selector;    /**     * 初始化监听器,要创建监听器必须传入端口号     * param:port 端口号。     * return:null     */    public PortListener(int port) {        this.port = port;        init();    }    /**     * 初始化端口监听器,获取通道、注册事件。     * param:null;     * return:null     */    private void init() {        ServerSocketChannel serverSocketChannel = getServerSocketChannel();        selector = getSelector();        registerEvent(serverSocketChannel, selector, SelectionKey.OP_ACCEPT);    }    /**     * 开始监听事件。     */    public void startListening() {        System.out.println("开始监听" + port + "号端口");        while (!Thread.interrupted()) {            handleEvent();        }    }    /**     * 但监听器监听到注册过的事件到来的时候,     * selector.select();为线程阻塞方法,当感兴趣的事件到来的时候才会触发这个方法。     */    private void handleEvent() {        try {            selector.select();            Iterator iterator = selector.selectedKeys().iterator();            while (iterator.hasNext()) {                SelectionKey key = (SelectionKey) iterator.next();                if (key.isAcceptable()) {                    handleAcceptableEvent(key);                } else if (key.isReadable()) {                    handleReadableEvent(key);                }                iterator.remove();            }        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 处理一个可读的事件。     *     * @param key 可读事件     */    private void handleReadableEvent(SelectionKey key) {        SocketChannel socketChannel = (SocketChannel) key.channel();        ListenerObserver.acceptNotify(socketChannel);    }    /**     * 处理Acceptable事件     *     * @param key 以注册是的事件触发。并向选择器注册读取事件。     */    private void handleAcceptableEvent(SelectionKey key) {        ServerSocketChannel serverSocketChannel;        try {            serverSocketChannel = (ServerSocketChannel) key.channel();            SocketChannel socketChannel = serverSocketChannel.accept();            socketChannel.configureBlocking(false);            socketChannel.register(selector, SelectionKey.OP_READ);        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 获取一个ServerSocketChannel对象,并对其进行一些配置。     * param:null。     * return:ServerSocketChannel     */    private ServerSocketChannel getServerSocketChannel() {        ServerSocketChannel serverSocketChannel = null;        try {            serverSocketChannel = ServerSocketChannel.open();            serverSocketChannel.configureBlocking(false);            serverSocketChannel.socket().bind(new InetSocketAddress(port));        } catch (Exception e) {            e.printStackTrace();        }        return serverSocketChannel;    }    /**     * 获取一个选择器。     * param:null     * return Selector.     */    private Selector getSelector() {        Selector selector = null;        try {            selector = Selector.open();        } catch (Exception e) {            e.printStackTrace();        }        return selector;    }    /**     * 为通道注册选择器要监听的事件。     *     * @param serverSocketChannel 待监测的通道。     * @param selector            选择器     * @param opAccept            待监听的事件选项(int)。     */    private void registerEvent(ServerSocketChannel serverSocketChannel, Selector selector, int opAccept) {        try {            serverSocketChannel.register(selector, opAccept);        } catch (Exception e) {            e.printStackTrace();        }    }}

转载于:https://my.oschina.net/marjeylee/blog/804474

你可能感兴趣的文章
solidity智能合约[49]-安全-溢出***
查看>>
Centos时间同步问题
查看>>
Spring Cloud - 关于Feign的常见问题总结
查看>>
百晓生详解nginx(下)nginx在实际生产环境中的应用(该帖连载更新)
查看>>
解决大数据难题 阿里云MaxCompute获科技大奖
查看>>
爬虫笔记3:requests库使用
查看>>
Linux 基础知识(七)
查看>>
创建私有CA
查看>>
(最简单)红米手机3S 3X的usb调试模式在哪里打开的流程
查看>>
如何将PDF压缩有什么简单的方法
查看>>
Java数据结构和算法(五)——队列
查看>>
jQuery
查看>>
Java实现爬虫示例
查看>>
JNA 基础篇<二> 结构体
查看>>
电脑上怎么将PDF图纸文件快速转换成DWG格式CAD图纸?
查看>>
Docker部署微服务详解
查看>>
我的友情链接
查看>>
MySQL常用函数大全
查看>>
以MYSQL作为HIVE的元数据库,配置HIVE的远程连接
查看>>
写在认证过后
查看>>