文档章节

nio服务器端

marjey
 marjey
发布于 2016/12/12 14:31
字数 1038
阅读 8
收藏 0
点赞 0
评论 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
博文 146
码字总数 139219
作品 0
昆明
使用Nginx代理thrift NIO实现SSL链路加密

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

囚兔 ⋅ 2016/06/22 ⋅ 11

JAVA NIO non-blocking模式实现高并发服务器

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

Mr&Cheng ⋅ 2013/01/30 ⋅ 1

JAVA NIO non-blocking模式实现高并发服务器

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

xpbug ⋅ 2013/01/10 ⋅ 4

关于BIO和NIO的理解

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

天天顺利 ⋅ 2015/10/23 ⋅ 0

Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (服务器端)

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

永顺 ⋅ 2017/11/29 ⋅ 0

Java NIO 高性能服务器编程

最近在研究如何使用NIO技术开发一个多用户在线的即时通讯工具,如题,请问各位大神: 1、如何架构服务器端设计能够增加系统的吞吐量和效率呢? 2、即时通讯过程中非常常见的就是客户端A发送信...

passenger ⋅ 2014/09/19 ⋅ 7

MINA是否适合长连接通讯

目前正在考虑socket长连接的解决办法。客户端是C写的,每隔5秒左右往服务器端发送数据,同时服务器要返回一些指令信息,所以需要保持着长连接。目前server 端用NIO实现,没用其他框架。 但是...

sam.ray ⋅ 2010/09/18 ⋅ 6

Java NIO原理图文分析及代码实现

前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。可以参考:http://baik...

phacks ⋅ 2015/08/19 ⋅ 0

NIO服务主动推送信息问题

今天在做基于NIO的推送服务器的时候,因为之前一直使用的IO,现在考虑更换用NIO的,在做Demo的时候发现了很多问题,捉摸了两天难以解决,而且网上的资料也不没有解答,上来问问各位大神们,我...

Defens ⋅ 2015/03/03 ⋅ 4

Java NIO原理图文分析及代码实现

目录:一.java NIO 和阻塞I/O的区别 阻塞I/O通信模型 java NIO原理及通信模型 二.java NIO服务端和客户端代码实现 具体分析: 一.java NIO 和阻塞I/O的区别 阻塞I/O通信模型 假如现在你对...

Flyer_cao ⋅ 2016/12/19 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 28分钟前 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

版本控制工具

CSV , SVN , GIT ,VSS

颖伙虫 ⋅ 昨天 ⋅ 0

【2018.06.19学习笔记】【linux高级知识 13.1-13.3】

13.1 设置更改root密码 13.2 连接mysql 13.3 mysql常用命令

lgsxp ⋅ 昨天 ⋅ 0

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部