1. 程式人生 > >Java NIO框架Netty簡單使用

Java NIO框架Netty簡單使用

    之前寫了一篇文章:Java 網路IO程式設計總結(BIO、NIO、AIO均含完整例項程式碼),介紹瞭如何使用Java原生IO支援進行網路程式設計,本文介紹一種更為簡單的方式,即Java NIO框架。

    Netty是業界最流行的NIO框架之一,具有良好的健壯性、功能、效能、可定製性和可擴充套件性。同時,它提供的十分簡單的API,大大簡化了我們的網路程式設計。

    同Java IO介紹的文章一樣,本文所展示的例子,實現了一個相同的功能。

1、服務端

    Server:

package com.anxpp.io.calculator.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Server {
    private int port;
    public Server(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)
             .option(ChannelOption.SO_BACKLOG, 1024)
             .childOption(ChannelOption.SO_KEEPALIVE, true)
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new ServerHandler());
                 }
             });
            ChannelFuture f = b.bind(port).sync();
            System.out.println("伺服器開啟:"+port);
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 9090;
        }
        new Server(port).run();
    }
}

    ServerHandler:

package com.anxpp.io.calculator.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.io.UnsupportedEncodingException;
import com.anxpp.io.utils.Calculator;
public class ServerHandler extends ChannelInboundHandlerAdapter {
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
		ByteBuf in = (ByteBuf) msg;
		byte[] req = new byte[in.readableBytes()];
		in.readBytes(req);
		String body = new String(req,"utf-8");
		System.out.println("收到客戶端訊息:"+body);
		String calrResult = null;
		try{
			calrResult = Calculator.Instance.cal(body).toString();
		}catch(Exception e){
			calrResult = "錯誤的表示式:" + e.getMessage();
		}
		ctx.write(Unpooled.copiedBuffer(calrResult.getBytes()));
	}
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}
	/**
	 * 異常處理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		ctx.close();
	}
}

2、客戶端

    Client:

package com.anxpp.io.calculator.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.util.Scanner;
public class Client implements Runnable{
    static ClientHandler client = new ClientHandler();
    public static void main(String[] args) throws Exception {
    	new Thread(new Client()).start();
		@SuppressWarnings("resource")
		Scanner scanner = new Scanner(System.in);
		while(client.sendMsg(scanner.nextLine()));
    }
	@Override
	public void run() {
        String host = "127.0.0.1";
        int port = 9090;
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(client);
                }
            });
            ChannelFuture f = b.connect(host, port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
            workerGroup.shutdownGracefully();
        }
	}
}

    ClientHandler:

package com.anxpp.io.calculator.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.io.UnsupportedEncodingException;
public class ClientHandler extends ChannelInboundHandlerAdapter {
	ChannelHandlerContext ctx;
	/**
	 * tcp鏈路簡歷成功後呼叫
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		this.ctx = ctx;
	}
	public boolean sendMsg(String msg){
		System.out.println("客戶端傳送訊息:"+msg);
		byte[] req = msg.getBytes();
		ByteBuf m = Unpooled.buffer(req.length);
		m.writeBytes(req);
		ctx.writeAndFlush(m);
		return msg.equals("q")?false:true;
	}
	/**
	 * 收到伺服器訊息後呼叫
	 * @throws UnsupportedEncodingException 
	 */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
        ByteBuf buf = (ByteBuf) msg;
		byte[] req = new byte[buf.readableBytes()];
		buf.readBytes(req);
		String body = new String(req,"utf-8");
		System.out.println("伺服器訊息:"+body);
    }
    /**
     * 發生異常時呼叫
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

3、用於計算的工具類

  1. package com.anxpp.io.utils;
  2. import javax.script.ScriptEngine;
  3. import javax.script.ScriptEngineManager;
  4. import javax.script.ScriptException;
  5. publicenumCalculator{
  6. Instance;
  7. privatefinalstaticScriptEngine jse =newScriptEngineManager().getEngineByName("JavaScript");
  8. publicObject cal(String expression)throwsScriptException{
  9. return jse.eval(expression);
  10. }
  11. }

4、測試

    分別啟動服務端和客戶端,然後再客戶端控制檯輸入表示式:

  1. 1+5+5+5+5+5
  2. 客戶端傳送訊息:1+5+5+5+5+5
  3. 伺服器訊息:26
  4. 156158*458918+125615
  5. 客戶端傳送訊息:156158*458918+125615
  6. 伺服器訊息:7.1663842659E10
  7. 1895612+555+5+5+5+5+5+5+5-5*4/4
  8. 客戶端傳送訊息:1895612+555+5+5+5+5+5+5+5-5*4/4
  9. 伺服器訊息:1896197

    可以看到服務端返回的結果。

    檢視服務端控制檯:

  1. 伺服器開啟:9090
  2. 收到客戶端訊息:1+5+5+5+5+5
  3. 收到客戶端訊息:156158*458918+125615
  4. 收到客戶端訊息:1895612+555+5+5+5+5+5+5+5-5*4/4

5、更多

    相關文章:

    後續會繼續更新Netty相關內容,直到一個簡陋的通訊伺服器完成。