【Netty权威指南读书笔记】NIO通信模型

  1、缓冲区Buffer

  在NIO库中,所有数据都是用缓冲区(Buffer)处理的。缓冲区实质上是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit、position)等信息。

  2、通道Channel

  Channel是一个通道,可以通过它读取和写入数据。通道与流的不同之处在于通道是双向的,流只是一个方向上移动(一个流必须是InputStream或者OutputStream的子类),而且通道可以同时用于读写。

  3、多路复用器Selector

  多路复用器提供选择已经就绪的任务的能力。简单来讲,Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获得就绪Channel的集合,进行后续的I/O操作。

  介绍完NIO几个核心概念,下面来看看服务端与客户端的通信序列图,并用NIO实现一个EchoServer,以对整个流程有更好的理解。



 

package io.nio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.Iterator;
import java.util.Set;

public class NioEchoServer {

	public static void main(String[] args) {
		MultiplexerEchoServer server = new MultiplexerEchoServer(8080);
		new Thread(server).start();
		new Thread(new Power(server)).start();
	}

	private static class MultiplexerEchoServer implements Runnable {

		private Selector selector;
		private ServerSocketChannel server;
		private volatile boolean running;

		public MultiplexerEchoServer(int port) {
			try {
				running = true;
				selector = Selector.open();
				server = ServerSocketChannel.open();
				server.configureBlocking(false);
				server.socket().bind(new InetSocketAddress(port), 1024);
				server.register(selector, SelectionKey.OP_ACCEPT);
				System.out.println("Server start in:" + port);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
			}
		}

		public void stop() {
			running = false;
		}

		@Override
		public void run() {
			while (running) {
				try {
					selector.select(1000);
					Set<SelectionKey> readyKeys = selector.selectedKeys();
					Iterator<SelectionKey> it = readyKeys.iterator();
					SelectionKey key = null;
					while (it.hasNext()) {
						key = it.next();
						it.remove();
						try {
							handle(key);
						} catch (Exception e) {
							if (key != null) {
								key.cancel();
								if (key.channel() != null) {
									key.channel().close();
								}
							}
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		private void handle(SelectionKey key) throws IOException {
			if (key.isValid()) {
				if (key.isAcceptable()) {
					ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
					SocketChannel sc = ssc.accept();
					sc.configureBlocking(false);
					sc.register(selector, SelectionKey.OP_READ);
				}
				if (key.isReadable()) {
					SocketChannel sc = (SocketChannel) key.channel();
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					int readBytes = sc.read(buffer);
					// 大于0:读到了字节、0:没有读到字节、-1:链路已经关闭
					if (readBytes > 0) {
						buffer.flip();
						byte[] bytes = new byte[buffer.remaining()];
						buffer.get(bytes);
						String msg = new String(bytes, "UTF-8");
						System.out.println("Server received:" + msg);
						doWrite(sc, msg);
					} 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 buffer = ByteBuffer.allocate(bytes.length);
				buffer.put(bytes);
				buffer.flip();
				channel.write(buffer);
			}
		}

	}

	private static class Power implements Runnable {

		private MultiplexerEchoServer server;

		public Power(MultiplexerEchoServer server) {
			this.server = server;
		}

		@Override
		public void run() {
			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			try {
				String msg = in.readLine();
				while (msg != null && !"shutdown".equalsIgnoreCase(msg)) {
					msg = in.readLine();
				}
				server.stop();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

}

 

 

package io.nio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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 NioEchoClient {

	public static void main(String[] args) {
		EchoClient client = new EchoClient("127.0.0.1", 8080);
		new Thread(client).start();
		new Thread(new Writer(client)).start();
	}

	private static class EchoClient implements Runnable {

		private String host;
		private int port;
		private Selector selector;
		private SocketChannel client;
		private volatile boolean running;

		public EchoClient(String host, int port) {
			try {
				this.running = true;
				this.host = host == null ? "127.0.0.1" : host;
				this.port = port;
				this.selector = Selector.open();
				this.client = SocketChannel.open();
				client.configureBlocking(false);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
			}
		}

		@Override
		public void run() {
			try {
				doConnect();
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
			}
			while (running) {
				try {
					selector.select(1000);
					Set<SelectionKey> readyKeys = selector.selectedKeys();
					Iterator<SelectionKey> it = readyKeys.iterator();
					SelectionKey key = null;
					while (it.hasNext()) {
						key = it.next();
						it.remove();
						try {
							handle(key);
						} catch (Exception e) {
							e.printStackTrace();
							if (key != null) {
								key.cancel();
								if (key.channel() != null) {
									key.channel().close();
								}
							}
							System.exit(1);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
					System.exit(1);
				}
			}
			if (selector != null) {
				try {
					selector.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		private void handle(SelectionKey key) throws IOException {
			if (key.isValid()) {
				SocketChannel sc = (SocketChannel) key.channel();
				if (key.isConnectable()) {
					if (sc.finishConnect()) {
						sc.register(selector, SelectionKey.OP_READ);
					} else {
						System.exit(1);
					}
				}
				if (key.isReadable()) {
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					int readBytes = sc.read(buffer);
					// 大于0:读到了字节、0:没有读到字节、-1:链路已经关闭
					if (readBytes > 0) {
						buffer.flip();
						byte[] bytes = new byte[buffer.remaining()];
						buffer.get(bytes);
						String msg = new String(bytes, "UTF-8");
						System.out.println("Client received:" + msg);
					} else if (readBytes < 0) {
						key.cancel();
						sc.close();
					}
				}
			}
		}

		private void doConnect() throws IOException {
			if (client.connect(new InetSocketAddress(host, port))) {
				client.register(selector, SelectionKey.OP_READ);
			} else {
				client.register(selector, SelectionKey.OP_CONNECT);
			}
		}

		public void doWrite(String msg) {
			byte[] bytes = msg.getBytes();
			ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
			buffer.put(bytes);
			buffer.flip();
			try {
				client.write(buffer);
				System.out.println("Cliend send:" + msg);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		public void stop() {
			running = false;
		}
	}

	private static class Writer implements Runnable {

		private EchoClient client;

		public Writer(EchoClient client) {
			this.client = client;
		}

		@Override
		public void run() {
			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			try {
				String msg = in.readLine();
				while (msg != null && !"quit".equalsIgnoreCase(msg)) {
					client.doWrite(msg);
					msg = in.readLine();
				}
				client.stop();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

猜你喜欢

转载自ryan-imagic.iteye.com/blog/2116100