Skip to content

Implement_netty_mutils_protocols

Geng Zhang edited this page Mar 6, 2017 · 1 revision

Netty同一端口支持多协议

这里的多协议支持,不仅仅是说框架支持多种协议;
而是指的是在同一个端口上同时支持多种协议的调用。

前提是:一个长连接只会用一种协议的数据。

实现思路

常见的思路是,我们服务端启动一个监听端口,例如 Tomcat 的 8080 端口,我们知道来的请求都是 HTTP 请求, 可以认为就是这个端口只支持 HTTP 协议。

如果我们需要实现一个监听端口,同时支持多种协议,那么我们需要能区分出是什么协议。
怎么区分呢?

其实很简单,我们可以通过数据包的前几位来判断这个协议是什么长连接。
例如:HTTP 是 GET/POST 等开头, HTTP/2 有协议预言,BSOA 协议有魔术位。

OK,还有一个问题就是,刚建立的长连接是不会发送数据的,我们怎么判断是啥协议呢?
那只能等待第一个请求过来的时候,才能判断是啥协议了。

Netty的ChannelInitializer

Netty的服务端的ServerBootStrap在启动的时候,可以通过childHandler()方法,为每个长连接指定一个ChannleHandler链初始化的类。

serverBootstrap.childHandler(new NettyServerChannelInitializer(transportConfig));

如果这个端口只支持一种协议,那么常见的做法是当长连接来的时候,就直接设置好协议解码器、协议编码器、消息分发器等ChannelHandler。例如:

    protected void initChannel(SocketChannel ch) throws Exception {
         ch.pipeline().addLast("frame", new FixedLengthFrameDecoder(4))
                      .addLast("encoder", new NettyEncoder(protocol))
                      .addLast("decoder", new NettyDecoder(protocol))
                      .addLast("serverChannelHandler", serverChannelHandler);
    }

但是我们为了实现多协议支持,就不能这么做,我们只能先只设置一个自适应解码器AdapterDecoder

    protected void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast("adapter", new AdapterDecoder(serverChannelHandler, transportConfig));
    }

AdapterDecoder实现

这个 AdapterDecoder 的作用只有一个,读取消息的前几位,判断出协议。
然后再动态的为这个长连接(Channel)重新设置 ChannleHandler 链。 设置完毕后将自己从链里删除。
这个自适应解码器相当于是一次性的,只为了第一次请求的时候自动装载 ChannelHandler链,它已经完成了自己的使命。

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 判断下,已经注册的头协议
        int offset = ProtocolFactory.getMaxMagicOffset();
        if (in.readableBytes() < offset) {
            return;
        }
        // 读取头几位
        byte[] magicHeadBytes = new byte[offset];
        in.readBytes(magicHeadBytes);
        in.readerIndex(in.readerIndex() - offset);
        // 自动判断协议
        Protocol protocol = ProtocolFactory.adaptiveProtocol(magicHeadBytes);
        if (protocol != null) {
            ChannelPipeline pipeline = ctx.pipeline();
            ProtocolInfo protocolInfo = protocol.protocolInfo();

            if (protocolInfo.getNetProtocol() == ProtocolInfo.NET_PROTOCOL_TCP) {
                /*
                 * 动态装载 TCP 相关的 ChannelHandler, 然后删掉自己
                 */
                pipeline.addLast(new LengthFieldBasedFrameDecoder(protocolInfo.maxFrameLength(),
                                        protocolInfo.lengthFieldOffset(),
                                        protocolInfo.lengthFieldLength(),
                                        protocolInfo.lengthAdjustment(),
                                        protocolInfo.initialBytesToStrip(),
                                        false))
                        .addLast("encoder", new NettyEncoder(protocol))
                        .addLast("decoder", new NettyDecoder(protocol))
                        .addLast("serverChannelHandler", serverChannelHandler);
                pipeline.remove(this);
                pipeline.fireChannelActive(); // 重新触发连接建立事件
            } else if (protocolInfo.getNetProtocol() == ProtocolInfo.NET_PROTOCOL_HTTP) {
                /*
                 * 动态装载 HTTP 相关的 ChannelHandler, 然后删掉自己
                 */
                pipeline.remove(this);
            }
        } else { //telnet
            /*
             * 动态装载 Telnet 相关的 ChannelHandler, 然后删掉自己
             */
            pipeline.remove(this);
        }
    }
Clone this wiki locally