背景说明:
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)); }