Netty做webSocket客户端,服务端拆包发送客户端接收处理

背景说明:

webSocket服务器端发送的message如果小于10K,则发送为一条信息。否则会拆成10K大小的若干包去发送,随后一条信息包含end bit表示消息结束。


netty client端配置如下:

        connectionBootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(1024*1024))
                .handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(final Channel ch) throws Exception {
                        clientHandler = new WebSocketClientNettyAdapter(WebSocketClientHandshakerFactory.newHandshaker(_uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), 100 * 1024 * 1024), t);
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(
                          new HttpClientCodec(),
                          new HttpObjectAggregator(8192),
                          //      WebSocketClientCompressionHandler.INSTANCE,
                          DgtWebSocketClientCompressionHandler.INSTANCE,
                          clientHandler
                        );
                    }
                });

其中HttpClientCodec是客户端解码器,其类继承关系如下:


因此第一步消息会进入CombinedChannelDuplexHandler类的channelRead方法:


继而调用HttpClientCodec内置Decoder类,其继承关系如下:


继而由ByteToMessageDecoder 的channelRead执行读处理。

第一条webSocket握手升级消息处理成功后,decoder变为WebSocket13FrameDecoder,后续的消息都会进入ByteToMessageDecoder 的channelRead执行读处理,具体机制可以debug跟踪源码。


读入message后,会进入callDecode方法进行解码:


继而调用相应的解码器的decode方法


webSocket解码器会按照webSocket去解析消息,读出消息长度以及是否使用压缩协议,继而转向不同的处理机制。

在本例中,服务器会将超过10K的消息进行压缩传输,所以读到第一个10K时,没有解析到final bit,并且得知消息使用了压缩,从而会将这10K消息转到压缩处理JdkZlibDecoder的decode方法,解析完成后发往我们自己的handler去处理消息。

然而此时的消息只是消息的第一部分,我们会收到一个BinaryWebSocketFrame,其isFinalFragment表示为false表示不是最终消息片段,后续的消息会继续效用callDecode方法进行解析,并以ContinuationWebSocketFrame的格式发送,知道我们接收到isFinalFragment为true的消息后,此条信息才算发送完整。

@Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        _client.onReadActivity();


        final Channel ch = ctx.channel();


        if (!handshaker.isHandshakeComplete()) {
            // web socket client connected
            handshaker.finishHandshake(ch, (FullHttpResponse) msg);
            handshakeFuture.setSuccess();
            _isActive = true;
            _client.onConnected();
            return;
        }


        if (msg instanceof FullHttpResponse) {
            final FullHttpResponse response = (FullHttpResponse) msg;
            throw new Exception("Unexpected FullHttpResponse (getStatus=" + response.getStatus() + ", content="
                    + response.content().toString(CharsetUtil.UTF_8) + ')');
        }


        final WebSocketFrame frame = (WebSocketFrame) msg;


        if (frame instanceof BinaryWebSocketFrame) {
            //junior
            ByteBuf content = ((WebSocketFrame) msg).content();
            if (!frame.isFinalFragment()) {
                byte[] bytes = new byte[content.readableBytes()];
                for (int i = 0; i < content.readableBytes(); i++) {
                    bytes[i] = content.getByte(i);
                }
                _binData.add(bytes);
                return;
            }
            byte[] bytes = new byte[content.readableBytes()];
            for (int i = 0; i < content.readableBytes(); i++) {
                bytes[i] = content.getByte(i);
            }
            _client.onBinaryMessage(ByteBuffer.wrap(bytes));


            return;
        } else if(frame instanceof ContinuationWebSocketFrame){
            ByteBuf content = ((WebSocketFrame) msg).content();
            byte[] bytes = new byte[content.readableBytes()];
            for (int i = 0; i < content.readableBytes(); i++) {
                bytes[i] = content.getByte(i);
            }
            _binData.add(bytes);
            if (!frame.isFinalFragment()) {
                return;
            }
            byte[] allBytes = new byte[0];
            for (byte[] _bytes:_binData) {
                allBytes = ArrayUtils.addAll(allBytes,_bytes);
            }
            _binData.clear();
            _client.onBinaryMessage(ByteBuffer.wrap(allBytes));
        }

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值