I’ve been looking at two examples in
tokio-core related to futures & UDP as well as the tutorial that Alex Crichton wrote (https://github.com/alexcrichton/futures-rs/blob/master/TUTORIAL.md). He’s done quite a lot and I keep seeing his name pop up, much praise to him. I’m still confused a bit confused as to how futures operate in the context of a larger application. I think this is partly due to the fact that I haven’t heard of futures until Rust, and I am still very new to Rust.
From what I understand, tokio builds on top of the futures crate to handle asynchronous I/O for protocols (tokio==mio+futures). Okay, I could have pulled that definition off of any website. Well then, what does this mean to me? I need to receive a response (or fire off a request which has its own response), but I don’t want to sit there waiting for it. Ideally I’d like to go off and do other things. In essence, I need to process something that will arrive in the future, and I am not ready now (or rather it is not ready).
So specific to my problem, I think this means I would use tokio to set up a future for a “UDP handler” which could send and receive udp packets, populating and pulling from queues as needed.
So far, I can only really see the receiving aspect via
poll(). It seems to me that
poll() would be the perfect spot to listen for new packets on a socket. I gleamed this from the echo-udp.rs example in
tokio-core. What I don’t understand is how this same socket could be used to send data to a destination. It looks like accessing that socket can only occur within the context of
poll(), but I do not want to send data there, only receive it. I do understand that poll, rather the
Server future owns the socket, so yes I can only access it from
poll(). I considered perhaps I need two futures, one for sending and receiving, but I threw that idea out as I don’t see why/how the sender would need to poll.
The other example I saw was the udp-codec.rs example in
tokio-core. This sets up a
UpdCodec which is then split into its sink (recv) and stream (send) halves. I am able to follow how and why we define our codec, but the rest is lost on me once it’s split into halves. It looks like this future already knows what to send and receive, and is expected to return because of that. Are futures not supposed to be kept alive, meaning once I bind a socket and tie it to a future which receives, am I expected to continually create a new “receiving” future after each one has been consumed?
Finally in both of the examples above,
udp-codec.rs, the tasks are actually run when we give it to the Core trait
Core::run(...). This doesn’t return until the future completes, meaning we’re stuck there. It looks like the future dies once
run returns, meaning anything associated with that future would be inaccessible after it runs and returns. All the examples I looked at do not perform any addition steps after the future is run. Furthermore, the examples I saw all tied sending and receiving to each other on localhost, not a foreign entity (ie: send and receive to ‘myself’ which are joined to each other). Are we supposed to create futures in their own threads so we can do other things while the future is running?
Ultimately, I am trying to port a UDP library which implements reliability, congestion avoidance, among a few other things. When it was first suggested to me to use futures to accomplish my goal, I envisioned that they would help with the tx/rx at the lower layer, where higher layers would would handle the algorithmic aspects of the library. Is my topography skewed on how this should be layered?
I know I’m pretty lost right now and look like I need some hand-holding. Any help would be appreciated. Thanks.