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