赞
踩
学习NIO之前我们先看下面几个知识点:
1.阻塞和非阻塞:阻塞和非阻塞是进程在访问数据的时候,数据内是否准备就绪的一种处理方式,
当数据没有准备的时候:
阻塞:往往需要等待缓冲区中的数据准备好过后才处理其他的事情,否则一直等待在那里。
非阻塞:当我们的进程访问我们的数据缓冲区的时候,数据没有准备好的时候直接返回,
不需要等待,数据有的时候,也直接返回。
2.同步和异步的方式
同步和异步都是基于应用程序和操作系统处理IO时间锁采用的方式,比如同步应用程序直接参与IO读写的操作。
2.1异步:所有的IO读写交给操作系统去处理同步的方式在处理IO事件的时候,
必须阻塞在某个方法上面等待我们的IO时间完成(阻塞IO事件或则通过轮询IO时间的方式)
对于异步来说,所有的IO读写都交给了操作系统,这个时候,我们可以去其他的事情,并不
需要去完成真正的IO操作,当操作完成IO后给我们的应用程序一个通知就可以。
2.2同步:1.阻塞到IO事件,阻塞到read或则write。这个时候我们就不能完全做自己的事情。
让读写方法加入到线程里面,然后阻塞线程来实现,对线程的性能开销比较大。
3.IO事件的轮询,多路复用技术(select模式)
3.1读写事件交给一个单独线程来处理,这个完成IO事件的注册功能,还有就是不断的去轮询。
我们的读写缓冲区,看是否有数据准备好。通知我们相应的读写线程。这样的话,以前的读写线程就可以
做其他的事情,这个时候阻塞的不是所有的IO线程。阻塞的select这个线程。
Cilent ----- ----> Select管家 ---- ------> Server
解释:当客人来的时候,就给管家说,我来了,管家得到这个注册信息后,给Server说,我这里
有一个或则多个客人,Server你去给某人A这件东西,给另外人B这样东西,这个时候客人是可以
去做自己的事情,比如看看花园等等,当管家知道Server给他任务后,他就去找对应的某人,告诉他
Server给他某样东西。(根据我们的注册信息)
3.2Java IO模型
BIO :jdk1.4以前我们使用都是BIO,阻塞IO。阻塞到我们的读写方法,阻塞到线程上提供线程
对于线程的开销本来就是性能的浪费。
NIO:jdk1.4 Linux多路复用技术(selet模式),实现IO事件的轮询方式 :同步非阻塞的模式
对于这种方式目前是主流的网络通信模式。
Mina,netty mina2.0 netty5.0–网络通信框架。比我们直接写NIO要容易些,并且代码可读性更好。
AIO:jdk1.7后(NIO2)才是实现真正的异步Aio,学习linux epoll模式
Aio使用的比较少。
小结: 1.BIO阻塞的IO
2.NIO select+非阻塞 实现同步非阻塞
3.AIOA 异步非阻塞IO
4.NIO AIO 原理的解读
对于网络通信而言NIO,AIO并没有改变网络通信的基本步骤,只是在其原来的基础上(serverscoket,socket)做了一个改进。
3.3Socket <–建立连接需要三次握手–> ServerSocket
对于三次握手的方式,建立稳定的连接性能开销比较大,解决方案从思想上来说比较容易
就是减少连接的次数,对我们的读写通信管道进行一个抽象。
对于读和写采用抽象的管道的概念:
3.4Channel:是一个在TCP连接之间抽象,一个TCP连接可以对应多个管道,而不是以前的方式只有
一个通信信道,减少了TCP连接的次数。
UDP:采用相同方式,也是抽象成管道。
3.5NIO模型原理:
通过我们的selctor(选择器)就相对于一个管家,管理所有的IO事件,
connection accept 客户端和服务端的读写。–IO事件都交给我们的selctor管理
selctor(选择器)如何进行管理IO事件
当IO事件注册给我们的选择器的时候,选择器会给他们分配一个ket(可以简单的理解成一个事件的标签)
当我们的IO事件完成后悔通过key值来找到相应的管道,然后通过管道发送数据和接受数据等操作。
对于读和写数据的缓冲区:
通过byteBuffer类,提供很多读写的方法:put() get()方法
服务端:ServerSocketChannel
客户端:SocketChanner
选择器:Selector selector=Select.open();这样我们就打开了我们的选择器
4.Selectionkey:可以通过它来判断IO事件是否已经就绪。
key.Accptable:是否可以接受客户端的连接
key.connctionable:是否可以连接服务端
key.isreadable():缓冲区是否可读
key.iswriteable:缓冲区是否可写
5.如何获得事件keys值
Selecionkey keys=selector.selectedkeys();
6.如何注册
channel.regist(Selctor,Selectionkey_OP_Write);
channel.regist(Selctor,Selectionkey_OP_Read);
7.AIO
服务端:AsynchronousServerSocketChannel
客户端:AsynchronousSocketChannel
用户处理器:CompletionHandler接口,这个接口实现应用程序向操作系统发起IO请求
当完成后去处理具体的逻辑,否则做自己该做的事情。
下面来看一下应用:
服务端程序代码:
package com.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; 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.util.Set; import java.util.Iterator; public class NIOServer { private int flag=1; //缓冲区大小 private int blockSize=4096; //发送数据缓冲区 private ByteBuffer sendbuffer=ByteBuffer.allocate(blockSize); //接受数据缓冲区 private ByteBuffer receivebuffer=ByteBuffer.allocate(blockSize); //选择器 private Selector selector; //初始化服务端对象 public NIOServer(int port) throws IOException { //ServerSocketChannel是一个可以监听新进来的TCP连接的通道,就像标准IO中的ServerSocket一样。 ServerSocketChannel serverSocketChannel=ServerSocketChannel.open(); //设置是否 为非阻塞 serverSocketChannel.configureBlocking(false); //获取Socket ServerSocket serverSocket=serverSocketChannel.socket(); //绑定ip和端口 serverSocket.bind(new InetSocketAddress(port)); //打开选择器 selector=Selector.open(); //注册 打开与接受客户端的连接 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server start->"+port); } //监听客服端连接 public void listen() throws IOException { while(true) { //轮询选择器 selector.select(); //遍历选择器 返回一个Set集合 Set<SelectionKey> selectedKeys=selector.selectedKeys(); Iterator<SelectionKey> itetor=selectedKeys.iterator(); while(itetor.hasNext()) { SelectionKey selectionKey=itetor.next(); itetor.remove(); //业务逻辑 handleKey(selectionKey); } } } public void handleKey(SelectionKey selectionKey) throws IOException { //服务端 ServerSocketChannel server=null; //客户端 SocketChannel client=null; String reciveText; String sendText; int count=0; //是否与客服端连接 if(selectionKey.isAcceptable()) { server=(ServerSocketChannel)selectionKey.channel(); client=server.accept(); client.configureBlocking(false); //读事件 client.register(selector,SelectionKey.OP_READ); }else if(selectionKey.isReadable()) { client=(SocketChannel) selectionKey.channel(); //内容读到缓冲区 receivebuffer.clear(); count=client.read(receivebuffer); if(count>0) { //从缓存中取出内容体 reciveText=new String(receivebuffer.array(),0,count); System.out.println("服务端接受到客服端的信息:"+reciveText); //注册 写事件 client.register(selector,SelectionKey.OP_WRITE); } selectionKey.cancel(); }else if(selectionKey.isWritable()) { //清除缓冲区 sendbuffer.clear(); client=(SocketChannel) selectionKey.channel(); sendText="msg send to client"+flag++; sendbuffer.put(sendText.getBytes()); //flip()使缓冲区为一系列新的通道写入或相对获取 操作做好准备 sendbuffer.flip(); //发送数据 client.write(sendbuffer); System.out.println("服务端发送数据给客服端:"+sendText); } } public static void main(String[] args) throws IOException { int port=6005; NIOServer server=new NIOServer(port); server.listen(); } }
客户端代码:
package com.nio; 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.util.Set; import java.util.Iterator; public class NIOClient { private static int flag=1; //缓冲区大小 private static int blockSize=4096; //发送数据缓冲区 private static ByteBuffer sendbuffer=ByteBuffer.allocate(blockSize); //接受数据缓冲区 private static ByteBuffer receivebuffer=ByteBuffer.allocate(blockSize); //设置ip和地址 private final static InetSocketAddress serverAddress=new InetSocketAddress("127.0.0.1", 6005); public static void main(String[] args) throws IOException { //打开服务端Channel SocketChannel socketChannel=SocketChannel.open(); //设置为非阻塞 socketChannel.configureBlocking(false); //连接这个通道的插座 socketChannel.connect(serverAddress); //打开选择器 Selector selector=Selector.open(); //把channel注册到选择器上 是否与服务端连接 socketChannel.register(selector, SelectionKey.OP_CONNECT); Set<SelectionKey> selectionKeys; Iterator<SelectionKey> iterator; SelectionKey selectionKey = null; SocketChannel client = null; int count=0; //接受数据 String receiveTest; //发送数据 String sendText; while(true) { // selector.select(); selectionKeys=selector.selectedKeys(); iterator=selectionKeys.iterator(); while(iterator.hasNext()) { selectionKey=iterator.next(); //是否可以连接服务器 if(selectionKey.isConnectable()) { System.out.println("clent connet"); client=(SocketChannel) selectionKey.channel(); System.out.println("starting 。。。。。"); //告诉是否该通道的连接操作正在进行中。 if(client.finishConnect()) { // //完成了一个套接字通道的连接的过程。 // client.finishConnect(); System.out.println("客服端完成连接操作"); //清空缓存 sendbuffer.clear(); //将内容写到缓存 sendbuffer.put("Hello ,Server".getBytes()); //flip()使缓冲区为一系列新的通道写入或相对获取 操作做好准备 sendbuffer.flip(); //从给定的缓冲区写入该通道的一个字节序列。 client.write(sendbuffer); client.register(selector, SelectionKey.OP_READ); } // } if(selectionKey.isWritable()) { sendbuffer.clear(); client=(SocketChannel) selectionKey.channel(); sendText="Msg send to Server"+flag++; sendbuffer.put(sendText.getBytes()); sendbuffer.flip(); client.write(sendbuffer); System.out.println("客服端发送数据给服务端"+sendText); client.register(selector, SelectionKey.OP_READ); } if(selectionKey.isReadable()) { client=(SocketChannel) selectionKey.channel(); receivebuffer.clear(); client.read(receivebuffer); count=client.read(receivebuffer); if(count>0) { receiveTest=new String(receivebuffer.array(), 0, count); System.out.println("客户端接受到服务端的数据"+receiveTest); } client.register(selector,SelectionKey.OP_WRITE); } selectionKeys.clear(); //iterator.remove(); } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。