文档章节

nio服务器端

marjey
 marjey
发布于 2016/12/12 14:31
字数 1038
阅读 9
收藏 0

使用异步处理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的通信模型示意图:

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();
        }
    }
}

© 著作权归作者所有

共有 人打赏支持
marjey
粉丝 2
博文 173
码字总数 139219
作品 0
昆明
使用Nginx代理thrift NIO实现SSL链路加密

1 目标说明 1.1 调研目的 本次调研主要为了解决两个问题: thrift提供的SSL API只支持BIO(阻塞式IO),而我们使用的是NIO API,希望能在不改变IO模型的前提下对链路进行加密; 未来系统可能...

囚兔
2016/06/22
2.9K
11
JAVA NIO non-blocking模式实现高并发服务器

Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要讲的是如何使用NIO的网络新特性,来构建高性能非阻塞并发服务器. 文章基于个人理解,我也来搞搞NIO.,...

Mr&Cheng
2013/01/30
0
1
JAVA NIO non-blocking模式实现高并发服务器

Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要讲的是如何使用NIO的网络新特性,来构建高性能非阻塞并发服务器. 文章基于个人理解,我也来搞搞NIO.,...

xpbug
2013/01/10
0
4
关于BIO和NIO的理解

最近大概看了ZooKeeper和Mina的源码发现都是用Java NIO实现的,所以有必要搞清楚什么是NIO。下面是我结合网络资料自己总结的,为了节约时间图示随便画的,能达意就行。 简介:BIO:同步阻塞式...

天天顺利
2015/10/23
7.3K
0
Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (服务器端)

目录 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 之二 NIO Channel 小结 Java NIO 的前生今世 之三 NIO Buffer 详解 Java NIO 的前生...

永顺
2017/11/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

day63-20180821-流利阅读笔记-待学习

性别歧视在日本:“我是女生,所以社会不让我学医” 毛西 2018-08-21 1.今日导读 大家在看病的时候,有留意过女医生的比例吗?在性别歧视现象十分严重的日本,男医生和女医生的比例达到了惊人...

aibinxiao
50分钟前
2
0
Ubuntu18.04 显卡GF-940MX安装NVIDIA-390.77

解决办法: 下面就给大家一个正确的姿势在Ubuntu上安装Nvidia驱动: (a)首先去N卡官网下载自己显卡对应的驱动:www.geforce.cn/drivers (b)下载后好放在英文路径的目录下,怎么简单怎么来...

AI_SKI
今天
4
0
深夜胡思乱想

魔兽世界 最近魔兽世界出了新版本, 周末两天升到了满级,比之前的版本体验好很多,做任务不用抢怪了,不用组队打怪也是共享拾取的。技能简化了很多,哪个亮按哪个。 运维 服务器 产品 之间的...

Firxiao
今天
1
0
MySQL 8 在 Windows 下安装及使用

MySQL 8 带来了全新的体验,比如支持 NoSQL、JSON 等,拥有比 MySQL 5.7 两倍以上的性能提升。本文讲解如何在 Windows 下安装 MySQL 8,以及基本的 MySQL 用法。 下载 下载地址 https://dev....

waylau
今天
2
0
微信第三方平台 access_token is invalid or not latest

微信第三方开发平台code换session_key说的特别容易,但是我一使用就带来无穷无尽的烦恼,搞了一整天也无济于事. 现在记录一下解决问题的过程,方便后来人参考. 我遇到的这个问题搜索了整个网络也...

自由的开源
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部