BIO NIO AIO实例
来源:互联网 发布:北京奥运会姚明数据 编辑:程序博客网 时间:2024/05/24 03:22
本文介绍java网络IO方面的知识,包含BIO、NIO和AIO的使用例子。
1.IO 术语
BIO 同步阻塞IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理。
NIO 同步非阻塞IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。
AIO 异步非阻塞IO,在此种模式下,用户进程只需要发起一个IO操作然后就立即返回,等IO操作真的完成以后,应用程序会得到IO操作完成的通知,此时用户程序对数据进行处理就好了,而不需要进行实际的IO读写操作,因为真正的IO读或写操作已经由内核完成。
上面涉及到两对词语,同步和异步,阻塞和非阻塞。
同步 指用户进程触发IO操作并等待IO操作是否就绪,通常使用轮询方法查看。
异步 指用户进程触发IO操作后就做自己的事情,当IO操作完成时会得到IO操作完成的通知,通常是把回调函数给通知者。
阻塞 指当读写文件操作符时,如果当时数据没有准备好或者不可读写,用户程序就进入等待状态,直到数据准备好或者可以读写为止。
非阻塞 相对于阻塞,当数据没有准备好或者不可读写,立刻返回而不等待。
2.代码示例
为了能更快的看到这些IO具体怎么使用,下面直接show code。
2.1 BIO
1.BIO server
package com.nio.example;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class PlainEchoServer { private static final ExecutorService executorPool = Executors.newFixedThreadPool(5); private static class Handler implements Runnable { private Socket clientSocket; public Handler(Socket clientSocket){ this.clientSocket = clientSocket; } @Override public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true); char chars[] = new char[64]; int len = reader.read(chars); StringBuffer sb = new StringBuffer(); sb.append(new String(chars, 0, len)); System.out.println("From client: " + sb); writer.write(sb.toString()); writer.flush(); } catch (IOException e) { e.printStackTrace(); try { clientSocket.close(); } catch (IOException ex) { // ignore on close } } } } public void serve(int port) throws IOException { final ServerSocket socket = new ServerSocket(port); try { while (true) { long beforeTime = System.nanoTime(); final Socket clientSocket = socket.accept(); System.out.println("Establish connection time: " + (System.nanoTime() - beforeTime) + " ns"); executorPool.execute(new Handler(clientSocket)); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { PlainEchoServer server = new PlainEchoServer(); server.serve(8080); }}
2.BIO client
package com.nio.example;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.Reader;import java.io.Writer;import java.net.Socket;import java.net.UnknownHostException;public class PlainEchoClient { public static void main(String args[]) throws Exception { for (int i = 0; i < 1; i++) {// i,20 startClientThread(i + "name"); } } private static void startClientThread(String name) throws UnknownHostException, IOException { Thread t = new Thread(new Runnable() { @Override public void run() { try { startClient(); } catch (Exception e) { e.printStackTrace(); } } }, name); t.start(); } private static void startClient() throws UnknownHostException, IOException { long beforeTime = System.nanoTime(); String host = "127.0.0.1"; int port = 8080; Socket client = new Socket(host, port); // 建立连接后就可以往服务端写数据了 Writer writer = new OutputStreamWriter(client.getOutputStream()); writer.write("Hello Server. from: " + Thread.currentThread().getName()); writer.flush(); // 写完以后进行读操作 Reader reader = new InputStreamReader(client.getInputStream()); char chars[] = new char[64];// 假设所接收字符不超过64位,just for demo int len = reader.read(chars); StringBuffer sb = new StringBuffer(); sb.append(new String(chars, 0, len)); System.out.println("From server: " + sb); writer.close(); reader.close(); client.close(); System.out.println("Client use time: " + (System.nanoTime() - beforeTime) + " ns"); }}
以上两段代码借用建飞的 bio&nio如何选择一文。
2.2 NIO
1.NIO server
package com.stevex.app.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.ClosedChannelException;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;public class NIOServer { private ByteBuffer readBuffer; private Selector selector; public static void main(String[] args) { NIOServer server = new NIOServer(); server.init(); server.listen(); } private void init() { readBuffer = ByteBuffer.allocate(1024); ServerSocketChannel servSocketChannel; try { servSocketChannel = ServerSocketChannel.open(); servSocketChannel.configureBlocking(false); // 绑定端口 servSocketChannel.socket().bind(new InetSocketAddress(8383)); selector = Selector.open(); servSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } catch (IOException e) { e.printStackTrace(); } } private void listen() { while (true) { try { selector.select(); Iterator<SelectionKey> ite = selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove();// 确保不重复处理 handleKey(key); } } catch (Throwable t) { t.printStackTrace(); } } } private void handleKey(SelectionKey key) throws IOException, ClosedChannelException { SocketChannel channel = null; try { if (key.isAcceptable()) { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); channel = serverChannel.accept();// 接受连接请求 channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { channel = (SocketChannel) key.channel(); readBuffer.clear(); /* * 当客户端channel关闭后,会不断收到read事件,但没有消息,即read方法返回-1 所以这时服务器端也需要关闭channel,避免无限无效的处理 */ int count = channel.read(readBuffer); if (count > 0) { // 一定需要调用flip函数,否则读取错误数据 readBuffer.flip(); /* * 使用CharBuffer配合取出正确的数据 String question = new String(readBuffer.array()); * 可能会出错,因为前面readBuffer.clear();并未真正清理数据 只是重置缓冲区的position, limit, mark, * 而readBuffer.array()会返回整个缓冲区的内容。 decode方法只取readBuffer的position到limit数据。 例如,上一次读取到缓冲区的是"where", * clear后position为0,limit为 1024, 再次读取“bye"到缓冲区后,position为3,limit不变, * flip后position为0,limit为3,前三个字符被覆盖了,但"re"还存在缓冲区中, 所以 new String(readBuffer.array()) 返回 "byere", * 而decode(readBuffer)返回"bye"。 */ CharBuffer charBuffer = CharsetHelper.decode(readBuffer); String question = charBuffer.toString(); String answer = getAnswer(question); channel.write(CharsetHelper.encode(CharBuffer.wrap(answer))); } else { // 这里关闭channel,因为客户端已经关闭channel或者异常了 channel.close(); } } } catch (Throwable t) { t.printStackTrace(); if (channel != null) { channel.close(); } } } private String getAnswer(String question) { String answer = null; switch (question) { case "who": answer = "我是小娜\n"; break; case "what": answer = "我是来帮你解闷的\n"; break; case "where": answer = "我来自外太空\n"; break; case "hi": answer = "hello\n"; break; case "bye": answer = "88\n"; break; default: answer = "请输入 who, 或者what, 或者where"; } return answer; }}
2.NIO client
package com.stevex.app.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Iterator;import java.util.Random;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;public class NIOClient implements Runnable { private BlockingQueue<String> words; private Random random; public static void main(String[] args) { // 种多个线程发起Socket客户端连接请求 for (int i = 0; i < 10; i++) { NIOClient c = new NIOClient(); c.init(); new Thread(c).start(); } } @Override public void run() { SocketChannel channel = null; Selector selector = null; try { channel = SocketChannel.open(); channel.configureBlocking(false); // 请求连接 channel.connect(new InetSocketAddress("localhost", 8383)); selector = Selector.open(); channel.register(selector, SelectionKey.OP_CONNECT); boolean isOver = false; while (!isOver) { selector.select(); Iterator<SelectionKey> ite = selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove(); if (key.isConnectable()) { if (channel.isConnectionPending()) { if (channel.finishConnect()) { // 只有当连接成功后才能注册OP_READ事件 key.interestOps(SelectionKey.OP_READ); channel.write(CharsetHelper.encode(CharBuffer.wrap(getWord()))); sleep(); } else { key.cancel(); } } } else if (key.isReadable()) { ByteBuffer byteBuffer = ByteBuffer.allocate(128); channel.read(byteBuffer); byteBuffer.flip(); CharBuffer charBuffer = CharsetHelper.decode(byteBuffer); String answer = charBuffer.toString(); System.out.println(Thread.currentThread().getId() + "---" + answer); String word = getWord(); if (word != null) { channel.write(CharsetHelper.encode(CharBuffer.wrap(word))); } else { isOver = true; } sleep(); } } } } catch (IOException e) { e.printStackTrace(); } finally { if (channel != null) { try { channel.close(); } catch (IOException e) { e.printStackTrace(); } } if (selector != null) { try { selector.close(); } catch (IOException e) { e.printStackTrace(); } } } } private void init() { words = new ArrayBlockingQueue<String>(5); try { words.put("hi"); words.put("who"); words.put("what"); words.put("where"); words.put("bye"); } catch (InterruptedException e) { e.printStackTrace(); } random = new Random(); } private String getWord() { return words.poll(); } private void sleep() { try { TimeUnit.SECONDS.sleep(random.nextInt(3)); } catch (InterruptedException e) { e.printStackTrace(); } }}
3.charset 编码
package com.stevex.app.nio;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.charset.CharacterCodingException;import java.nio.charset.Charset;import java.nio.charset.CharsetDecoder;import java.nio.charset.CharsetEncoder;public class CharsetHelper { private static final String UTF_8 = "UTF-8"; private static CharsetEncoder encoder = Charset.forName(UTF_8).newEncoder(); private static CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder(); public static ByteBuffer encode(CharBuffer in) throws CharacterCodingException{ return encoder.encode(in); } public static CharBuffer decode(ByteBuffer in) throws CharacterCodingException{ return decoder.decode(in); }}
以上代码来自博文:http://stevex.blog.51cto.com/4300375/1581376/
2.3 AIO
1.AIO server
package com.aio.example;import java.io.IOException;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousServerSocketChannel;import java.nio.channels.AsynchronousSocketChannel;import java.util.concurrent.ExecutionException;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit;import java.util.concurrent.TimeoutException;public class AIOSocketServer { public static final int PORT = 8082; public static final String HOST = "localhost"; public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { new AIOSocketServer(); } public AIOSocketServer() throws IOException, InterruptedException, ExecutionException { // open a server channel and bind to a free address, then accept a connection System.out.println("Open server channel"); SocketAddress address = new InetSocketAddress(HOST, PORT); AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(address); System.out.println("Initiate accept"); Future<AsynchronousSocketChannel> future = server.accept(); // wait for the accept to finish AsynchronousSocketChannel worker = future.get(); System.out.println("Accept completed"); ByteBuffer readBuffer = ByteBuffer.allocate(100); try { // read a message from the client, timeout after 10 seconds worker.read(readBuffer).get(10, TimeUnit.SECONDS); System.out.println("Message received from client: " + new String(readBuffer.array())); // send a message to the client ByteBuffer message = ByteBuffer.wrap("hello client, i am Alice.".getBytes()); worker.write(message); } catch (TimeoutException e) { System.out.println("Client didn't respond in time"); } server.close(); }}
2.AIO client
package com.aio.example;import java.io.IOException;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousSocketChannel;import java.nio.channels.CompletionHandler;import java.util.concurrent.ExecutionException;import java.util.concurrent.Future;/** * * @author macun 2015年11月19日 上午9:56:00 */public class AIOSocketClient { public static final int PORT = 8082; public static final String HOST = "localhost"; public static void main(String[] args) throws IOException { // create a client SocketAddress address = new InetSocketAddress(HOST, PORT); ClientWrapper client = new ClientWrapper(address); // start client thread client.start(); try { client.join(); } catch (InterruptedException e) { System.out.println(e); } client.close(); } public static class ClientWrapper extends Thread { AsynchronousSocketChannel client; Future<Void> connectFuture; public ClientWrapper(SocketAddress server) throws IOException{ // open a new socket channel and connect to the server System.out.println("Open client channel"); client = AsynchronousSocketChannel.open(); System.out.println("Connect to server"); connectFuture = client.connect(server); } public void run() { System.out.println("client run."); // if the connect hasn't happened yet cancel it // if (!connectFuture.isDone()) { // connectFuture.cancel(true); // return; // } try { connectFuture.get(); } catch (InterruptedException e1) { System.out.println("client connect error." + e1); return; } catch (ExecutionException e1) { System.out.println("client connect error." + e1); return; } try { // send a message to the server ByteBuffer message = ByteBuffer.wrap("hello server, i am Bruce.".getBytes()); // wait for the response System.out.println("Sending message to the server..."); Integer countBytes = client.write(message).get(); System.out.println(countBytes); final ByteBuffer readBuffer = ByteBuffer.allocate(100); // Future<Integer> numberBytes = client.read(readBuffer); client.read(readBuffer, null, new CompletionHandler<Integer, Object>() { @Override public void completed(Integer result, Object attachment) { System.out.println("Message received from server: " + new String(readBuffer.array())); clearUp(); } @Override public void failed(Throwable e, Object attachment) { System.err.println("Exception performing write"); e.printStackTrace(); clearUp(); } private void clearUp() { try { client.close(); } catch (IOException e) { System.out.println(e); } } }); } catch (InterruptedException e) { System.out.println(e); } catch (ExecutionException e) { System.out.println(e); } } public void close() throws IOException { client.close(); } }}
以上代码改自:http://www.ibm.com/developerworks/cn/java/j-nio2-1/
3.三种IO适用场景建议
1.BIO:适用于连接数目比较小且固定的架构,对服务器资源要求比较高,并发局限于应用中。jdk1.4以前的唯一选择,程序直观简单。
2.NIO:适用于连接数目较多且连接较短的应用,比如聊天服务器,并发局限于应用中,编程相对复杂,jdk1.4后开始支持,jetty就是采用的NIO。
3.AIO:适用于连接数目较多且连接较长的应用,比如相册服务器,充分调用OS参与并发操作,编程比复杂,jdk1.7开始支持。
4.IO方面推荐文章
1.深入分析java I/O 的工作机制
2.NIO2 入门
- BIO NIO AIO实例
- BIO/AIO/NIO
- BIO AIO NIO
- BIO,NIO,AIO
- BIO、NIO、AIO区别
- java BIO NIO AIO
- BIO NIO AIO
- Java BIO、NIO、AIO
- java BIO NIO AIO
- Java BIO/NIO/AIO
- java bio nio aio
- BIO、NIO、AIO
- java nio aio bio
- java nio bio aio
- JAVA AIO BIO NIO
- Java BIO、NIO、AIO
- Java bio nio aio
- Java BIO、NIO、AIO
- Kernel Methods(PRML第六章)读书笔记
- Android开发-蓝牙-步骤方法
- Java中构造代码块和成员变量初始化的顺序关系
- Spring框架
- svn "cannot set LC_CTYPE locale"
- BIO NIO AIO实例
- 函数
- Zedboard上运行Linaro系统(一):SD卡分区
- 介绍一个个人觉得比较好用VC日志文件输出工具
- xc Delay代码
- 快速对图像的像素进行操作 opencv 实战
- 【Leetcode】121. Best Time to Buy and Sell Stock 【动态规划】
- 杭电-1872 稳定排序(sort+结构体)
- Tempter of the Bone