文档章节

NIO同步非阻塞

刘付kin
 刘付kin
发布于 2016/12/07 11:40
字数 1367
阅读 15
收藏 0
点赞 0
评论 0

#jdk1.5版本新引入的socket编程!!

##它与BIO的区别如下图:

输入图片说明

#为什么需要引入NIO?

主要是传统的BIO在服务器端以阻塞的形式来接受(accept)客户端的请求,每接受一个请求就会创建一个线程(thread)来和这个客户端进行交互,每个线程只对接一个客户端,而且很多时候这个线程都会在inputstream和outputstream哪里阻塞了,严重浪费线程资源。

#模型分析:

其实NIOserver和操作系统的关系就是=====》消费者和生产者的关系。既然是消费者和生产者的关系,就可以在他们之间添加一个消息队列来缓解这种速度不匹配的问题。

模型图:

输入图片说明

如上图所示,NIO服务器端内部的Selector里面维护一个“事件队列”,时间包括accept、read、write等。也就是说这是的服务器端不再需要阻塞式地等待连接请求(accept),只需要做一下步骤:

  • 1:不断的去轮询这个消息队列,拿到事件。
  • 2:判断事件类型,如果是op_accept就连接、如果是op_read就去操作系统哪里读取到本身的ByteBuff中。

#总结

NIO是同步(轮询)非阻塞的socket通信编程。主要是借助了:生产者和消费者问题模型。

它里面只要有两个东西:

  • 1.)ByteBuffer。其实就是对byte[]和一些操作byte[]的方法的封装,包括编码和解码等。
  • 2.)Selector。多路复用器。内部维护一个“事件消息队列”,server不断的轮询这个Selector的消息队列,获取事件类型后,进行处理。
  • 3.)NIO由于没法使用数据流的概念,所以不适合做文件传输。只适合于做响应式的请求,比如RPC

#具体的实现:

NIOServer

package org.java.io.mynio;

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.nio.charset.Charset;
import java.util.Iterator;

public class NIOServer {

	ByteBuffer buffer = null;
	Selector selector = null;
	
