我的NIO学习笔记
来源:互联网 发布:三明治板的刚度矩阵 编辑:程序博客网 时间:2024/05/22 04:54
一、文章来由
研究Nio也有几天了,在网上看了很多文章,给人整体的感觉就是,一个原本简简单单的东西,被说的好复杂。或者是类似 http://ifeve.com/selectors/ 这种百科全书式的教你如何用接口,这种文章看似介绍了每个函数,面面俱到,却很难串起来。
但是人学东西本来就是一个感性认识到理性认识的过程,如果看到的都是单点,怎么在脑海中形成一张图?
(1)是什么(功能)
(2)为什么这么设计(应用场景)
(3)怎么实现的(具体代码)
这三个问号搞完了以后,才是如何去使用,不能本末倒置。
另外最近实习,需要学习很多东西,文章大都是网上质量高一些的文章的转载,原创文章就相对少一些~~
二、NIO设计原理
一个IO操作分成了两个步骤:①发起IO请求 ②实际的IO操作
(1)阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。
(2)同步IO和异步IO的区别就在于第二个步骤是否阻塞
传统的 IO 称为 BIO,是一种同步阻塞 IO 。要知道 IO 都是交给操作系统完成的,系统在 IO 的时候,如果当前线程等待在那里,效率是很低的。在网络编程里,传统的socket也是 BIO,一个连接一个线程。
于是 NIO 出现了,它是一种同步非阻塞 IO,其实说白了,就是用一个线程不断轮询,去看有没有事件去处理…
服务员不断询问顾客有没有什么要求,然后同步去处理~~
用户自己实现的一个while循环,while(selector.select() > 0)
,通过select阻塞的轮询,来看是否有事件处理。如果有处理就完了。
道理就这么简单。
三、selector
selector就是那个轮询的处理器,它采用register的模式,要被selector轮询,一定要登记。就像你去餐馆吃东西,一定要点菜一样。
selector可以监听四种不同类型的事件:
Connect
Accept
Read
Write
这四种事件用SelectionKey的四个常量来表示:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
对应成代码就是:
Selector selector = Selector.open();channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectionKey.OP_READ);while(true) { int readyChannels = selector.select(); if(readyChannels == 0) continue; Set selectedKeys = selector.selectedKeys(); Iterator keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // a connection was accepted by a ServerSocketChannel. } else if (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } keyIterator.remove(); }}
很明显的是:
但是客户端注册selector的时候,不可能有serverChannel的出现了,所以客户端不可能有isAcceptable事件,就像服务端不可能有isConnectable一样
当连接建立后两段都需要获得socketchannel来通信 ,跟socket一样,事实上,如果不用selector的话,跟socket就是一毛一样的,socketChannel底层也就是socket。如下:
/////NioServer.javapackage nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;/** * Created by hupo.wh on 2016/7/16. */public class NioServer { public static void main(String args[]) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); //非阻塞 //字符序列和字节序列的编码和解码 Charset charset = Charset.forName("UTF-8"); ByteBuffer buf = ByteBuffer.allocate(48); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); String mesg = ""; if(socketChannel != null){ //do something with socketChannel... System.out.println("Connection has been built!!!"); while (socketChannel.read(buf) > 0) { buf.flip(); mesg += charset.decode(buf); } System.out.println(mesg); } } }}
/////NioClient.javapackage nio;import java.io.IOException;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;/** * Created by hupo.wh on 2016/7/15. */public class NioClient { public static void main(String args[]) throws IOException { SocketChannel sc = SocketChannel.open(); sc.connect(new InetSocketAddress("127.0.0.1",8888)); sc.configureBlocking(false); String newData = "Hello Wanghu..." + System.currentTimeMillis(); ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put(newData.getBytes()); buf.flip(); while(buf.hasRemaining()) { sc.write(buf); } sc.close(); }}
四、例程
server例程一:
package nio;import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;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 hupo.wh on 2016/7/16. */public class App2 { public static void main(String[] args) throws IOException { int port = 30; ServerSocketChannel serverChannel = ServerSocketChannel.open( ); ServerSocket serverSocket = serverChannel.socket( ); Selector selector = Selector.open( ); serverSocket.bind (new InetSocketAddress(port)); serverChannel.configureBlocking (false); serverChannel.register (selector, SelectionKey.OP_ACCEPT); while (true) { int n = selector.select( ); if (n == 0) { continue; // nothing to do } Iterator it = selector.selectedKeys().iterator( ); while (it.hasNext( )) { SelectionKey key = (SelectionKey) it.next( ); if (key.isAcceptable( )) { ServerSocketChannel server = (ServerSocketChannel) key.channel( ); SocketChannel channel = server.accept( ); if (channel == null) { ;//handle code, could happen } channel.configureBlocking (false); channel.register (selector, SelectionKey.OP_READ); } if (key.isReadable( )) { //readDataFromSocket (key); } it.remove( ); } } }}
Server例程二:
package nio;/** * Created by hupo.wh on 2016/7/16. */import java.io.IOException;import java.net.InetAddress;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.nio.channels.spi.SelectorProvider;import java.nio.charset.Charset;import java.util.Set;/** * Created by wwh on 15-7-25. */public class NioSocket { //字符序列和字节序列的编码和解码 private Charset charset = Charset.forName("UTF-8"); void run(String ip, int port) throws IOException { try { //创建服务端套接字 ServerSocketChannel server = ServerSocketChannel.open(); //绑定ip和端口 server.socket().bind(new InetSocketAddress(ip, port)); //设置非阻塞 server.configureBlocking(false); //创建selector事件选择器 Selector selector = Selector.open(); //将自己的监听套接字注册到selector上,监听 accept事件 //SelectionKey代表SelectableChannel和Selector的关系,Selectable是Selector可监听的事件channel. server.register(selector, SelectionKey.OP_ACCEPT); while(selector.select() > 0){ //selector.select()返回事件 for(SelectionKey sk : selector.selectedKeys()) { //从事件集合中删除正要处理的事件 selector.selectedKeys().remove(sk); //判断事件的类型,依次处理 if(sk.isAcceptable()){ //如果事件为接受连接accpet事件 System.out.println("accpet 事件"); //调用accept接受请求连接 SocketChannel client = server.accept(); //设置为非阻塞 client.configureBlocking(false); //向selector上注册读事件 client.register(selector, SelectionKey.OP_READ); //将sk对应的channel设置为准备接受其他请求 sk.interestOps(SelectionKey.OP_ACCEPT); } if(sk.isReadable()){ //如果事件为可读事件 System.out.println("read 事件"); SocketChannel client = (SocketChannel)sk.channel(); //定义缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); String mesg = ""; try { while (client.read(buffer) > 0) { buffer.flip(); mesg += charset.decode(buffer); } System.out.println("收到:" + mesg); sk.interestOps(SelectionKey.OP_READ); }catch (IOException e){ //如果出现异常,则取消当前的client连接 sk.cancel(); if(sk.channel() != null){ sk.channel().close(); } } //回复给发来消息的client client.write(charset.encode(mesg)); System.out.println("回复:" + mesg); } } } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { NioSocket Server = new NioSocket(); Server.run("localhost", 10000); }}
- 我的NIO学习笔记
- JavaSE初学笔记之<nio的学习>
- Java Nio学习笔记
- NIO学习笔记
- NIO学习笔记(一)
- NIO学习笔记1
- Java NIO学习笔记
- [网络]NIO学习笔记
- java NIO 学习笔记
- NIO学习笔记一
- NIO学习笔记二
- NIO学习笔记三
- NIO学习笔记四
- NIO学习笔记五
- Java NIO学习笔记
- NIO学习笔记
- NIO学习笔记
- java nio学习笔记
- Android CrashHandler编写自己的异常捕获类
- 一步一步学习Unix编程[2]——迭代服务器
- CodeForces 691D Swaps in Permutation (并查集 + 双向链表)
- hdu5699 货物运输(二分)
- hdu 2844 coins DP多重背包
- 我的NIO学习笔记
- 用正则表达式检验输入框内容是否合法
- 【JVM】(一):Java内存模型
- 【COCI2012 Task 5】T6 poplocavanje ([JZOJ3172]贴瓷砖)(AC自动机模板)
- thinkphp的model三大自动功能
- WebViewClient与常用事件监听
- 前端开发人员必须了解的七大技能图谱
- Centos6服务器部署JavaWeb项目(tomcat6+jdk6)
- 设计模式之装饰模式