今天测试中发现Chrome在发送WebsocketFrame时,会自动将大于127KB的数据分片传输, 网上有人提到如果服务器支持RFC6455则不会出现这个问题, 但实测Netty使用了Websocket13的handshaker也无法解决这个问题.
那么就需要在服务器端进行处理, 将分片的数据结合在一起之后再交给后面的handler去执行.
用BinaryFrame做例子, 在传递数据时, 会先收到一个BinaryFrame, 它的isFinalFragment位(第一位)是0, 后续的数据会以ContinuationFrame的形式发送, 直到最后一片finalFragment位是1的ContinuationFrame结束, 中间不会穿插其它的Frame.
那么在服务器端我们就可以写一个Handler, 来拼接这些ContinuationFrame:
@Slf4j
public class ContinuationWebSocketFrameHandler extends ChannelInboundHandlerAdapter {
private ByteBuf data = null;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof WebSocketFrame) {
WebSocketFrame frame = (WebSocketFrame) msg;
if (frame instanceof BinaryWebSocketFrame) {
if (!frame.isFinalFragment()) {
if (data != null) {
data.release();
}
ByteBuf buf = frame.content();
data = ctx.alloc().buffer(buf.readableBytes());
data.writeBytes(buf);
buf.release();
return;
} else {
ctx.fireChannelRead(msg);
if (data != null) {
data.release();
data = null;
}
return;
}
} else if (frame instanceof ContinuationWebSocketFrame) {
if (data == null) {
log.error("ContinuationWebSocketFrame找不到初始的BinaryWebSocketFrame");
frame.content().release();
return;
}
data.writeBytes(frame.content());
frame.content().release();
if (frame.isFinalFragment()) {
ctx.fireChannelRead(new BinaryWebSocketFrame(data));
data = null;
return;
}
} else {
if (data != null) {
data.release();
data = null;
}
ctx.fireChannelRead(msg);
}
} else {
if (data != null) {
data.release();
data = null;
}
ctx.fireChannelRead(msg);
}
}
}
可以看到逻辑还是比较简单的, 判断一个frame是不是finalFragment, 不是的话, 就将数据暂时存入一个ByteBuf里, 直到收到final fragment,再向后传递并重置ByteBuf.
需要注意的是如果业务有传递TextFrame的需求的话, 需要同时处理TextFrame的分片.