DotNetty
DotNetty copied to clipboard
MQTT PUBLISH packet encoder splits the topic and payload into different frames
The Mqtt encoder splits the PUBLISH packet into different frames, one containing the topic and QoS information, the other containing an optional payload data.
This causes the downstream socket implementation to send them as different different packets. Since the netty implementation encodes the entire PUBLISH packet into the same byte buffer, is there a particular reason dotnetty chose to implement it this way?
Related issue: https://github.com/Azure/azure-iot-sdk-csharp/issues/1814
In order for a downstream library to not send multiple TCP packets for the same PUBLISH packet, there are three options that I can think of:
- The DotNetty implementation could be updated to not encode the PUBLISH packet into two separate buffers - one for the payload and another for topic information.
- Compare
ChannelOutboundBuffer.TotalPendingWriteBytes
property to((IByteBuffer)ChannelOutboundBuffer.Current).GetIoBuffer().Count
and use that to validate if this is the terminal byte buffer being sent for the current packet.
- if true, the current byte buffer being sent is the terminal byte buffer being sent, so call
ClientWebSocket.SendAsync()
withendOfMessage
marked as true. - if false, the current byte buffer being sent is not the terminal byte buffer being sent, so call
ClientWebSocket.SendAsync()
withendOfMessage
marked as false.
- Have DotNetty expose a dedicated property to indicate if the current byte buffer being sent is terminal, or if there are more pending bytes.
@nayato Do you think option 2 is a logical solution for this issue? Can we rely on ChannelOutboundBuffer.TotalPendingWriteBytes
to infer if the current message is the terminal message for the current packet?
Going with option 2 you're still going to have multiple WS data frames on the wire. It's just that not every one of them will be marked with FIN flag. That said current behavior is compliant with MQTT over WS spec ([MQTT-6.0.0-2]).
I assume the ask is to not split messages in the first place where possible. I'd suggest to modify DoWrite
implementation to consolidate buffers accumulated in channelOutboundBuffer
before calling SendAsync
.
On option 1: The issue at hand is specific to channel implementation for integration with WebSocket in SDK. Writing out messages in a single buffer would result in an additional copy of message payload - a high cost operation.