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