"Elegant" message passing from single UDP socket to multiple tokio::tasks

Dear Rust community,

I have been programming Rust for almost 9 months. This is my first post ;).

Maybe you can help me with the following problem statement:

Via a single tokio::udp socket I am receiving messages, which are going to be distributed further on to several tokio::tasks.

To be more precise: Each message contains a single service, which is handled by exactly one of the spawned tokio::tasks. A Json defines all relevant services for the receiver.

I am now stuck which of Tokio's channel primitives I should use:

  • My first idea was to use tokio::sync::watch (reflecting a single-producer, multi-consumer). But after a closer look this is not fully appropriate for that scenario: Now every message from the socket is distributed to all consumers / tokio::tasks, although the message is only relevant for a single tokio::task. Also what happens if the network packets are received in a much higher frequency that the watch channel can distribute (slow receiver)?

  • My second approach was to just use a dedicated oneshot channel per service (and it's corresponding tokio::task). Whenever a message comes in via the socket, the service type is determined and the appropriate oneshot channel is selected. This would reflect the 1:1 communication more precisely, but now I have to create (potentially) a lot of channels. Isn't that too inefficient?

Are there any other solution?

Thanks in advance!

Greetings from Germany!

A oneshot channel? Do you only need to send one message per service?

Too little/vague info.
From all I read you may be better spawning a task for each received message. I have no idea what you intend a service to receiving a single message to be doing.

Thanks for answering.

Some clarifications: The "services", which I mentioned, will each get several messages back-to-back (not only a single message and then the service is done).

So a channel with a buffer is needed (definitely later on). I mentioned the tokio::oneshot, because it suited the 1:1 communication scenario and I thought for getting started the single value "capacity" is enough.

I would probably try to model your services using the actor pattern, spawning a task per service. The udp socket task can then store a list or hash map of actor handles that it can use to send messages to the services. I would use a tokio::sync::mpsc channel.

Thank you Alice!

Your article is perfect and solved my questions.

Thanks for the quick reply!