分別使用Java IO、NIO、Netty實現的一個Echo Server示例

分別使用Java IO、Java NIO、Netty來實現一個簡單的EchoServer(即原樣返回客戶端的輸入信息)。html

Java IO

int port = 9000;
ServerSocket ss = new ServerSocket(port);
while (true) {
	final Socket socket = ss.accept();
	new Thread(new Runnable() {
		public void run() {
			while (true) {
				try {
					BufferedInputStream in = new BufferedInputStream(
							socket.getInputStream());
					byte[] buf = new byte[1024];
					int len = in.read(buf); // read message from client
					String message = new String(buf, 0, len);
					BufferedOutputStream out = new BufferedOutputStream(
							socket.getOutputStream());
					out.write(message.getBytes()); // echo to client
					out.flush();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}
	}).start();
}

實際效果用telnet來演示,以下所示:java

$ telnet 127.0.0.1 9000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hi
hi
你好
你好

java io缺點:shell

須要爲每一個客戶端鏈接建立一個線程。app

Java NIO

ServerSocketChannel ssChannel = ServerSocketChannel.open();
int port = 9001;
ssChannel.bind(new InetSocketAddress(port));
Selector selector = Selector.open();

ssChannel.configureBlocking(false);
ssChannel.register(selector, SelectionKey.OP_ACCEPT); //註冊監聽鏈接請求

while (true) {
	selector.select();//阻塞 直到某個channel註冊的事件被觸發
	Set<SelectionKey> keys = selector.selectedKeys();
	for (SelectionKey key : keys) {
		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);
			sc.read(buffer);
			
			buffer.flip();
			sc.write(buffer);
		}
	}
	keys.clear();
}

優勢:socket

事件驅動  可經過一個線程來管理多個鏈接(channel)ide

但要注意 並非任何場景都是NIO優於IO的。
oop

Netty

public class NettyEchoServer {
	public class EchoServerHandler extends ChannelHandlerAdapter {
		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) { //ehco to client
			ctx.write(msg);
			ctx.flush();
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
			// Close the connection when an exception is raised.
			cause.printStackTrace();
			ctx.close();
		}

	}

	private int port;

	public NettyEchoServer(int port) {
		this.port = port;
	}

	public void run() throws Exception {
		EventLoopGroup bossGroup = new NioEventLoopGroup(); 
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap(); 
			b.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)
					.childHandler(new ChannelInitializer<SocketChannel>() { 
								@Override
								public void initChannel(SocketChannel ch)
										throws Exception {
									ch.pipeline().addLast(
											new EchoServerHandler());
								}
							}).option(ChannelOption.SO_BACKLOG, 128) 
					.childOption(ChannelOption.SO_KEEPALIVE, true); 

			// Bind and start to accept incoming connections.
			ChannelFuture f = b.bind(port).sync(); 

			// Wait until the server socket is closed.
			// In this example, this does not happen, but you can do that to gracefully shut down your server.
			f.channel().closeFuture().sync();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception {
		int port = 9002;
		new NettyEchoServer(port).run();
	}

}

上例摘自官方文檔ui

優勢:this

事件驅動的概念比NIO更直觀 彷佛僅須要繼承ChannelHandlerAdapter, 而後覆蓋相應方法便可。spa

參考文檔:

http://en.wikipedia.org/wiki/Non-blocking_I/O_(Java)

https://today.java.net/pub/a/today/2007/02/13/architecture-of-highly-scalable-nio-server.html

http://www.javaworld.com/article/2078654/java-se/java-se-five-ways-to-maximize-java-nio-and-nio-2.html

http://netty.io/wiki/user-guide-for-5.x.html

相關文章
相關標籤/搜索