	public void init(){
		buffer = ByteBuffer.allocate(1024);
		try {
			selector = Selector.open();
			
			ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
			serverSocketChannel.bind(new InetSocketAddress(4700));
			serverSocketChannel.configureBlocking(false);
			
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("服务器启动了,--->4700");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void run(){
		while (true) {
			try {
				selector.select();
				Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
				while (ite.hasNext()) {
					SelectionKey key = ite.next();
					ite.remove();
					handlerKey(key);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void handlerKey(SelectionKey key){
		if(key.isAcceptable()){
			try {
				ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
				SocketChannel channel = serverSocketChannel.accept();
				channel.configureBlocking(false);
				channel.register(selector, SelectionKey.OP_READ);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else if(key.isReadable()){
			try {
				SocketChannel channel = (SocketChannel) key.channel();
				buffer.clear();
				int count = channel.read(buffer);
				if(count > 0){
					buffer.flip();
					System.out.println("Server:收到了" + Charset.forName("utf-8").newDecoder().decode(buffer).toString());
					//同时向客户端返回一个信息
					buffer.clear();
					buffer.put("I am Server".getBytes());
					buffer.flip();
//这里中间一定不能够打印buffer中的信息,否则的话,会是的buffer中的position和limit指针移位,
//最后buffer中就“相当于”没有信息了,(这一定要记住!!!)
					channel.write(buffer);
				}else{
					channel.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		NIOServer server = new NIOServer();
		server.init();
		server.run();
	}
	
	public ByteBuffer getBuffer() {
		return buffer;
	}
	public void setBuffer(ByteBuffer buffer) {
		this.buffer = buffer;
	}
	public Selector getSelector() {
		return selector;
	}
	public void setSelector(Selector selector) {
		this.selector = selector;
	}
}

NioClient

package org.java.io.mynio;

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.nio.charset.Charset;
import java.util.Iterator;

public class NIOClient {

	private ByteBuffer buffer = null;
	private Selector selector = null;
	private SocketChannel socketChannel = null;
	
	public static void main(String[] args) {
		NIOClient nioClient = new NIOClient();
		nioClient.init();
		nioClient.connectServer();
		nioClient.select();
	}
	
	public void init(){
		buffer = ByteBuffer.allocate(1024);
		try {
			selector = Selector.open();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void connectServer(){
		try {
			socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false);
			socketChannel.connect(new InetSocketAddress("localhost", 4700));
			socketChannel.register(selector, SelectionKey.OP_CONNECT);
			System.out.println("向服务器发送连接请求,等等请求结果!!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void select(){
		while (true) {
			try {
				selector.select();
				Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
				while (ite.hasNext()) {
					SelectionKey key = ite.next();
					ite.remove();
					handleKey(key);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void handleKey(SelectionKey key){
		SocketChannel channel = (SocketChannel) key.channel();
		try{
			if(key.isConnectable()){
	            if( channel.isConnectionPending()){
	                if(channel.finishConnect()){
	                	//只有当连接成功后才能注册OP_READ事件
	                	channel.register(selector, SelectionKey.OP_READ);
	                    buffer.clear();
	    				buffer.put("I am Client".getBytes("utf-8"));
	    				buffer.flip();
	    				channel.write(buffer);
	                }else{
	                    key.cancel();
	                }
	            }
	        }else if(key.isReadable()){
				buffer.clear();
				int count = channel.read(buffer);
				if(count > 0){
					buffer.flip();
					System.out.println("Client:收到了" + Charset.forName("utf-8").newDecoder().decode(buffer));
					
					//重新设置ByteBuffer,将发送的信息发送过去。
					buffer.clear();
					buffer.put("liufu".getBytes("utf-8"));
					buffer.flip();
//这里一定要注意,不能够对buffer来进行任何的读操作,否则会是的它里面的position和limit指针便宜了,postion和limit相等了,也就是buffer里面的信息相当于没有了。
//System.out.println(Charset.forName("utf-8").newDecoder().decode(buffer));
//System.out.println(Charset.forName("utf-8").newDecoder().decode(buffer));
					channel.write(buffer);
				}else{
					channel.close();
				}
			}
		}catch(Exception e){
			
		}
	}
}

#总结:

Nio主要借助于:ByteBuffer和Selector。

  • 1.)其实ByteBuffer里面就是封装了byte[]以及对这个数组进行操作的方法。
  • 2.)Selector里面就维护一个事件的“消息队列”。操作系统往里面添加事件,而服务器端不断的轮询这个Selector中的消息队列,拿到事件后在对这个事件进行处理。

#注意的问题:

  • ByteBuffer数组与byte[]的转换

    ByteBuffer在 buffer.clear();

    buffer.put("liufu".getBytes("utf-8"));

    buffer.flip();

    之后一定不能够对这个数组进行“读”操作,否则的话,这个ByteBuffer就会出现position和limit重叠的现象, 也就相当于ByteBuffer中没有数据了。如图:

输入图片说明

  • 而byte[]就没有这种问题

    byte[] bt = "byte Test".getBytes();

    System.out.println(new String(bt));

    System.out.println(new String(bt));


#后续追加::

其实上述的ByteBuf问题,只需要发送前byffer.flip();就解决了。

输入图片说明


#思考:

服务器端启动后,只有一个主线程在轮询那个Selector。而且拿到一个key后,就会对它进行处理,处理完后再去遍历Selector中的其他时间,所有事情都有这个线程处理,速度很慢,怎么解决??

#答案:

引入线程池。但是需要解决Selector同步的问题。

有这种需求就会有相应的解决方案框架:netty , mino

© 著作权归作者所有

共有 人打赏支持
刘付kin
粉丝 6
博文 100
码字总数 72832
作品 0
深圳
『IO』BIO,NIO和AIO以及相关名词解析

网络通信中经常用到IO操作,IO操作主要有BIO,NIO以及AIO等几种模式,要弄清这几种IO模式,又需要弄懂阻塞/非阻塞,同步/异步概念。网上对这几种概念的解释各不相同,我在本文中记录下我自己...

dejunz ⋅ 2017/09/19 ⋅ 0

Linux IO模型与Java NIO

概述 看Java NIO一篇文章的时候又看到了“异步非阻塞”这个概念,一直处于似懂非懂的状态,想解释下到底什么是异步 什么是非阻塞,感觉抓不住重点。决定仔细研究一下。 本文试图研究以下问题...

yingtju ⋅ 2017/03/19 ⋅ 0

浅谈“阻塞同步”,“BIO、NIO、AIO”

一、阻塞?同步? 可能大家平常会经常听到这两个名词,但是没花太多心思详细了解,今天就来揭开这层面纱。 一次IO操作,以read方法举例,会经历两个阶段: (1)等待数据准备(Waitingfor the...

叫我宫城大人 ⋅ 2017/09/04 ⋅ 0

Java NIO那点事

什么是NIO? New Input/Output 基于通道和缓冲区的I/O方式 可使用Native函数库直接分配堆外内存 通过存储于Java堆的DirectByteBuffer对象直接操作已分配的堆外内存 同步非阻塞模型 与传统IO区...

爱做梦的胖子 ⋅ 2017/06/15 ⋅ 0

Java IO: BIO, NIO, AIO(含代码实现)

BIO, NIO, AIO,本身的描述都是在Java语言的基础上的。 而描述IO,我们需要从三个层面: 编程语言 实现原理 底层基础 从编程语言层面 BIO, NIO, AIO以Java的角度理解: BIO,同步阻塞式IO,简...

tantexian ⋅ 2016/05/11 ⋅ 0

servlet3异步原理与实践

一、什么是Servlet servlet 是基于 Java 的 Web 组件,由容器进行管理,来生成动态内容。像其他基于 Java 的组件技术一样,servlet 也是基于平台无关的 Java 类格式,被编译为平台无关的字节...

新栋BOOK ⋅ 2017/10/23 ⋅ 0

JAVA中IO技术:BIO、NIO、AIO

1、同步异步、阻塞非阻塞概念 由上描述基本可以总结一句简短的话,同步和异步是目的,阻塞和非阻塞是实现方式。 1 同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己...

那位先生 ⋅ 2016/08/04 ⋅ 0

Netty 之传统 BIO、NIO 编程

说明 系列文章:http://www.jianshu.com/p/594441fb9c9e 本文完全参考自《Netty权威指南(第2版)》,李林峰著。 传统 BIO 编程是什么样的? 基本的网络编程模型是,即两个进程间相互通信,其...

被称为L的男人 ⋅ 2017/09/02 ⋅ 0

Java BIO、NIO、AIO 比较

理解一下概念,以银行取款为例 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写)。 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读...

Corlang ⋅ 2016/02/01 ⋅ 0

关于spring cloud的ZUUL的一个疑问

Netflix宣布了通用API网关 Zuul的 架构转型。Zuul原本采用同步阻塞架构,转型后叫作Zuul 2,采用异步非阻塞架构。Zuul 2和 Netflix宣布了通用API网关 Zuul的 架构转型。Zuul原本采用同步阻塞...

爱吃大肉包 ⋅ 2017/12/11 ⋅ 4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

开启远程SSH

SSH默认没有开启账号密码登陆,需要再配置表中修改: vim /etc/ssh/sshd_configPermitRootLogin yes #是否可以使用root账户登陆PasswordAuthentication yes #是都开启密码登陆ser...

Kefy ⋅ 14分钟前 ⋅ 0

Zookeeper3.4.11+Hadoop2.7.6+Hbase2.0.0搭建分布式集群

有段时间没更新博客了,趁着最近有点时间,来完成之前关于集群部署方面的知识。今天主要讲一讲Zookeeper+Hadoop+Hbase分布式集群的搭建,在我前几篇的集群搭建的博客中已经分别讲过了Zookeep...

海岸线的曙光 ⋅ 21分钟前 ⋅ 0

js保留两位小数方法总结

本文是小编针对js保留两位小数这个大家经常遇到的经典问题整理了在各种情况下的函数写法以及遇到问题的分析,以下是全部内容: 一、我们首先从经典的“四舍五入”算法讲起 1、四舍五入的情况...

孟飞阳 ⋅ 39分钟前 ⋅ 0

python log

python log 处理方式 log_demo.py: 日志代码。 #! /usr/bin/env python# -*- coding: utf-8 -*-# __author__ = "Q1mi""""logging配置"""import osimport logging.config# 定义三种......

inidcard ⋅ 54分钟前 ⋅ 0

mysql 中的信息数据库以及 shell 查询 sql

Information_schema 是 MySQL 自带的信息数据库,里面的“表”保存着服务器当前的实时信息。它提供了访问数据库元数据的方式。 什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,...

blackfoxya ⋅ 56分钟前 ⋅ 0

maven配置阿里云镜像享受飞的感觉

1.在maven目录下的conf/setting.xml中找到mirrors添加如下内容,对所有使用改maven打包的项目生效。 <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.al......

kalnkaya ⋅ 56分钟前 ⋅ 0

centos7下创建新用户并授权

1、创建新用户 创建一个用户名为:test adduser test 创建初始密码: passwd test 2、授予root权限 个人用户的权限只可以在/home/test下有完整权限,其他目录要看别人授权。而经常需要roo...

xixingzhe ⋅ 今天 ⋅ 0

求助:TiledMap如何旋转对象呢?

比如我要旋转一个梯子的角度,单纯在TiledMap旋转角度好像没有效果。那是要用代码来控制角度,还是说只能通过导入相对应的斜的图片才可以呢?

花谢自相惜 ⋅ 今天 ⋅ 0

Micronaut 之HelloWorld!

小试一下Micronaut,按照官方文档跑了一下helloworld 第一步克隆,按照官方文档是: git clone git@github.com:micronaut-projects/micronaut-core.git 结果怎么是这样?? 换个方法吧 git ...

桂哥 ⋅ 今天 ⋅ 0

pom文件

Aeroever ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部