NIO编程
来源:互联网 发布:放置江湖网络错误 编辑:程序博客网 时间:2024/06/06 19:21
1.NIO弥补了原来同步阻塞I/O的不足,它在标准java代码中提供了高速的,面向块的I/O
2.NIO的几个特性
缓冲区(buffer),通道(channel);多路复用器(selector)
3.一个多路复用器selector可以同时轮循多个Channel,JDK使用epoll()代替传统的select实现,因此它并没有最大连接句柄1024的限制,这个意味着只需要一个线程负责selector的轮询。就可以接入成千上完的客户端
以下是个netty权威指南中NIO实现通信的一个例子
1.服务端的serverTime
package com.afan.nio;public class TimeServer { public static void main(String[] args){ int port = 8080; if(args != null && args.length > 0){ try { port = Integer.valueOf(args[0]); } catch (Exception e) { // TODO: handle exception } } MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port); new Thread(timeServer,"NIO-MultiplexTimeServer-001").start(); }}
2.服务端处理类
package com.afan.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.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Date;import java.util.Iterator;import java.util.Set;public class MultiplexerTimeServer implements Runnable{ private Selector selector; private ServerSocketChannel servChannel; private volatile boolean stop; /** * 初始化多路复用器,绑定监听端口 */ public MultiplexerTimeServer(int port) { try { selector = Selector.open(); servChannel = ServerSocketChannel.open(); servChannel.configureBlocking(false); servChannel.socket().bind(new InetSocketAddress(port)); servChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("the time server is start in port : "+ port); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } public void stop(){ this.stop = true; } @Override public void run() { while(!stop){ try { selector.select(1000); Set<SelectionKey> selectkeys = selector.selectedKeys(); Iterator<SelectionKey> it = selectkeys.iterator(); SelectionKey key = null; while(it.hasNext()){ key = it.next(); it.remove(); try { handleInput(key); } catch (Exception e) { if(key != null){ key.cancel(); if(key.channel() != null){ key.channel().close(); } } } } } catch (Exception e) { e.printStackTrace(); } } //多路复用器关闭后,所有注册在上面的channel和pipe等资源都会被自动 //去注册,所有不需要重复释放资源 if(selector != null){ try { selector.close(); } catch (Exception e) { e.printStackTrace(); } } } private void handleInput(SelectionKey key) throws IOException{ if(key.isValid()){ //处理新接入的请求消息 if(key.isAcceptable()){ //accept the new connection ServerSocketChannel ssc = (ServerSocketChannel)key.channel(); SocketChannel sc = ssc.accept(); sc.configureBlocking(false); //add the new connection to the selector sc.register(selector,SelectionKey.OP_READ); } if(key.isReadable()){ //read the data SocketChannel sc = (SocketChannel)key.channel(); ByteBuffer readBuffer = ByteBuffer.allocate(1024); int readBytes = sc.read(readBuffer); if(readBytes > 0){ readBuffer.flip(); byte[] bytes = new byte[readBuffer.remaining()]; readBuffer.get(bytes); String body = new String(bytes,"UTF-8"); System.out.println("the time server receive order :" + body); String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ?new Date(System.currentTimeMillis()).toString():"BAD ORDER"; doWrite(sc,currentTime); }else if(readBytes < 0){ //对端链路关闭 key.cancel(); sc.close(); } } } } private void doWrite(SocketChannel channel,String response) throws IOException{ if(response != null && response.trim().length() > 0){ byte[] bytes = response.getBytes(); ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); writeBuffer.put(bytes); writeBuffer.flip(); channel.write(writeBuffer); } }}
3.客户端接入的clientITme
package com.afan.nio;public class TimeClient { public static void main(String[] args){ int port = 8080; if(args != null && args.length > 0){ try{ port = Integer.valueOf(args[0]); }catch(Exception e){ } } new Thread(new TimeClientHandle("127.0.0.1",port),"TimeClient-001").start(); }}
4.客户端处理类handle
package com.afan.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.Iterator;import java.util.Set;public class TimeClientHandle implements Runnable{ private String host; private int port; private Selector selector; private SocketChannel socketChannel; private volatile boolean stop; public TimeClientHandle(String host,int port) { this.host = host == null ? "127.0.0.1" : host; this.port = port; try { selector = Selector.open(); socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } @Override public void run() { try { doConnect(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } while(!stop){ try { selector.select(1000); Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> it = selectedKeys.iterator(); SelectionKey key = null; while(it.hasNext()){ key = it.next(); it.remove(); try { handleInput(key); } catch (Exception e) { if(key != null){ key.cancel(); if(key.channel() != null){ key.channel().close(); } } } } } catch (Exception e) { e.printStackTrace(); System.exit(1); } } //多路复用器关闭后,所有注册在上面的channel和pipe等资源 //都会被自动去注册并关闭,所不需要重复释放资源 if(selector != null){ try { selector.close(); } catch (Exception e) { e.printStackTrace(); } } } private void handleInput(SelectionKey key) throws IOException{ if(key.isValid()){ //判断是否连接成功 SocketChannel sc = (SocketChannel)key.channel(); if(key.isConnectable()){ if(sc.finishConnect()){ sc.register(selector, SelectionKey.OP_READ); doWrite(sc); }else{ System.exit(1); } } if(key.isReadable()){ ByteBuffer readBuffer = ByteBuffer.allocate(1024); int readBytes = sc.read(readBuffer); if(readBytes > 0){ readBuffer.flip(); byte[] bytes = new byte[readBuffer.remaining()]; readBuffer.get(bytes); String body = new String(bytes,"UTF-8"); System.out.println("NOW IS" + body); this.stop = true; }else if(readBytes < 0){ //对端链路关闭 key.cancel(); sc.close(); } } } } private void doConnect() throws IOException{ //如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答 if(socketChannel.connect(new InetSocketAddress(port))){ socketChannel.register(selector,SelectionKey.OP_READ); doWrite(socketChannel); }else{ socketChannel.register(selector,SelectionKey.OP_CONNECT); } } private void doWrite(SocketChannel sc) throws IOException{ byte[] req = "QUERY TIME ORDER".getBytes(); ByteBuffer writeBuffer = ByteBuffer.allocate(req.length); writeBuffer.put(req); writeBuffer.flip(); sc.write(writeBuffer); if(!writeBuffer.hasRemaining()){ System.out.println("Send order 2 server succeed."); } }}
总结:
NIO编程的优点: 1.客户端发起的连接操作时异步的,可以通过在多路复用器注册OP_CONNECT等待后续结果,不需要像以前的客户端那样被同步阻塞 2.SocketChannel的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,这样I/O通信线程就可以处理其他的链路,不需要同步等待这个链路可用 3.线程模型的优化(也就是使用了epoll)
阅读全文
0 0
- NIO编程
- NIO编程
- NIO编程
- NIO编程
- Java NIO TCP编程
- Java NIO编程关注点
- NIO的Socket编程
- java nio编程细节
- nio高并发编程
- Java 网络编程nio
- Java NIO 网络编程
- Java NIO编程
- Java高级编程-NIO
- nio高并发编程
- Java NIO 编程总结
- java NIO 网络编程
- NIO编程 TimeServer && TimeClient
- Java NIO网络编程
- adb命令--之查看进程及Kill进程
- crontab快速入门
- switch能否作用在char上,能否作用在byte上,能否作用在string上?
- Nodejs指西 Event Loop Task queue
- Java:学生成绩统计(while语句)
- NIO编程
- 洛谷P2668 斗地主(NOIp2015)(BZOJ4325)
- 校内联考——Contest First 题解
- 多元高斯分布
- PAT乙级1005. 继续(3n+1)猜想 (25)
- table表格行号
- 吝啬的国度
- 动物声音模拟器
- 八(1) 3 -顺序串算法