[netty5: WebSocketFrame]-源码分析

发布于:2025-07-08 ⋅ 阅读:(21) ⋅ 点赞:(0)

WebSocketFrame

WebSocketFrame 是 Netty 中用于表示 WebSocket 消息帧的抽象基类,封装了帧的内容、分片标志和扩展位信息,供各类具体帧(如文本、二进制、控制帧)继承使用。

public abstract class WebSocketFrame extends BufferHolder<WebSocketFrame> {
	// 表示是否是当前 WebSocket 消息的最后一个分片(frame)。
    private final boolean finalFragment;
    // 表示 RSV1/RSV2/RSV3 位(协议保留字段),通常用于 WebSocket 扩展(如压缩、加密扩展等)。
    private final int rsv;

    protected WebSocketFrame(Buffer binaryData) {
        this(true, 0, binaryData);
    }

    protected WebSocketFrame(Send<Buffer> binaryData) {
        super(binaryData);
        finalFragment = true;
        rsv = 0;
    }

    protected WebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(binaryData);
        this.finalFragment = finalFragment;
        this.rsv = rsv;
    }

    protected WebSocketFrame(WebSocketFrame copyFrom, Buffer binaryData) {
        super(binaryData);
        finalFragment = copyFrom.finalFragment;
        rsv = copyFrom.rsv;
    }

	// 标识当前帧是否是消息的最后一部分(WebSocket 支持帧分片)
    public boolean isFinalFragment() {
        return finalFragment;
    }

	// 返回 RSV(Reserved Bits),可用于判断是否启用 WebSocket 扩展,如压缩 (permessage-deflate)
    public int rsv() {
        return rsv;
    }

    public Buffer binaryData() {
        return getBuffer();
    }

    @Override
    public String toString() {
        return StringUtil.simpleClassName(this) + "(data: " + getBuffer().toString(defaultCharset()) + ')';
    }
}

PingWebSocketFrame

PingWebSocketFrame 是 Netty 中表示 WebSocket Ping 控制帧的类,用于发送心跳检测数据,继承自 WebSocketFrame 并实现帧复制逻辑。

public class PingWebSocketFrame extends WebSocketFrame {

    public PingWebSocketFrame(Buffer binaryData) {
        super(binaryData);
    }

    public PingWebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(finalFragment, rsv, binaryData);
    }

    private PingWebSocketFrame(PingWebSocketFrame copyFrom, Buffer data) {
        super(copyFrom, data);
    }

    @Override
    protected WebSocketFrame receive(Buffer buf) {
        return new PingWebSocketFrame(this, buf);
    }
}

PongWebSocketFrame

PongWebSocketFrame 是 Netty 中用于响应 Ping 控制帧的 WebSocket Pong 帧实现,继承自 WebSocketFrame,支持数据内容和帧复制功能。

public class PongWebSocketFrame extends WebSocketFrame {

    public PongWebSocketFrame(Buffer binaryData) {
        super(binaryData);
    }

    public PongWebSocketFrame(Send<Buffer> binaryData) {
        super(binaryData);
    }

    public PongWebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(finalFragment, rsv, binaryData);
    }

    private PongWebSocketFrame(PongWebSocketFrame copyFrom, Buffer data) {
        super(copyFrom, data);
    }

    @Override
    protected WebSocketFrame receive(Buffer buf) {
        return new PongWebSocketFrame(this, buf);
    }
}

BinaryWebSocketFrame

BinaryWebSocketFrame 是 Netty 中用于传输二进制数据的 WebSocket 数据帧,支持设置是否为最后一帧以及协议扩展位。

public class BinaryWebSocketFrame extends WebSocketFrame {

    public BinaryWebSocketFrame(Buffer binaryData) {
        super(binaryData);
    }

    public BinaryWebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(finalFragment, rsv, binaryData);
    }

    private BinaryWebSocketFrame(BinaryWebSocketFrame copyFrom, Buffer data) {
        super(copyFrom, data);
    }

    @Override
    protected WebSocketFrame receive(Buffer buf) {
        return new BinaryWebSocketFrame(this, buf);
    }
}

TextWebSocketFrame

TextWebSocketFrame 是 Netty 中用于传输 UTF-8 编码文本的 WebSocket 数据帧,支持字符串构造、扩展位设置以及是否为最后一帧的标识。

public class TextWebSocketFrame extends WebSocketFrame {

    public TextWebSocketFrame(BufferAllocator allocator, String text) {
        super(fromText(allocator, text));
    }

    public TextWebSocketFrame(Buffer binaryData) {
        super(binaryData);
    }

    public TextWebSocketFrame(BufferAllocator allocator, boolean finalFragment, int rsv, String text) {
        super(finalFragment, rsv, fromText(allocator, text));
    }

    private TextWebSocketFrame(TextWebSocketFrame copyFrom, Buffer data) {
        super(copyFrom, data);
    }

    private static Buffer fromText(BufferAllocator allocator, String text) {
        if (text == null || text.isEmpty()) {
            return allocator.allocate(0);
        } else {
            return allocator.copyOf(text, UTF_8);
        }
    }

    public TextWebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(finalFragment, rsv, binaryData);
    }

    /**
     * Returns the text data in this frame.
     */
    public String text() {
        return binaryData().toString(UTF_8);
    }

    @Override
    protected WebSocketFrame receive(Buffer buf) {
        return new TextWebSocketFrame(this, buf);
    }
}

ContinuationWebSocketFrame

ContinuationWebSocketFrame 是用于 WebSocket 消息分片场景的续帧类型,可承载文本或二进制数据,支持设置是否为最终帧和协议扩展位。

public class ContinuationWebSocketFrame extends WebSocketFrame {

    public ContinuationWebSocketFrame(Buffer binaryData) {
        super(binaryData);
    }

    public ContinuationWebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(finalFragment, rsv, binaryData);
    }

    public ContinuationWebSocketFrame(BufferAllocator allocator, boolean finalFragment, int rsv, String text) {
        this(finalFragment, rsv, fromText(allocator, text));
    }

    private ContinuationWebSocketFrame(ContinuationWebSocketFrame copyFrom, Buffer data) {
        super(copyFrom, data);
    }
    
    public String text() {
        return binaryData().toString(StandardCharsets.UTF_8);
    }

    private static Buffer fromText(BufferAllocator allocator, String text) {
        if (text == null || text.isEmpty()) {
            return allocator.allocate(0);
        } else {
            return allocator.copyOf(text.getBytes(StandardCharsets.UTF_8));
        }
    }

    @Override
    protected WebSocketFrame receive(Buffer buf) {
        return new ContinuationWebSocketFrame(this, buf);
    }
}

CloseWebSocketFrame

CloseWebSocketFrame 表示 WebSocket 连接关闭帧,包含关闭状态码和可选的关闭原因,用于优雅地关闭连接。

public class CloseWebSocketFrame extends WebSocketFrame {

    public CloseWebSocketFrame(BufferAllocator allocator, WebSocketCloseStatus status) {
        this(allocator, requireValidStatusCode(status.code()), status.reasonText());
    }

    public CloseWebSocketFrame(BufferAllocator allocator, WebSocketCloseStatus status, String reasonText) {
        this(allocator, requireValidStatusCode(status.code()), reasonText);
    }

    public CloseWebSocketFrame(BufferAllocator allocator, int statusCode, String reasonText) {
        this(allocator, true, 0, requireValidStatusCode(statusCode), reasonText);
    }

    public CloseWebSocketFrame(BufferAllocator allocator, boolean finalFragment, int rsv) {
        this(finalFragment, rsv, allocator.allocate(0));
    }

    public CloseWebSocketFrame(BufferAllocator allocator, boolean finalFragment, int rsv, int statusCode, String reasonText) {
        super(finalFragment, rsv, newBinaryData(allocator, requireValidStatusCode(statusCode), reasonText));
    }

    public CloseWebSocketFrame(boolean finalFragment, int rsv, Buffer binaryData) {
        super(finalFragment, rsv, binaryData);
    }

    private CloseWebSocketFrame(CloseWebSocketFrame copyFrom, Buffer data) {
        super(copyFrom, data);
    }

	// 构造符合 WebSocket Close 帧格式的二进制数据,包含状态码和 UTF-8 编码的关闭原因文本。
    private static Buffer newBinaryData(BufferAllocator allocator, short statusCode, String reasonText) {
        if (reasonText == null) {
            reasonText = StringUtil.EMPTY_STRING;
        }

        final Buffer binaryData;
        if (!reasonText.isEmpty()) {
            byte[] reasonTextBytes = reasonText.getBytes(StandardCharsets.UTF_8);
            binaryData = allocator.allocate(2 + reasonTextBytes.length);
            binaryData.writeShort(statusCode);
            binaryData.writeBytes(reasonTextBytes);
        } else {
            binaryData = allocator.allocate(2).writeShort(statusCode);
        }

        return binaryData;
    }

    public int statusCode() {
        Buffer binaryData = binaryData();
        if (binaryData == null || binaryData.readableBytes() < 2) {
            return -1;
        }

        return binaryData.getUnsignedShort(binaryData.readerOffset());
    }

    public String reasonText() {
        Buffer binaryData = binaryData();
        if (binaryData == null || binaryData.readableBytes() <= 2) {
            return "";
        }

        int base = binaryData.readerOffset();
        try {
            binaryData.skipReadableBytes(2);
            return binaryData.toString(StandardCharsets.UTF_8);
        } finally {
            binaryData.readerOffset(base);
        }
    }

    @Override
    protected WebSocketFrame receive(Buffer buf) {
        return new CloseWebSocketFrame(this, buf);
    }

    static short requireValidStatusCode(int statusCode) {
        if (WebSocketCloseStatus.isValidStatusCode(statusCode)) {
            return (short) statusCode;
        } else {
            throw new IllegalArgumentException(
                    "WebSocket close status code does NOT comply with RFC-6455: " + statusCode);
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到