簡體   English   中英

Netty ByteToMessageDecoder不接收在其他tcp數據包中發送的消息

[英]Netty ByteToMessageDecoder not receiving messages sent in different tcp packets

自定義ByteToMessageEncoder不會接收在相同tcp連接中但在不同tcp消息中發送的字節。

我被分配來解決一個問題,在該問題中,使用了兩年的舊系統開始出現異常。 在我這方面,有一些其他開發人員用netty編寫的tcp服務器,它接收帶有靜態長度標頭和可變長度主體的二進制消息。 正文長度由告訴消息類型的標頭字段定義。 我們維護消息類型及其長度的映射。

面臨的問題是,在正確解碼了標頭並知道主體長度之后,同一解碼器期望主體出現在同一ByteBuf中(這是來自byteChannel的一個fireChannelRead事件)。

但是,有時緩沖區中沒有足夠的內容,因此解碼器放棄了。 但是,下次將解碼方法稱為主體字節時,就會顯示主體字節並將其錯誤地解釋為標頭,從而使解碼器不同步。

使用netty組裝消息的正確方法是什么,消息的字節可能會分成較小的塊?

這是當前解碼器的基礎。

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Message message = decode(ctx, in); 
        if (message != null) {
            out.add(message);
        }
    }

    protected Message decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if (in.readableBytes() < MessageHeader.SIZE) {
            return null;
        }
        ByteBuf headerBytes = in.readBytes(MessageHeader.SIZE);
        MessageHeader header = MessageHeader.decode(headerBytes, new ProcessingTracker(MessagePart.HEADER));
        if (header == null) {
            ctx.disconnect().sync();
            logger.debug("Disconnected from channel");
            return null;
        }
        int bodySize = header.getMessageType().getMessageBodySize();
        if (!waitingForBytes(in, bodySize, READ_TRY_TIMES)) {
            ctx.disconnect().sync();
            logger.debug("Disconnected from channel");
            return null;
        }
        ByteBuf messageBytes = in.readBytes(bodySize);
        messageBytes.resetReaderIndex();
        Message message = Message.decode(header, messageBytes, 0);
        return message;
    }


    public boolean waitingForBytes(ByteBuf in, int bodySize, int counter) {
        if (counter == 0) {
            logger.warn("Didn't get enough bytes of message body in MessagDecoder. Giving up and disconnecting from remote peer.");
            return false;
        }
        logger.debug(String.format("Readable bytes in buffer %d, expected %d", in.readableBytes(), bodySize));

        if (in.readableBytes() < bodySize) {

            try {
                Thread.sleep(20L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return waitingForBytes(in, bodySize, counter - 1);
        } else {
            return true;
        }
    }

您的代碼中存在多個問題...

首先,不允許您在代碼中調用sync() ,因為這樣將“死鎖” EventLoop

其次,您不能在此處使用waitingForBytes ,因為它基本上會使EventLoop上的所有其他IO EventLoop ,這意味着您將永遠不會繼續執行任何IO。 在像Netty這樣的框架中, EventLoop不要阻塞EventLoop線程,這一點很重要,因為這基本上會導致一切陳舊,並且不會取得任何進展。

查看超類ByteToMessageDecoder ,很明顯子類應該通過讀取的字節或解碼的消息數來傳達解碼進度。 我認為我從那里繼承此代碼的人錯過了這一點。

此實現通過了一些初步測試:

public class MessageDecoder extends ByteToMessageDecoder {

    private static final Logger logger = LoggerFactory.getLogger(MessageDecoder.class);

    private ByteBuf headBuf = Unpooled.buffer(MessageHeader.SIZE);

    private MessageHeader header = null;

    private ByteBuf bodyBuf;

    private int bodylength = 0;

    private int messageBytes = 0;

    private final static int STATE_READ_HEADER = 1, STATE_READ_BODY = 2;

    private int state = STATE_READ_HEADER;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Message message = decode(ctx, in);
        if (message != null) {
            out.add(message);
        }
    }

    protected Message decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        int readBytes = 0;
        logstate(in);
        switch (state) {
        case STATE_READ_HEADER:
            if (in.readableBytes() <= MessageHeader.SIZE - messageBytes) {
                readBytes = in.readableBytes();
            } else {
                readBytes = MessageHeader.SIZE - messageBytes;
            }
            headBuf.writeBytes(in, readBytes);
            messageBytes += readBytes;
            if (messageBytes == MessageHeader.SIZE) {
                state = STATE_READ_BODY;
                header = MessageHeader.decode(headBuf, new ProcessingTracker(MessagePart.HEADER));
                bodylength = header.getMessageType().getMessageBodySize();
                bodyBuf = Unpooled.buffer(bodylength);
            }
            break;
        case STATE_READ_BODY:
            if (in.readableBytes() <= bodylength - (messageBytes - MessageHeader.SIZE)) {
                readBytes = in.readableBytes();
            } else {
                readBytes = bodylength - (messageBytes - MessageHeader.SIZE);
            }
            bodyBuf.writeBytes(in, readBytes);
            messageBytes += readBytes;
            if (messageBytes == MessageHeader.SIZE + bodylength) {
                state = STATE_READ_HEADER;
                Message message = Message.decode(header, bodyBuf, 0);
                reset();
                return message;
            }
            break;
        }
        return null;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM