分别使用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 |