frame: fix UDP datagram boundary recovery after parse errors#246
frame: fix UDP datagram boundary recovery after parse errors#246gavrilaf wants to merge 1 commit into
Conversation
On a packet-oriented transport (UDP), a single malformed datagram used to produce dozens of spurious "invalid magic byte" errors because bufio scanned the bad datagram byte-by-byte looking for the next magic byte. Add EndpointUDPClientPacket: identical to EndpointUDPClient but marks the channel as PacketOriented. After any ReadError, DiscardBuffered() drops all bytes still buffered from the current datagram so the next Read() begins on a fresh UDP packet boundary. Key changes: - pkg/frame: Reader.DiscardBuffered() discards remaining buffered bytes - pkg/frame: ReadWriter uses a 65536-byte buffer in PacketOriented mode so a single bufio fill always covers exactly one datagram - pkg/frame/v2_frame: unmarshal header with Peek+Discard instead of peekAndDiscard so an incompatibility-flag error leaves the header bytes in the buffer for DiscardBuffered to clean up - channel: call DiscardBuffered on ReadError when PacketOriented - channel_provider: detect packetOrientedEndpoint and set the flag - endpoint_udp_client_packet: new EndpointUDPClientPacket type
|
Hello, the idea is good but this PR contains too many unrelated things. I'm assuming this PR is intended to drop entire datagrams in case of a read error, as the title and description mention. The following things are unrelated:
This is meant to support decoding multiple Mavlink frames coalesced in the same UDP packet. Useful, but another topic.
This is the opposite of the aim of the PR: what's the meaning of leaving bytes in the read buffer while you are going to discard them later?
Why creating a whole new endpoint instead of using the existing one? We can support datagram-based parsing but in an orderly fashion. |
|
I've moved the recovery mechanism in #260. With respect to this patch, i also made sure that a Mavlink frame can never spawn into two UDP packets. Thanks for providing the original implementation. |
When the endpoint is datagram-based and there's an error, discard buffered data and skip directly to the next datagram.
When the endpoint is datagram-based and there's an error, discard buffered data and skip directly to the next datagram.
Problem
When using
EndpointUDPClientover a real UDP link (e.g. a radio-link tunnel), a single malformed or out-of-order datagram produces dozens of spuriousparse error invalid magic byteerrors:The root cause:
bufio.Readertreats the UDP connection as a byte stream. After a parse error it continues scanning byte-by-byte inside the same datagram, generating one error per remaining byte. The next datagram then starts mid-buffer, corrupting further frames.This does not affect TCP or serial endpoints because those are true byte streams where byte-by-byte scanning is the correct recovery strategy.
Solution
Add
EndpointUDPClientPacket— identical toEndpointUDPClientbut marks the channel as packet-oriented. After anyReadError,DiscardBuffered()drops all bytes still buffered from the current datagram so the nextRead()begins on a fresh UDP packet boundary.One bad datagram → exactly one
EventParseError→ normal parsing resumes.Changes
pkg/frame: addReader.DiscardBuffered()to flush the bufio bufferpkg/frame:ReadWriterallocates a 65536-byte buffer in packet-oriented mode so one bufio fill always covers exactly one datagrampkg/frame/v2_frame: unmarshal header withPeek+Discardinstead ofpeekAndDiscardso an incompatibility-flag error leaves header bytes in the buffer forDiscardBufferedto clean upchannel: callDiscardBuffered()onReadErrorwhenPacketOrientedchannel_provider: detectpacketOrientedEndpointinterface and set the flagendpoint_udp_client_packet: newEndpointUDPClientPacketendpoint typeTests
TestEndpointUDPClientPacketRecoverAfterMalformedDatagrams: sends 2 junk datagrams followed by a valid one — verifies exactly 2EventParseErrorevents and correct frame received afterTestEndpointUDPClientRecoverAfterMalformedDatagram: existing-style test inendpoint_client_test.goTestReaderRecoverAfterInvalidV2IncompatibilityFlag: unit test for thePeek+Discardheader change