socket messages being split by netty

2019-08-08 15:19发布

问题:

I wrote a REST server based on netty 4. The client handler looks something like the following.

The bytebuffer capacity in the msg provided by netty varies. When the client message is larger than the buffer the message gets split. What I find is that both channelRead and ChannelReadComplete get called for each fragment. What I usually see is that the ByteBuf is around 512, and the message around 600. I get a channelRead for the first 512 bytes, followed by a ChannelReadComplete for them, and then another channelRead for the remaining 100 bytes and a channelReadComplete for them - 2 messages instead of 1.

I found a few related questions here, but I am wondering what is the point of channelReadComplete? Is it really called after every channelRead? As long as there are bytes available, shouldn't they be read in before channelReadComplete is called?

public class ClientHandler extends ChannelInboundHandlerAdapter {
    ....
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Report.debug("Read from client");
        ByteBuf buf = (ByteBuf) msg;
        String contents = buf.toString(io.netty.util.CharsetUtil.US_ASCII);
        ReferenceCountUtil.release(msg);

        ClientConnection client = ClientConnection.get(ctx);
        if (client != null) {
            client.messageText(contents);   // adds text to buffer
            return;
        }
        ((parse serial number from contents, process registration))
        ClientConnection.online(serialNumber, ctx);     // register success, create the client object
    }

    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ClientConnection client = ClientConnection.get(ctx);
        if (client == null) 
            Report.debug("completed read of message from unregistered client");
        else {
            Report.debug("completed read of message from client " + client.serialNumber());
            String contents = client.messageText();
            ... ((process message))
        }
    }
 }

回答1:

channelReadComplete is NOT called after each channelRead. The netty event loop will read from NIO socket and fire multiple channelRead until no more data to read or it should give up, then channelReadComplete is fired.



回答2:

Yes, channelReadComplete() is called after each channelRead() in the pipeline has finished. If an exception occurs in channelRead() then it will jump to the method ecxeptionCaught().

So you should put code into channelReadComplete() that you only want to have executed on a successful channelRead().

For example this is what our project does:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // compute msg
    ctx.fireChannelRead(msg); //tells the next handler 
                              //in pipeline (if existing) to read the channel
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    ctx.writeAndFlush("OK");
    ctx.fireChannelReadComplete();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    logger.error(Message.RCV_ERROR, cause.getMessage());
    ctx.writeAndFlush(cause.getMessage());
    ctx.close();
}

If the Client receives something different than "OK" then he doesn't have to send the rest. If you're looking for a method that gets called after all packages have arrived then:

@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    //close the writer that wrote the message to file (for example)
}

EDIT: You could also try sending bigger packages. The message size is controlled by the client, I think.



标签: java netty