- 浏览: 894860 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
天天来注册:
...
try catch finally 用法 -
tadpole_java:
谢谢你的分享。
二十七、Qt数据库(七)QSqlRelationalTableModel(转) -
359449749tan:
android之EditText文本监听(addTextChangedListener) -
michael_wang:
人过留名 多谢分享
Android NOtification 使用 -
wilsonchen:
wangqi0614 写道这个删除是删除所有的把?能不能值删除 ...
Android的SharedPreferences保存与删除数据简单实例
引用
第一部分:简单介绍NIO
服务器在合理时间内处理大量客户机的请求的能力取决于服务器使用I/O流的效率,同时为成百上千的客户提供服务的服务器必须能并发的使用I/O服务。
用Java语言写的服务器,由于其线程与客户机之比几乎是一比一,因而易受到大量线程开销的影响,其结果是即导致性能问题,又缺乏伸缩性。
引入java.nio包解决了线程开销问题,包中最重要的是新的 SelectableChannel 类和 Selector 类。通道(channel)是客户机和服务器之间的一种通信方式。 选择器(selector)与 Windows 消息循环类似,它从不同客户机捕获各种事件并将它们分派到相应的事件处理程序。本文展示这两个类如何协同工作,从而为 Java 平台创建非阻塞 I/O 机制。主要有四个类:
1. Buffer:它是包含数据且用于读写的线形表结构。其中还提供了一个特殊类用于内存映射文件的I/O操作。
2. Charset:它提供Unicode字符串影射到字节序列以及逆影射的操作。
3. Channels:包含socket,file和pipe三种管道,它实际上是双向交流的通道。
4. Selector:它将多元异步I/O操作集中到一个或多个线程中(它可以被看成是Unix中select()函数或Win32中WaitForSingleEvent()函数的面向对象版本)。
第二部分:传统IO
可以分析创建服务器的每个具体步骤。首先创建ServerSocket
ServerSocket server=new ServerSocket(10000);
然后接受新的连接请求
Socket newConnection=server.accept();
对于accept方法的调用将造成阻塞,直到ServerSocket接受到一个连接请求为止。一旦连接请求被接受,服务器可以读客户socket中的请求。
InputStream in = newConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buffer = new BufferedReader(reader);
Request request = new Request();
while(!request.isComplete()) {
String line = buffer.readLine();
request.addLine(line);
}
这样的操作有两个问题,首先BufferedReader类的readLine()方法在其缓冲区未满时会造成线程阻塞,只有一定数据填满了缓冲区或者客户关闭了套接字,方法才会返回。其次,它回产生大量的垃圾,BufferedReader创建了缓冲区来从客户套接字读入数据,但是同样创建了一些字符串存储这些数据。虽然BufferedReader内部提供了StringBuffer处理这一问题,但是所有的String很快变成了垃圾需要回收。
同样的问题在发送响应代码中也存在
Response response = request.generateResponse();
OutputStream out = newConnection.getOutputStream();
InputStream in = response.getInputStream();
int ch;
while(-1 != (ch = in.read())) {
out.write(ch);
}
newConnection.close();
类似的,读写操作被阻塞而且向流中一次写入一个字符会造成效率低下,所以应该使用缓冲区,但是一旦使用缓冲,流又会产生更多的垃圾。
传统的解决方法
通常在Java中处理阻塞I/O要用到线程(大量的线程)。一般是实现一个线程池用来处理请求,如图二
第三部分:新IO:NIO
1. Buffer
传统的I/O不断的浪费对象资源(通常是String)。新I/O通过使用Buffer读写数据避免了资源浪费。Buffer对象是线性的,有序的数据集合,它根据其类别只包含唯一的数据类型。
java.nio.Buffer 类描述
java.nio.ByteBuffer 包含字节类型。 可以从ReadableByteChannel中读在 WritableByteChannel中写
java.nio.MappedByteBuffer 包含字节类型,直接在内存某一区域映射
java.nio.CharBuffer 包含字符类型,不能写入通道
java.nio.DoubleBuffer 包含double类型,不能写入通道
java.nio.FloatBuffer 包含float类型
java.nio.IntBuffer 包含int类型
java.nio.LongBuffer 包含long类型
java.nio.ShortBuffer 包含short类型
可以通过调用allocate(int capacity)方法或者allocateDirect(int capacity)方法分配一个Buffer。特别的,你可以创建MappedBytesBuffer通过调用FileChannel.map(int mode,long position,int size)。直接(direct)buffer在内存中分配一段连续的块并使用本地访问方法读写数据。非直接(nondirect)buffer通过使用Java中的数组访问代码读写数据。有时候必须使用非直接缓冲例如使用任何的wrap方法(如ByteBuffer.wrap(byte[]))在Java数组基础上创建buffer。
2. 字符编码
向ByteBuffer中存放数据涉及到两个问题:字节的顺序和字符转换。ByteBuffer内部通过ByteOrder类处理了字节顺序问题,但是并没有处理字符转换。事实上,ByteBuffer没有提供方法读写String。
Java.nio.charset.Charset处理了字符转换问题。它通过构造CharsetEncoder和CharsetDecoder将字符序列转换成字节和逆转换。
3. 通道(Channel)
你可能注意到现有的java.io类中没有一个能够读写Buffer类型,所以NIO中提供了Channel类来读写Buffer。通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。通道的类等级结构图如图三:
图中ReadableByteChannel和WritableByteChannel分别用于读写。
GatheringByteChannel可以从使用一次将多个Buffer中的数据写入通道,相反的,ScatteringByteChannel则可以一次将数据从通道读入多个Buffer中。你还可以设置通道使其为阻塞或非阻塞I/O操作服务。
为了使通道能够同传统I/O类相容,Channel类提供了静态方法创建Stream或Reader
4. Selector
在过去的阻塞I/O中,我们一般知道什么时候可以向stream中读或写,因为方法调用直到stream准备好时返回。但是使用非阻塞通道,我们需要一些方法来知道什么时候通道准备好了。在NIO包中,设计Selector就是为了这个目的。SelectableChannel可以注册特定的事件,而不是在事件发生时通知应用,通道跟踪事件。然后,当应用调用Selector上的任意一个selection方法时,它查看注册了的通道看是否有任何感兴趣的事件发生。图四是selector和两个已注册的通道的例子
并不是所有的通道都支持所有的操作。SelectionKey类定义了所有可能的操作位,将要用两次。首先,当应用调用SelectableChannel.register(Selector sel,int op)方法注册通道时,它将所需操作作为第二个参数传递到方法中。然后,一旦SelectionKey被选中了,SelectionKey的readyOps()方法返回所有通道支持操作的数位的和。SelectableChannel的validOps方法返回每个通道允许的操作。注册通道不支持的操作将引发IllegalArgumentException异常。下表列出了SelectableChannel子类所支持的操作。
ServerSocketChannel OP_ACCEPT
SocketChannel OP_CONNECT, OP_READ, OP_WRITE
DatagramChannel OP_READ, OP_WRITE
Pipe.SourceChannel OP_READ
Pipe.SinkChannel OP_WRITE
//1. 简单网页内容下载 //这个例子非常简单,类SocketChannelReader使用SocketChannel来下载特定网页的HTML内//容。 package examples.nio; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.net.InetSocketAddress; import java.io.IOException; public class SocketChannelReader{ private Charset charset=Charset.forName("UTF-8");//创建UTF-8字符集 private SocketChannel channel; public void getHTMLContent(){ try{ connect(); sendRequest(); readResponse(); }catch(IOException e){ System.err.println(e.toString()); }finally{ if(channel!=null){ try{ channel.close(); }catch(IOException e){} } } } private void connect()throws IOException{//连接到CSDN InetSocketAddress socketAddress= new InetSocketAddress("http://www.csdn.net",80/); channel=SocketChannel.open(socketAddress); //使用工厂方法open创建一个channel并将它连接到指定地址上 //相当与SocketChannel.open().connect(socketAddress);调用 } private void sendRequest()throws IOException{ channel.write(charset.encode("GET " +"/document" +"\r\n\r\n"));//发送GET请求到CSDN的文档中心 //使用channel.write方法,它需要CharByte类型的参数,使用 //Charset.encode(String)方法转换字符串。 } private void readResponse()throws IOException{//读取应答 ByteBuffer buffer=ByteBuffer.allocate(1024);//创建1024字节的缓冲 while(channel.read(buffer)!=-1){ buffer.flip();//flip方法在读缓冲区字节操作之前调用。 System.out.println(charset.decode(buffer)); //使用Charset.decode方法将字节转换为字符串 buffer.clear();//清空缓冲 } } public static void main(String [] args){ new SocketChannelReader().getHTMLContent(); }
//2. 简单的加法服务器和客户机 //服务器代码 package examples.nio; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.net.InetSocketAddress; import java.io.IOException; /** * SumServer.java * * * * * @version 1.0 */ public class SumServer { private ByteBuffer _buffer=ByteBuffer.allocate(8); private IntBuffer _intBuffer=_buffer.asIntBuffer(); private SocketChannel _clientChannel=null; private ServerSocketChannel _serverChannel=null; public void start(){ try{ openChannel(); waitForConnection(); }catch(IOException e){ System.err.println(e.toString()); } } private void openChannel()throws IOException{ _serverChannel=ServerSocketChannel.open(); _serverChannel.socket().bind(new InetSocketAddress(10000)); System.out.println("服务器通道已经打开"); } private void waitForConnection()throws IOException{ while(true){ _clientChannel=_serverChannel.accept(); if(_clientChannel!=null){ System.out.println("新的连接加入"); processRequest(); _clientChannel.close(); } } } private void processRequest()throws IOException{ _buffer.clear(); _clientChannel.read(_buffer); int result=_intBuffer.get(0)+_intBuffer.get(1); _buffer.flip(); _buffer.clear(); _intBuffer.put(0,result); _clientChannel.write(_buffer); } public static void main(String [] args){ new SumServer().start(); } } // SumServer //客户代码 package examples.nio; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.SocketChannel; import java.net.InetSocketAddress; import java.io.IOException; /** * SumClient.java * * * @version 1.0 */ public class SumClient { private ByteBuffer _buffer=ByteBuffer.allocate(8); private IntBuffer _intBuffer; private SocketChannel _channel; public SumClient() { _intBuffer=_buffer.asIntBuffer(); } // SumClient constructor public int getSum(int first,int second){ int result=0; try{ _channel=connect(); sendSumRequest(first,second); result=receiveResponse(); }catch(IOException e){System.err.println(e.toString()); }finally{ if(_channel!=null){ try{ _channel.close(); }catch(IOException e){} } } return result; } private SocketChannel connect()throws IOException{ InetSocketAddress socketAddress= new InetSocketAddress("localhost",10000); return SocketChannel.open(socketAddress); } private void sendSumRequest(int first,int second)throws IOException{ _buffer.clear(); _intBuffer.put(0,first); _intBuffer.put(1,second); _channel.write(_buffer); System.out.println("发送加法请求 "+first+"+"+second); } private int receiveResponse()throws IOException{ _buffer.clear(); _channel.read(_buffer); return _intBuffer.get(0); } public static void main(String [] args){ SumClient sumClient=new SumClient(); System.out.println("加法结果为 :"+sumClient.getSum(100,324)); } } // SumClient
//3. 非阻塞的加法服务器 //首先在openChannel方法中加入语句 //_serverChannel.configureBlocking(false);//设置成为非阻塞模式 //重写WaitForConnection方法的代码如下,使用非阻塞方式 private void waitForConnection()throws IOException{ Selector acceptSelector = SelectorProvider.provider().openSelector(); /*在服务器套接字上注册selector并设置为接受accept方法的通知。 这就告诉Selector,套接字想要在accept操作发生时被放在ready表 上,因此,允许多元非阻塞I/O发生。*/ SelectionKey acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT); int keysAdded = 0; /*select方法在任何上面注册了的操作发生时返回*/ while ((keysAdded = acceptSelector.select()) > 0) { // 某客户已经准备好可以进行I/O操作了,获取其ready键集合 Set readyKeys = acceptSelector.selectedKeys(); Iterator i = readyKeys.iterator(); // 遍历ready键集合,并处理加法请求 while (i.hasNext()) { SelectionKey sk = (SelectionKey)i.next(); i.remove(); ServerSocketChannel nextReady = (ServerSocketChannel)sk.channel(); // 接受加法请求并处理它 _clientSocket = nextReady.accept().socket(); processRequest(); _clientSocket.close(); } } }
发表评论
-
判断两二叉树相等
2014-05-29 00:13 1083bool IsBSTEqual(BNode* root1, ... -
数据去重
2014-05-29 00:11 758引用 使用数据结构丰富的脚本语言,如Python,利用其中的字 ... -
判断一个整数是否为2的次方幂
2014-05-28 23:56 831/* 判断一个整数是否为2的次方幂 */ bool ... -
实现Comparable接口,进行排序
2014-05-28 23:47 8786import java.util.ArrayList; ... -
Struct2详解
2014-05-21 15:14 873引用 Servlet的缺点: 1、web.xml配置比较多 2 ... -
常用方法
2014-04-25 20:24 732[color=red]String[/color] ... -
Struts2、Spring3、HIbernate4总结
2014-04-16 10:35 822引用 第一部分:Struts2 1 ... -
Finally的使用总结
2014-04-15 22:02 832//清单一: public class ... -
Fibonacci数列的递归与非递归
2014-04-15 20:57 676//非递归实现 //f(1)=1,f(2)=1,f(n) ... -
Java中的异常
2014-04-14 09:42 587引用 Throwable包括两个子类:Error和Except ... -
JVM的垃圾回收机制
2014-04-13 18:39 812引用 堆被划分为新生代和旧生代, 新生代包含Eden 和 S ... -
集合类总结
2014-04-04 15:54 617引用 一. 总的框架 总的有Collection和Map Co ... -
设计模式之策略模式
2014-04-01 17:07 590main ======================= ... -
设计模式之责任链模式
2014-04-01 16:32 702main ======================= ... -
设计模式之访问者模式
2014-03-31 18:57 820main ======================= ... -
设计模式之工厂模式
2014-03-31 11:33 604main ======================= ... -
设计模式之观察者模式
2014-03-30 15:04 819main =============== ... -
Java虚拟机JVM详解
2014-03-29 12:13 1211引用 第一部分:JVM基本结构 1.什么是JVM 一个java ... -
Java中的反射机制Reflect
2014-03-28 17:17 3558package com.reflect.sym; i ... -
IOC原理详解
2014-03-28 09:54 996引用 1.当不用IOC时: 在采用面向对象方法设计的软件系统中 ...
相关推荐
Java NIO与IO性能对比分析.pdf
Java NIO与IO的区别和比较,
Java NIO IO 区别 比较 doc
Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道...Java NIO系列教程(十二) Java NIO与IO
JavaNIO浅析IO模型Java开发Java经验技巧共10页.pdf.zip
JavaNIO与IO的区别和比较.pdf
Ioserver java Nio socket 框架 是个不错的NIO 通讯框架,本来想学习mina框架,看了看mina的源码太头痛,本人觉得看懂了Ioserver 再看mina的框架,想多的学习 java NIO 的也可以下载 看看,很值得学习啊!!!
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...
java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...
Java NIO与IO性能对比分析
NULL 博文链接:https://ilrxx.iteye.com/blog/1051402
讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用
java频道\java io 与java nio区别.txt
Java_NIO与IO的区别和比较
JavaNIO传统IO 以网络应用为例.pdf
1、Java IO_NIO 2、Java+IO.pdf
Java.nio 与Java.io比较
java NIO技巧及原理解析,java IO原理,NIO框架分析,性能比较
Java IO NIO and NIO 2 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn...