klingtnet / rosc

An OSC library for Rust.
Apache License 2.0
173 stars 25 forks source link

help with parsing fragmented packets? #41

Closed flowb closed 1 year ago

flowb commented 1 year ago

Hi,

I'm a rust novice. 🦀🦀🦀 With a background in generative/interactive art.

I've been using the nannou library to explore this kickass language, and recently made up a personal learning project involving sending OSC data from TouchDesigner to a nannou app.

I started encountering an issue whereby the total channels i sent seem to be truncated based on packet size limitations. After some digging on the internet, I'm sort of coming to understand that the issue I'm having may be that I'm handling this as discrete packets rather than streams.

can you provide any guidance on how to handle this scenario using this crate?

thank you for all your fine work :)

klingtnet commented 1 year ago

Hi @flowb !

that I'm handling this as discrete packets rather than streams.

I guess that you're using UDP as the underlying transport, as shown in the sender example. In this case your OSC message size will be limited to the datagram size.

However, since the OSC protocol, as well as rosc, are transport agnostic you could choose any transport protocol to get your message over the wire. UDP is a packet based protocol whereas TCP is about a streaming of packets. Basically speaking, with a TCP socket you can send messages of any size and TCP will make sure that it will arrive correctly at the receiver side. I can't show you an example, but rosc.encode will just return a vector of bytes, so you could just send this over a TCP socket.

Please let me know if you've further questions. Also, if you found a solution I'd be happy to receive another usage example in a PR.

klingtnet commented 1 year ago

thank you for all your fine work :)

Oh, and thank you for the 💐 😊

flowb commented 1 year ago

Hi @klingtnet

Thanks for getting back to me so quick.

TouchDesigner uses UDP exclusively for OSC, so unless I were to generate the packets in another way, I don't think that TCP is an option there.

With regards to the data-payload limitation being implicit to using UDP here, I'm a bit mystefied. TouchDesigner can send and receive the additional channels when they are sent over UDP. Just to make sure it isn't some secret sauce that derivative cooked up, i also tested it with hexler's TouchOSC. In both cases it seems that they receiving application is able to reassemble the multi-packet data into a single bundle and then extract channels from it.

I may be over-thinking it, but perhaps the applications gather all packets with matching osc-timestamps into a big bucket and then process those in unison somehow?

klingtnet commented 1 year ago

TouchDesigner uses UDP exclusively for OSC, so unless I were to generate the packets in another way, I don't think that TCP is an option there.

That's true, then TCP is not an option.

I may be over-thinking it, but perhaps the applications gather all packets with matching osc-timestamps into a big bucket and then process those in unison somehow?

Just an idea, but wireshark can decode OSC messages and you could use that inspect what is being send by TouchDesigner and TouchOSC?

flowb commented 1 year ago

Hi,

So I honestly sort of figured this is where this was heading. I finally just sat down and ran a cap session, and the plot thickens a bit...

Wireshark initially seems to be receiving all channels in a single packet, however, further inspection shows that the packet shown in Wireshark to contain all the data is actually the product of fragmented packets that have been reassembled.

The process underlying it is ip4 packet fragmentation, which is something that is supposed to be handled at the IP layer. I don't know if the socket object in rust is somehow interfering this this process, or if the socket needs to be set up in a different way to properly reassemble the packets.

My next step on this journey will be to look into Ip framentation in rust sockets, unless this is already known to you.

Screenshot 2022-12-03 at 15 36 01

edit: I'm going to see if maybe this is just down to the buffer in the example being sized to the MTU. Perhaps creating a larger buffer to read the data into lets more channels through.

klingtnet commented 1 year ago

The process underlying it is ip4 packet fragmentation, which is something that is supposed to be handled at the IP layer. I don't know if the socket object in rust is somehow interfering this this process, or if the socket needs to be set up in a different way to properly reassemble the packets.

As said, fragmentation is handled entirely on the IP level and the UDP socket should not know about this.

Perhaps creating a larger buffer to read the data into lets more channels through.

That would have been my suggestion as well. This SO post explains some limitations of UDP packet sizes

flowb commented 1 year ago

well, simply setting an arbitrarily larger value for the buffer size seems to be the fix I'm looking for.

is there a responsible way to dynamically set this or do I, as the programmer implementing the library, just need to do my due diligence and manage my own memory footprint?

thank you for answering all my questions btw. :)

klingtnet commented 1 year ago

well, simply setting an arbitrarily larger value for the buffer size seems to be the fix I'm looking for.

Happy to hear that it has worked :)

is there a responsible way to dynamically set this or do I, as the programmer implementing the library, just need to do my due diligence and manage my own memory footprint?

On the sender side it's easy since you know upfront the size of the OSC message you want to send. It's more tricky on the receiver end, since they need to set a reasonable buffer size that might fit all possible messages. I think UDP is commonly chosen as a OSC transport because it's low overhead and has lower latency then for example TCP where first a connection is set up before any message is send.

thank you for answering all my questions btw. :)

Thank you for using rosc :)