1. 程式人生 > 實用技巧 >netty構建一個簡單的C/S程式

netty構建一個簡單的C/S程式

構建一個簡單的socket程式

這裡採用的是空maven專案,構建client端和server端,實現一個客戶端建立連線後傳送訊息,然後服務端返回一個訊息的簡單程式

server端程式碼

基本套路:

  1. 在Server類中建立分發執行緒組和工作執行緒組,建立啟動類Bootstrap服務端是ServerBootstrap)
  2. 為Bootstrap進行初始化,指定channel,初始化channel(初始化channel有兩個函式,一個是childHandler,還有個是handler,其中childHandler對應的是工作執行緒組)
  3. 編寫初始化channel的類,裝填handler在管道中。(這裡我把channel初始化類解除安裝Server類中了,使用了匿名內部類建立並初始化)
  4. 編寫業務handler

Server類程式碼:

public class Server {
    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();//分發執行緒組
        EventLoopGroup workerGroup = new NioEventLoopGroup();//工作執行緒組
        ServerBootstrap cb = new ServerBootstrap();//服務啟動物件
        try {
            //這裡直接用匿名內部類構造channel初始化類
            //繫結執行緒組,設定channel,初始化channel
            cb.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();//初始化管道
                    pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4))
                            .addLast(new LengthFieldPrepender(4))
                            .addLast(new StringDecoder())
                            .addLast(new StringEncoder())
                            .addLast(new ServerHandler());//最後執行我們的業務handler
                }
            });
            ChannelFuture channelFuture = cb.bind( 9999).sync();//繫結埠
            channelFuture.channel().closeFuture().sync();//關閉
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();//優雅關閉
            workerGroup.shutdownGracefully();//優雅關閉
        }
    }
}

handler程式碼:

public class ServerHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //等待五秒後傳送給客戶端訊息
        Thread.sleep(5000);
        System.out.println(ctx.channel().remoteAddress()+":"+msg);
        ctx.writeAndFlush("服務端端回覆");
    }

    //處理異常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();//遇到異常關閉連線
    }
}

client端程式碼

client端程式碼和server端程式碼很相似,這也是源於netty的巧妙設計,很強大
client端的server程式碼:

public class ClientServer {
    public static void main(String[] args) {
        EventLoopGroup group = new NioEventLoopGroup();//客戶端不需要分發執行緒,所以只需要一個執行緒組就行了
        Bootstrap cb = new Bootstrap();//這裡不再使用ServerBootstrap了,因為是客戶端
        try {
            //這裡直接用匿名內部類構造channel初始化類
            //同樣這裡的channel物件,用的也不是NioServerSocketChannel進行反射構造了。因為只有一個執行緒組就使用了handler
            cb.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4))
                            .addLast(new LengthFieldPrepender(4))
                            .addLast(new StringDecoder())
                            .addLast(new StringEncoder())
                            .addLast(new ClientHandler());//這裡基本上和服務端一樣
                }
            });
            ChannelFuture channelFuture = cb.connect("localhost", 9999).sync();
            channelFuture.channel().writeAndFlush("hello");//這裡是手動傳送訊息,其實還可以通過handler中的回撥觸發傳送訊息
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

handler程式碼:

public class ClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+":"+msg);//顯示服務端的訊息
        ctx.writeAndFlush("客戶端回覆");//然後再回復,這裡就會不斷的相互發送訊息,因為服務端也會回覆,互相傳送
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

執行結果

先啟動server再啟動client,檢視結果
Server端

Client端

總結

可以看出netty不僅對服務端有很好的封裝,相應的對於服務端也能同樣的套路編寫。方便了我們使用底層的網路程式設計API,netty的設計理念只是為了提供一個更好的使用底層網路程式設計的框架,可以供我們做更多的上層搭建。
通過之前的基於Http協議的程式和現在的C/S模式的程式,也能看出,這其中變化最大的部分應該是對channel初始化的配置,其中很多都是使用netty提供好的handler進行管道的配置,能讓我們更容易的解析socket中傳輸的資料,這將會是使用netty的重點學習部分。