分别使用Java IO、NIO、Netty实现的一个Echo Server示例

分别使用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();
	}

}

上例摘自官方文档

优点:

似乎仅需要继承ChannelHandlerAdapter, 然后覆盖相应方法即可。

参考文档:

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。