通过简单的Demo开发,熟悉传统的同步阻塞I/O(BIO)、伪异步I/O、非阻塞I/O(Nio)以及异步I/O(AIO)的差异。
同步阻塞I/O(BIO)
BIO通信模型(一客户端一线程)
代码分析
- TimerServer
package com.xpn.netty.bio;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TimerServer {
/**
* @param args
*/
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
Socket socket = null;
System.out.println("the server start at port: " + port);
while (true) {
socket = server.accept();
new Thread(new
TimeServerHandler(socket)).start();//每一个请求都启动一个线程
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
server = null;
}
}
}
}
- TimerClient
package com.xpn.netty.bio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TimeClient {
/**
* @param args
*/
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println("Query time order");// 发送数据
String resp = in.readLine();// 接受数据
System.out.println("Now is: " + resp);
} catch (Exception e) {
// TODO: handle exception
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
in = null;
}
if (out != null) {
out.close();
out = null;
}
if (socket != null) {
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
socket = null;
}
}
}
}
- TimeServerHandler
package com.xpn.netty.bio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
public class TimeServerHandler implements Runnable {
private Socket socket;
public TimeServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(
this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String currentTime = null;
String body = null;
while (true) {
body = in.readLine();// 接受数据
if (body == null)
break;
System.out.println("The time server receiver order : " + body);
currentTime = "Query time order".equalsIgnoreCase(body) ? new Date(
System.currentTimeMillis()).toString() : "Bad order";
out.println(currentTime);// 发送数据
}
} catch (Exception e) {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
in = null;
}
if (out != null) {
out.close();
out = null;
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
伪异步I/O
伪异步I/O模型图
在BIO的基础上增加了线程池,从而实现伪异步I/O
代码分析
- TimerServer
package com.xpn.netty.bio;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TimerServer {
/**
* @param args
*/
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
Socket socket = null;
System.out.println("the server start at port: " + port);
int queueSize = 50;
int maxPoolSize = 10000;
// 创建I/O任务线程池
TimeServerHandlerExcutePool singleExcutePool = new TimeServerHandlerExcutePool(
maxPoolSize, queueSize);
while (true) {
socket = server.accept();
/*new Thread(new
TimeServerHandler(socket)).start();//每一个请求都启动一个线程
*/ singleExcutePool.execute(new Thread(new TimeServerHandler(
socket)));
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
server = null;
}
}
}
}
- TimerClient(同BIO)
- TimerServerHandler(同BIO)
- TimerServerHandlerExcutePool
package com.xpn.netty.bio;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//伪异步IO
public class TimeServerHandlerExcutePool {
private ExecutorService executor;
public TimeServerHandlerExcutePool(int maxPoolSize, int queueSize) {
executor = new ThreadPoolExecutor(Runtime.getRuntime()
.availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(queueSize));
}
public void execute(Runnable task) {
executor.execute(task);
}
}
非阻塞I/O(NIO)
NIO服务端序列图
NIO客户端序列图
代码分析
- TimerServer
package com.xpn.netty.nio;
public class TimerServer {
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port);
new Thread(timeServer, "NIO-MultiplexerTimeServer-001").start();
}
}
- TimeClient
package com.xpn.netty.nio;
public class TimeClient {
/**
* @param args
*/
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
new Thread(new TimeClientHandle("127.0.0.1", port)).start();
}
}
- MultiplexerTimeServer
package com.xpn.netty.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;
/**
* 初始化selector、绑定监听端口(对应服务端序列图的1-4步骤)
*
* @param port
*/
public MultiplexerTimeServer(int port) {
try {
//3、创建Selector,启动线程
selector = Selector.open();
//1、打开ServerSocketChannel
servChannel = ServerSocketChannel.open();// 通过静态函数初始化多路复用器和channel
servChannel.configureBlocking(false);// 设置为非阻塞模式
//2、绑定监听地址InetSocketAddress
servChannel.socket().bind(new InetSocketAddress(port), 1024);// 绑定socket端口
//4、将ServerSocketChannel注册到Selector,监听
servChannel.register(selector, SelectionKey.OP_ACCEPT);// 注册channel到多路复用器
System.out.println("the timeServer start at port : " + port);
} catch (Exception e) {
// TODO: handle exception
}
}
public void stop() {
this.stop = true;
}
/**
*
*/
@Override
public void run() {
while (!stop) {
try {
int timeout = 1000;
selector.select(timeout);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
SelectionKey key = null;
//5、Selector轮询就绪的key
while (it.hasNext()) {
key = it.next();
it.remove();
try {
//6、处理新的客户端接入
handleInput(key);
} catch (Exception e) {
if (key != null) {
key.cancel();
if (key.channel() != null) {
key.channel().close();
}
}
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
// 多路复用器关闭后,所有注册在上面的channel和pipeline会被自动注册并关闭,所以不需要重复释放资源
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 6、处理新的客户端接入
* @param key
* @throws IOException
*/
private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
// 处理新接入的请求消息
if (key.isAcceptable()) {
// 接受新连接
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
// 增加一个新的连接到selector
//8、向Selector注册监听读操作
sc.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
// 读数据
//9、异步读取请求消息到ByteBuffer
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);// 将sc的数据读到ByteBuffer中
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);// 将ByteBuffer的数据读到byte[]数组中
String body = new String(bytes, "UTF-8");
System.out
.println("the timeServer receive order : " + body);
String currentTime = "Query time order"
.equalsIgnoreCase(body) ? new Date(
System.currentTimeMillis()).toString()
: "Bad order";
//11、异步写ByteBuffer到SocketChannel
doWrite(sc, currentTime);
} else if (readBytes < 0) {
// 对端链路关闭
key.cancel();
sc.close();
} else {
}
}
}
}
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);// 将byte[]数组写入缓冲区,进而写入到管道channel中
writeBuffer.flip();
channel.write(writeBuffer);
}
}
}
- TimeClientHandle
package com.xpn.netty.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;
/**
* 对应NIO客户端序列图的步骤1、2
* @param host
* @param port
*/
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) {
System.exit(1);
}
}
@Override
public void run() {
try {
//3、异步连接服务器
doConnect();
} catch (Exception e) {
System.exit(1);
}
//
while (!stop) {
try {
int timeout = 1000;
//6、创建Selector,启动线程
selector.select(timeout);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
SelectionKey key = null;
//7、Selector轮询就绪的key
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) {
System.exit(1);
}
}
// 多路复用器关闭后,所有注册在上面的channel和pipeline会被自动注册并关闭,所以不需要重复释放资源
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
// 判断是否连接成功
SocketChannel sc = (SocketChannel) key.channel();
//9、判断连接是否完成
if (key.isConnectable()) {
if (sc.finishConnect()) {
//10、连接完成注册读事件
sc.register(selector, SelectionKey.OP_READ);
doWrite(sc);
} else {
System.exit(1);
}
}
if (key.isReadable()) {
//11、 读消息
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);// 将sc的数据读到ByteBuffer中
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);// 将ByteBuffer的数据读到byte[]数组中
String body = new String(bytes, "UTF-8");
System.out.println("Now is : " + body);
this.stop = true;
} else if (readBytes < 0) {
key.cancel();
sc.close();
} else {
;
}
}
}
}
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.");
}
}
private void doConnect() throws IOException {
// 如果连接成功,则注册到多路复用器上,发送请求消息,读应答
//4、如果连接成功,
if (socketChannel.connect(new InetSocketAddress(host, port))) {
socketChannel.register(selector, SelectionKey.OP_READ);
doWrite(socketChannel);
} else {
//5、向Reactor线程的Selector注册连接事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
}
}
异步I/O(AIO)
代码分析
- TimerServer
package com.xpn.netty.aio;
public class TimerServer {
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
AsyncTimerServerHandler timeServer = new AsyncTimerServerHandler(port);
new Thread(timeServer, "AIO-AsyncTimerServerHandler-001").start();
}
}
- AsyncTimeClient
package com.xpn.netty.aio;
public class AsyncTimeClient {
/**
* @param args
*/
public static void main(String[] args) {
int port = 8080;
if (args != null && args.length > 0) {
port = Integer.parseInt(args[0]);
}
new Thread(new AsyncTimeClientHandler("127.0.0.1", port),
"AIO-AsyncTimeClientHandler-001").start();
}
}
- AsyncTimerServerHandler
package com.xpn.netty.aio;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.CountDownLatch;
public class AsyncTimerServerHandler implements Runnable {
private int port;
CountDownLatch latch;
AsynchronousServerSocketChannel asynchronousServerSocketChannel;
public AsyncTimerServerHandler(int port) {
this.port = port;
try {
asynchronousServerSocketChannel = AsynchronousServerSocketChannel
.open();
asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
System.out.println("the time server start at port : " + port);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
latch = new CountDownLatch(1);
doAccept();
try {
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
private void doAccept() {
asynchronousServerSocketChannel.accept(this,
new AcceptCompletionHandler());
}
}
- AsyncTimeClientHandler
package com.xpn.netty.aio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;
public class AsyncTimeClientHandler implements
CompletionHandler<Void, AsyncTimeClientHandler>, Runnable {
private AsynchronousSocketChannel client;
private String host;
private int port;
private CountDownLatch latch;
public AsyncTimeClientHandler(String host, int port) {
this.host = host;
this.port = port;
try {
client = AsynchronousSocketChannel.open();
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void run() {
latch = new CountDownLatch(1);
client.connect(new InetSocketAddress(host, port), this, this);
try {
latch.await();
} catch (Exception e) {
// TODO: handle exception
}
try {
client.close();
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void completed(Void result, AsyncTimeClientHandler attachment) {
byte[] req = "query time order".getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
client.write(writeBuffer, writeBuffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
if (buffer.hasRemaining()) {
client.write(buffer, buffer, this);
} else {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
client.read(
readBuffer,
readBuffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result,
ByteBuffer buffer) {
buffer.flip();
byte[] bytes = new byte[buffer
.remaining()];
buffer.get(bytes);
String body;
try {
body = new String(bytes,
"UTF-8");
System.out.println("Now is : "
+ body);
latch.countDown();
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void failed(Throwable exc,
ByteBuffer attachment) {
try {
client.close();
} catch (Exception e) {
// TODO: handle exception
}
}
});
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
try {
client.close();
} catch (Exception e) {
// TODO: handle exception
}
}
});
}
@Override
public void failed(Throwable exc, AsyncTimeClientHandler attachment) {
exc.printStackTrace();
try {
client.close();
latch.countDown();
} catch (Exception e) {
// TODO: handle exception
}
}
}
- AcceptCompletionHandler
package com.xpn.netty.aio;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AcceptCompletionHandler implements
CompletionHandler<AsynchronousSocketChannel, AsyncTimerServerHandler> {
@Override
public void completed(AsynchronousSocketChannel result,
AsyncTimerServerHandler attachment) {
attachment.asynchronousServerSocketChannel.accept(attachment, this);
ByteBuffer buffer = ByteBuffer.allocate(1024);
result.read(buffer, buffer, new ReadCompletionHandler(result));
}
@Override
public void failed(Throwable exc, AsyncTimerServerHandler attachment) {
exc.printStackTrace();
attachment.latch.countDown();
}
}
- ReadCompletionHandler
package com.xpn.netty.aio;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Date;
public class ReadCompletionHandler implements
CompletionHandler<Integer, ByteBuffer> {
private AsynchronousSocketChannel channel;
public ReadCompletionHandler(AsynchronousSocketChannel channel) {
if (this.channel == null)
this.channel = channel;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
byte[] body = new byte[attachment.remaining()];
attachment.get(body);
try {
String req = new String(body, "UTF-8");
System.out.println("the time server receive order : " + req);
String currentTime = "Query time order".equalsIgnoreCase(req) ? new Date(
System.currentTimeMillis()).toString() : "Bad order";
doWrite(currentTime);
} catch (Exception e) {
// TODO: handle exception
}
}
private void doWrite(String currentTime) {
if (currentTime != null && currentTime.trim().length() > 0) {
byte[] bytes = (currentTime).getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
channel.write(writeBuffer, writeBuffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
// 如果没有发送完成,继续发送
if (buffer.hasRemaining()) {
channel.write(buffer, buffer, this);
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
try {
channel.close();
} catch (Exception e) {
// TODO: handle exception
}
}
});
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
try {
this.channel.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}