Netty源码分析-Websocket之WebSocket08FrameDecoder

WebSocket08FrameDecoder是Netty协议WS协议的解码器。

 


public class WebSocket08FrameDecoder extends ByteToMessageDecoder
        implements WebSocketFrameDecoder {

    //当前解码器状态枚举
    enum State {
        READING_FIRST,
        READING_SECOND,
        READING_SIZE,
        MASKING_KEY,
        PAYLOAD,
        CORRUPT
    }

    //定义opcode
    private static final byte OPCODE_CONT = 0x0;
    private static final byte OPCODE_TEXT = 0x1;
    private static final byte OPCODE_BINARY = 0x2;
    private static final byte OPCODE_CLOSE = 0x8;
    private static final byte OPCODE_PING = 0x9;
    private static final byte OPCODE_PONG = 0xA;

    //Websocket最大荷载数据长度,超过该值抛出异常
    private final long maxFramePayloadLength;

    //是否允许WS扩展
    private final boolean allowExtensions;

    //是否期望对荷载数据进行掩码-客户端发送的数据必须要掩码
    private final boolean expectMaskedFrames;

    //是否允许掩码缺失
    private final boolean allowMaskMismatch;

    //分片发送的数量
    private int fragmentedFramesCount;

    //当前ws帧是否是完整的
    private boolean frameFinalFlag;

    //当前ws荷载数据是否已经掩码
    private boolean frameMasked;

    //RSV1 RSV2 RSV3
    private int frameRsv;

    //ws帧内 opocde的值
    private int frameOpcode;

    //荷载数据的长度
    private long framePayloadLength;
    //掩码
    private byte[] maskingKey;
    //ws协议PayloadLength表示的长度
    private int framePayloadLen1;

    //是否收到关闭帧
    private boolean receivedClosingHandshake;

    //初始状态
    private State state = State.READING_FIRST;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

        // Discard all data received if closing handshake was received before.
        //如果已经收到关闭帧,则丢弃说有字节
        if (receivedClosingHandshake) {
            in.skipBytes(actualReadableBytes());
            return;
        }
            switch (state) {
                case READING_FIRST:
                    if (!in.isReadable()) {
                        return;
                    }

                    //把荷载数据长度设置为0
                    framePayloadLength = 0;

                    // FIN, RSV, OPCODE
                    //读取ws帧的第一个字节,解析出FIN  RSV OPCODE
                    byte b = in.readByte();
                    frameFinalFlag = (b & 0x80) != 0;  //b & 10000000 得到FIN
                    frameRsv = (b & 0x70) >> 4;        //b & 01110000 完了右移4位 得到RSV
                    frameOpcode = b & 0x0F;  // b & 00001111 得到opcode

                    //改变状态
                    state = State.READING_SECOND;
                case READING_SECOND:
                    if (!in.isReadable()) {
                        return;
                    }
                    //读取ws帧的第二个字节
                    // MASK, PAYLOAD LEN 1
                    b = in.readByte();

                    //计算是否需要掩码
                    frameMasked = (b & 0x80) != 0; 
                    //ws协议PayloadLength表示的长度
                    framePayloadLen1 = b & 0x7F;

                    //如果RSV不为0说明使用了WS扩展协议,allowExtensions如果设置为不允许扩展则报错
                    //目前RSV都为0,还没有扩展协议
                    if (frameRsv != 0 && !allowExtensions) {
                        protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv);
                        return;
                    }

                    //如果不允许缺失掩码  并且 客户端又没有掩码 则报错
                    if (!allowMaskMismatch && expectMaskedFrames != frameMasked) {
                        protocolViolation(ctx, "received a frame that is not masked as expected");
                        return;
                    }

                    //如果opcpde为一个控制帧 如果 ping pong close
                    if (frameOpcode > 7) { // control frame (have MSB in opcode set)

                        // control frames MUST NOT be fragmented
                        //控制帧必须是一个完整的帧,所有frameFinalFlag必须为true
                        if (!frameFinalFlag) {
                            protocolViolation(ctx, "fragmented control frame");
                            return;
                        }

                        //控制帧framePayload必须小于等于125
                        // control frames MUST have payload 125 octets or less
                        if (framePayloadLen1 > 125) {
                            protocolViolation(ctx, "control frame with payload length > 125 octets");
                            return;
                        }

                        //控制帧目前只能是close ping pong,其它目前ws还未定义,出现则报错
                        // check for reserved control frame opcodes
                        if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING
                                || frameOpcode == OPCODE_PONG)) {
                            protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode);
                            return;
                        }

                        // close frame : if there is a body, the first two bytes of the
                        // body MUST be a 2-byte unsigned integer (in network byte
                        // order) representing a getStatus code
                        //关闭帧framePayloadLen1必为0,不能携带数据
                        if (frameOpcode == 8 && framePayloadLen1 == 1) {
                            protocolViolation(ctx, "received close control frame with payload len 1");
                            return;
                        }
                    } else { // data frame
                    	//小于7的都是数据帧
                    	//%x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
						//%x1:表示这是一个文本帧(frame)
						//%x2:表示这是一个二进制帧(frame)
                        // check for reserved data frame opcodes
                        //目前只支持这三种帧,其它抛出异常
                        if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT
                                || frameOpcode == OPCODE_BINARY)) {
                            protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode);
                            return;
                        }

                        //如果是延续帧,那前面必须有一个Text或Binary帧,通过fragmentedFramesCount>0来判断
                        // check opcode vs message fragmentation state 1/2
                        if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) {
                            protocolViolation(ctx, "received continuation data frame outside fragmented message");
                            return;
                        }

                        //如果fragmentedFramesCount != 0 说明前面出现了text或binary帧,并且fin为false 指示后续还有数据
                        //但是frameOpcode又不是一个延续帧,说明出现混乱情况报错
                        //我觉得frameOpcode != OPCODE_PING是一个无效的判断
                        // check opcode vs message fragmentation state 2/2
                        if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) {
                            protocolViolation(ctx,
                                    "received non-continuation data frame while inside fragmented message");
                            return;
                        }
                    }

                    //修改状态
                    state = State.READING_SIZE;
                 case READING_SIZE:

                    // Read frame payload length
                 	//如果payload length=126 后续2个字节是荷载数据的长度
                    if (framePayloadLen1 == 126) {
                        if (in.readableBytes() < 2) {
                            return;
                        }
                        //读2个字节,按无符号处理
                        framePayloadLength = in.readUnsignedShort();
                        if (framePayloadLength < 126) {
                            protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
                            return;
                        }

                        //127 后续8个字节是何在数据的长度
                    } else if (framePayloadLen1 == 127) {
                        if (in.readableBytes() < 8) {
                            return;
                        }
                        //读取8个字节为数据长度
                        framePayloadLength = in.readLong();
                        // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe
                        // just check if it's negative?

                        if (framePayloadLength < 65536) {
                            protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
                            return;
                        }
                    } else {
                    	//payload length<125 说明framePayloadLen1本身就表示数据长度
                        framePayloadLength = framePayloadLen1;
                    }

                    //如果荷载数据的长度 大于阈值,抛出异常
                    if (framePayloadLength > maxFramePayloadLength) {
                        protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded.");
                        return;
                    }

                    if (logger.isDebugEnabled()) {
                        logger.debug("Decoding WebSocket Frame length={}", framePayloadLength);
                    }

                    //转换状态
                    state = State.MASKING_KEY;
                case MASKING_KEY:
                	//是否有掩码
                    if (frameMasked) {
                        if (in.readableBytes() < 4) {
                            return;
                        }

                        //读取4个字节,读取掩码
                        if (maskingKey == null) {
                            maskingKey = new byte[4];
                        }
                        in.readBytes(maskingKey);
                    }

                    //转换状态
                    state = State.PAYLOAD;
                case PAYLOAD:
                	//可读数据达不到荷载数据长度则等待下一轮事件
                    if (in.readableBytes() < framePayloadLength) {
                        return;
                    }

                    ByteBuf payloadBuffer = null;
                    try {
                    	//将荷载数据读到新的缓冲区中
                        payloadBuffer = readBytes(ctx.alloc(), in, toFrameLength(framePayloadLength));

                        //切换状态为初始状态,进行下一轮读取。
                        state = State.READING_FIRST;

                        //如果有掩码,需要进行XOR二次计算还原出原文
                        // Unmask data if needed
                        if (frameMasked) {
                            unmask(payloadBuffer);
                        }

                        // Processing ping/pong/close frames because they cannot be
                        // fragmented
                        //根据情况封装不同数据帧
                        if (frameOpcode == OPCODE_PING) {
                            out.add(new PingWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer));
                            payloadBuffer = null;
                            return;
                        }
                        if (frameOpcode == OPCODE_PONG) {
                            out.add(new PongWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer));
                            payloadBuffer = null;
                            return;
                        }
                        if (frameOpcode == OPCODE_CLOSE) {
                        	//如果是对方发的Close帧则关闭socket
                            receivedClosingHandshake = true;
                            checkCloseFrameBody(ctx, payloadBuffer);
                            out.add(new CloseWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer));
                            payloadBuffer = null;
                            return;
                        }

                        // Processing for possible fragmented messages for text and binary
                        // frames
                        if (frameFinalFlag) {
                        	//如果是最终的分片则fragmentedFramesCount=0
                            // Final frame of the sequence. Apparently ping frames are
                            // allowed in the middle of a fragmented message
                            if (frameOpcode != OPCODE_PING) {
                                fragmentedFramesCount = 0;
                            }
                        } else {
                            // Increment counter
                            //否则fragmentedFramesCount++
                            fragmentedFramesCount++;
                        }

                        // 返回各种帧
                        if (frameOpcode == OPCODE_TEXT) {
                            out.add(new TextWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer));
                            payloadBuffer = null;
                            return;
                        } else if (frameOpcode == OPCODE_BINARY) {
                            out.add(new BinaryWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer));
                            payloadBuffer = null;
                            return;
                        } else if (frameOpcode == OPCODE_CONT) {
                            out.add(new ContinuationWebSocketFrame(frameFinalFlag, frameRsv,
                                    payloadBuffer));
                            payloadBuffer = null;
                            return;
                        } else {
                            throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: "
                                    + frameOpcode);
                        }
                    } finally {
                    	//释放缓冲区,如果payloadBuffer!=null 说明没有成功返回数据帧
                        if (payloadBuffer != null) {
                            payloadBuffer.release();
                        }
                    }
                case CORRUPT:
                    if (in.isReadable()) {
                        // If we don't keep reading Netty will throw an exception saying
                        // we can't return null if no bytes read and state not changed.
                        in.readByte();
                    }
                    return;
                default:
                    throw new Error("Shouldn't reach here.");
            }
    }

    private void unmask(ByteBuf frame) {
        int i = frame.readerIndex();
        int end = frame.writerIndex();

        ByteOrder order = frame.order();

        //把掩码二进制数组转换为int
        int intMask = ((maskingKey[0] & 0xFF) << 24)
                    | ((maskingKey[1] & 0xFF) << 16)
                    | ((maskingKey[2] & 0xFF) << 8)
                    | (maskingKey[3] & 0xFF);

        //如果是小端序,需要把INT类型的掩码反转
        if (order == ByteOrder.LITTLE_ENDIAN) {
            intMask = Integer.reverseBytes(intMask);
        }

        //XOR运算,还原原始值
        for (; i + 3 < end; i += 4) {
            int unmasked = frame.getInt(i) ^ intMask;
            frame.setInt(i, unmasked);
        }
        for (; i < end; i++) {
            frame.setByte(i, frame.getByte(i) ^ maskingKey[i % 4]);
        }
    }

    //抛出异常
    private void protocolViolation(ChannelHandlerContext ctx, String reason) {
        protocolViolation(ctx, new CorruptedFrameException(reason));
    }

    //抛出异常,关闭socket
    private void protocolViolation(ChannelHandlerContext ctx, CorruptedFrameException ex) {
        state = State.CORRUPT;
        if (ctx.channel().isActive()) {
            Object closeMessage;
            if (receivedClosingHandshake) {
                closeMessage = Unpooled.EMPTY_BUFFER;
            } else {
                closeMessage = new CloseWebSocketFrame(1002, null);
            }
            ctx.writeAndFlush(closeMessage).addListener(ChannelFutureListener.CLOSE);
        }
        throw ex;
    }
}

QQ群:212320390

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值