Netty実戦シリーズ1つのマルチプロトコルが併存


Netty実戦シリーズ1つのマルチプロトコルが併存
  • 前言
  • 問題解決の考え方
  • プライマリサービスクラス
  • ProtocolSelectorHandlerプロトコルセレクタ
  • MyTextWebSocketFrameHandlerサービス側処理websocket
  • MyHttpServerHandlerサービス側処理http
  • カスタムプロトコル
  • SimpleProtocolプロトコル
  • CustomEncoderカスタムプロトコルエンコーダ
  • CustomDecoderカスタムプロトコルデコーダ
  • CutsomServerHandlerサービス側カスタムプロトコルプロセッサ
  • 検証結果
  • 前言
    前に多くのソースコードについての文章を书いて、私はやはり実戦に来なければならないと思って、いくつかの问题を解决して、最近Nettyでゲームサーバーのフレームワークを书きたいと思って、すでに少し书いて、私は1つのnettyプロセスの1つのポートに多くのプロトコルに応答することができて、例えば私は1つのポートがHTTPに応答することができることを望んで、WebSocketもしこれらのまだ知らないことに対して私が書いたソースコードの文章を見ることができたら、基本的には話していますが、プライベートプロトコルについては、私もここで例を示します.
    問題解決の構想
  • は2つのTCP プロセスを開き、1つのプロセスは1つに対応します.
  • 1つのプロセスは複数のポートをバインドし、異なるポートは異なるプロトコルを処理する.
  • プロセス1ポート、プロトコルヘッダを分析して異なるプロトコルを判断し、プロセッサを動的に追加します.

  • 最初の2つは簡単なはずですが、3つ目を簡単に実現しましょう.
    プライマリ・サービス・クラス
    他の設定はありませんが、基本テンプレートですが、nettyというプロセッサが1つしかありません.これは私たちが異なるプロトコルを解析して動的に追加する鍵です.
    public class MyTestServer {
        public static void main(String[] args) throws Exception{
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
    
                ServerBootstrap serverBootstrap = new ServerBootstrap();
    
                serverBootstrap.group(bossGroup, workerGroup);
                serverBootstrap.channel(NioServerSocketChannel.class);
                serverBootstrap.childOption(ChannelOption.TCP_NODELAY,true);
    
                serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
    
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new ProtocolSelectorDecoder());
    
                    }
                });
    
                //     
                ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
                channelFuture.channel().closeFuture().sync();
    
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    

    ProtocolSelectorHandlerプロトコルセレクタ
    これが動的プロトコルを実現する鍵であり,実際には最初の数バイトが何であるかを簡単に判断すること,すなわちいくつかの約束が必要である,例えばProtocolSelectorHandlerを約束すれば,WebSocketURIであり,/wsの接頭辞を見ると,GET /wsの握手が必要であることを説明する.私はWebSocketのプロセッサを追加して、再びこのデータを処理して、それからプロトコルセレクタを削除して、もちろんここは簡単な実現で、その間に問題が発生するかもしれませんが、あなたはプロトコルセレクタを削除して惨めで、私たちはしばらくハハを気にしません.WebSocketプロトコルであれば、私たちは一般的に を伝えないので、 プロトコルと プロトコルを区別することができます.HTTPリクエスト行にHTTPがありますか.もちろん私もただ简単に実现して、もし比较的に本当ならば、やはり判断を改行して、それから プロトコルが良いかどうかを见ます.
    /**
     *      ,      HTTP WEBSOCKET TCP    
     * Author: wangwei
     */
    public class ProtocolSelectorHandler extends ByteToMessageDecoder {
        /**
         * websocket       
         */
        private static final String WEBSOCKET_LINE_PREFIX = "GET /ws";
        /**
         * websocket uri
         */
        private static final String WEBSOCKET_PREFIX = "/ws";
        /**
         *   10   ,           
         */
        private static final int SPACE_LENGTH = 10;
    
        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            System.out.println("before :" + ctx.pipeline().toString());
            if (isWebSocketUrl(in)) {
                System.out.println("addWebSocketHandlers");
    
                addWebSocketHandlers(ctx.pipeline());
            } else if (isCustomProcotol(in)) {
                System.out.println("addTCPProtocolHandlers");
    
                addTCPProtocolHandlers(ctx.pipeline());
            } else {
                System.out.println("addHTTPHandlers");
                addHTTPHandlers(ctx.pipeline());
            }
            ctx.pipeline().remove(this);
            System.out.println("after :" + ctx.pipeline().toString());
        }
    
        /**
         *    websocket     
         *
         * @param byteBuf
         * @return
         */
        private boolean isWebSocketUrl(ByteBuf byteBuf) {
            if (byteBuf.readableBytes() < WEBSOCKET_LINE_PREFIX.length()) {
                return false;
            }
            byteBuf.markReaderIndex();
            byte[] content = new byte[WEBSOCKET_LINE_PREFIX.length()];
            byteBuf.readBytes(content);
            byteBuf.resetReaderIndex();
            String s = new String(content, CharsetUtil.UTF_8);
            return s.equals(WEBSOCKET_LINE_PREFIX);
        }
    
        /**
         *           
         * @param byteBuf
         * @return
         */
        private boolean isCustomProcotol(ByteBuf byteBuf) {
            byteBuf.markReaderIndex();
            byte[] content = new byte[SPACE_LENGTH];
            byteBuf.readBytes(content);
            byteBuf.resetReaderIndex();
            String s = new String(content, CharsetUtil.UTF_8);
            return s.indexOf(" ") == -1;
        }
    
        /**
         *     WebSocket   
         * @param pipeline
         */
        private void addWebSocketHandlers(ChannelPipeline pipeline) {
            pipeline.addLast(new HttpServerCodec());
            pipeline.addLast(new HttpObjectAggregator(8192));
            pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PREFIX));
            pipeline.addLast(new MyTextWebSocketFrameHandler());
        }
        /**
         *     TCP       
         * @param pipeline
         */
        private void addTCPProtocolHandlers(ChannelPipeline pipeline) {
            pipeline.addLast(new CustomDecoder(1024, 1, 4));//  1          1     ,4   4      
            pipeline.addLast(new CutsomServerHandler());
        }
    
    
        /**
         *     HTTP   
         * @param pipeline
         */
        private void addHTTPHandlers(ChannelPipeline pipeline) {
            pipeline.addLast(new HttpServerCodec());
            pipeline.addLast(new HttpObjectAggregator(8192));
            pipeline.addLast(new MyHttpServerHandler());
    
    
        }
    }
    

    MyTextWebSocketFrameHandlerサービス側websocket
    これは簡単ですが、受け取って返してください.
    public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
    
            System.out.println(String.format("  websocket   [%s]  :", ctx.channel().remoteAddress()+":"+ctx.channel().id().asLongText()) + msg.text());
            ctx.channel().writeAndFlush(new TextWebSocketFrame(  msg.text()));
        }
    
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.out.println("    :" + cause.getMessage());
            ctx.close();
        }
    }
    

    MyHttpServerHandlerサービス側httpの処理
    受信したメッセージを簡単に印刷しても返信はありません.
    public class MyHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
            if (msg instanceof FullHttpRequest) {
    
                FullHttpRequest httpRequest = (FullHttpRequest) msg;
                ByteBuf content1 = httpRequest.content();
                String cont = content1.toString(CharsetUtil.UTF_8);
                System.out.println(String.format("  HTTP   [%s]  ", ctx.channel().remoteAddress()+":"+ctx.channel().id().asLongText())  + ":" + cont);
    
            }
    
        }
    
    }
    
    

    カスタムプロトコル
    SimpleProtocolプロトコル
    まず、HTTPバイトストリームで直接送信される転送するプロトコルフォーマットを定義します.
    @Data
    public class SimpleProtocol {
        /**
         *     
         */
        private byte protocolType;
        /**
         *      
         */
        private int bodyLength;
        /**
         *     
         */
        private byte[] body;
    }
    

    CustomEncoderカスタムプロトコルエンコーダ
    カスタムプロトコルはもちろんコーディングする必要があります.最も簡単な実装は:
    public class CustomEncoder extends MessageToByteEncoder<SimpleProtocol> {
    
        @Override
        protected void encode(ChannelHandlerContext ctx, SimpleProtocol simpleProtocol, ByteBuf out) throws Exception {
            byte protocolType = simpleProtocol.getProtocolType();
            int length = simpleProtocol.getBodyLength();
            byte[] body = simpleProtocol.getBody();
            out.writeByte(protocolType);
            out.writeInt(length);
            if(length>0){
                out.writeBytes(body);
            }
        }
    }
    

    CustomDecoderカスタムプロトコルデコーダ
    私は直接集積TCPをサボって、内部は粘着バッグの分解問題を処理して、ここで処理がバッファを解放することを覚えておくことに注意します.
    public class CustomDecoder extends LengthFieldBasedFrameDecoder {
        
    
        public CustomDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
            super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
        }
        
        @Override
        protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
            ByteBuf byteBuf = (ByteBuf) super.decode(ctx, in);
            if (byteBuf == null) {
                return null;
            }
            SimpleProtocol simpleProtocol = new SimpleProtocol();
            simpleProtocol.setProtocolType(byteBuf.readByte());
            int length = byteBuf.readInt();
            simpleProtocol.setBodyLength(length);
            if (simpleProtocol.getBodyLength() > 0) {
                byte[] body = new byte[length];
                byteBuf.readBytes(body);
                simpleProtocol.setBody(body);
            }
            in.release();//    
            return simpleProtocol;
        }
    
    }
    

    CutsomServerHandlerサービス側カスタムプロトコルプロセッサ
    簡単な印刷で受け取った情報でもあります.
    public class CutsomServerHandler extends SimpleChannelInboundHandler<SimpleProtocol> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, SimpleProtocol msg) throws Exception {
            System.out.println(String.format("  TCP       [%s]  :", ctx.channel().remoteAddress()+":"+ctx.channel().id().asLongText()) +new String(msg.getBody(), CharsetUtil.UTF_8));
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    
    }
    

    検証結果
    基本的には書いてありますが、実行の結果を見てみましょう.先LengthFieldBasedFrameDecoder.Netty实战系列一之多协议并存_第1张图片それからHTTPに行って、2つを見てみましょう.Netty实战系列一之多协议并存_第2张图片カスタム・プロトコルを追加します.Netty实战系列一之多协议并存_第3张图片実际には、WebSocketを理解している限り、HTTPプロトコルはどのように解析されているのか、WebSocketプライベートプロトコルはどのように解析されているのか、自然にどのように区分されているのかを知っています.彼らはすべてTCPに基づいているので、TCPのバイトデータを手に入れることができ、どのプロトコルなのかを判断する方法があります.すべてのアプリケーション層のプロトコルは処理することができます.もちろん私はここで簡単にTCP個を実現しました.あなたは3個を拡張することができます.
    ソース:netty_action
    詳細:https://cloud.tencent.com/developer/article/1366184
    はい、今日はここまでで、学习の理解に役立つことを望んで、大神は喷かないでくださいを见て、自分の学习の理解のためだけで、能力は有限で、多く许してください.