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

jopen 9年前發布 | 23K 次閱讀 Java IO Java開發

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

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來演示,如下所示:

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

java io缺點:

需要為每個客戶端連接創建一個線程。

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();
}

優點:

事件驅動  可通過一個線程來管理多個連接(channel)

但要注意 并不是任何場景都是NIO優于IO的。

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();
    }

}

上例摘自官方文檔

優點:

事件驅動的概念比NIO更直觀 似乎僅需要繼承ChannelHandlerAdapter, 然后覆蓋相應方法即可。

參考文檔:

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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!