quiche icon indicating copy to clipboard operation
quiche copied to clipboard

[Question] Is it possible to receive and send data on one QUIC connection concurrently

Open mickel8 opened this issue 3 years ago • 3 comments

Let's assume scenario in which client want to upload some file to the server and download some other file from the server in the same time. Is it possible to do so on one QUIC connection? I mean quiche::Connection is wrapped in Pin pointer so it's impossible to create two threads one that will read from UDP socket and will pass data to the Connection and another that will generate outgoing QUIC packets and will send them over UDP socket. Or at least it's not trivial as I didn't manage to do it using Arc, Rc or RefCell pointers.

I can for sure create two separate threads for one UDP socket so that one thread will read from UDP socket, the other thread will send over UDP socket but in the main thread I will have to periodically check rx end of channel to receive data that comes from thread that receives data from UDP socket. Is there a better way to do concurrent sending and receiving over one QUIC connection?

mickel8 avatar Jun 22 '21 18:06 mickel8

The protocol itself does not prevent it. You can have client/server initiated, uni/bi-directional streams within the same connection. Why not use multiple streams for this? This way you can multiplex both requests.

su225 avatar Aug 25 '21 17:08 su225

Good point but I want to do this in parallel so that sending and receiving will happen in the same time

mickel8 avatar Aug 25 '21 18:08 mickel8

Usually, download is where the remote would be sending lots of data and upload is where the local would be sending lots of data right? You can make use of this fact and use a single thread and do the following (of course, subject to congestion control limits. If you're server, then sometimes anti-amplification limits as well)

       {[STREAM-1|DOWNLOAD_REQUEST] [STREAM-2|UPLOAD_DATA,chunk_id=1]} ---->
<----- {[STREAM-1|DOWNLOAD_DATA,chunk_id=1] [ACK]}
       {[STREAM-2|UPLOAD_DATA,chunk_id=2] [ACK]}----->
<----- {[STREAM-1|DOWNLOAD_DATA,chunk_id=2] [ACK]}

Here the ACK is for ACKing the packets previously sent. Notice that upload and download are happening at the same time. Subject to limits note that the following is also valid

       {#1 [STREAM-1|DOWNLOAD_REQUEST] [STREAM-2|UPLOAD_DATA,chunk_id=1]} ---->
       {#2 [STREAM-2|UPLOAD_DATA,chunk_id=2]} ---->
       {#3 [STREAM-2|UPLOAD_DATA,chunk_id=3]} ---->
<----- {#1 [STREAM-1|DOWNLOAD_DATA,chunk_id=1] [ACK|#3]}
<----- {#2 [STREAM-1|DOWNLOAD_DATA,chunk_id=2]}
       {#4 [STREAM-2|UPLOAD_DATA,chunk_id=3,FIN=1] [ACK|#2]} ---->
<----- {#3 [STREAM-1|DOWNLOAD_DATA,chunk_id=3] [ACK|#4]}
<----- {#4 [STREAM-1|DOWNLOAD_DATA,chunk_id=4]}
.....

Note that sending and receiving are happening independently. Although at QUIC layer they are processed in the same thread, neither of them are blocked by the other (even when there are losses) due to QUIC stream semantics.

Once the data is delivered, you have to perform this in higher layers. The QUIC layer delivers the data and your higher layer buffers it to be consumed by downloader_thread where as uploader_thread will be calling send_data() handing over the buffer.

Not quiche expert, but just my guess based on my QUIC knowledge (so forgive me if I'm horribly wrong) - using multiple threads per connection in QUIC transport layer makes many things complicated. The received packets could be in multiple threads and then comes the question of who will process? You suggest one possibility where one is for sending and another one is for receiving. However, you now have shared state between threads (one example is anti-amplication limit which depends on the amount of data sent by the client) - which means, lock and synchronization headache on every send an receive. In general, this is performance killer and also makes programming difficult.

su225 avatar Sep 01 '21 15:09 su225