-
Notifications
You must be signed in to change notification settings - Fork 657
WIP: Change Transport to allow for implementing partially reliable transport like QUIC #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
change handshake and connect to allow implementer to opt into providing a second unreliable unordered stream to carry the forwarded packets. rathole commands will still be sent over reliable ordered streams.
41866de to
8a4cf93
Compare
|
@rapiz1 I would need some feedback, in order to proceed. I am not yet sure whether the currently implemented approach is the best way to do it. |
|
@emillynge Oh. I thought we're focused on #98 first. I will take a look |
yujqiao
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if I miss something, but this doesn't seem right 🤔
| run_data_channel_for_tcp::<T, T::ReliableStream>(reliable, &args.local_addr).await?; | ||
| } | ||
| TransportStream::PartiallyReliable(_, unreliable) => { | ||
| run_data_channel_for_tcp::<T, T::UnreliableStream>(unreliable, &args.local_addr).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem right. After reading quinn::Datagrams, I see it's really just datagram, just like what UDP provides. And your AsyncWrite wrapper simply wrap poll_write to send a datagram. But when you do the forwarding for a TCP service, copy_bidirectional is called and byte stream is forwarded, in which every byte is from the application layer, not a TCP packet, and should be guaranteed to be sent to the destination, while with T::UnreliableStream, it can be lost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I must have misunderstood what the rathole client<->server data channel was carrying.
If we are ACK'ing data from the end user, then we absolutely have to ensure that the packet does indeed arrive (reliability).
But what we can still try to take advantage of is the ability to send QUIC packet unordered.
| run_data_channel_for_udp::<T, T::ReliableStream>(reliable, &args.local_addr).await?; | ||
| } | ||
| TransportStream::PartiallyReliable(_, unreliable) => { | ||
| run_data_channel_for_udp::<T, T::UnreliableStream>(unreliable, &args.local_addr).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may also not work as expect. run_data_channel_for_udp call write_all to write packets. And write_all can call poll_write multiple times and there's no guarantee that a packet passed to write_all will not be fragmented and passed to poll_write at a whole, which means it's possible multiple quic datagrams are used to carry one UdpTraffic, and some of them are lost, causing the deserialization of UdpTraffic to fail.
|
To move this forward, I think
|
78f1157 to
97541af
Compare
This PR is meant as a way to discuss how the
Transporttrait could be changed such that protocols that are partially reliable can leverage their ability to send forwarded packets unordered and unreliably.I will repeat my thought from #64 (comment)
Benefits from forwarding packets unordered and unreliably
An ordered reliable transport, mans that:
Now, this is nice (almost necessary) for the control channel. But it is actually a problem for the data channel.
As an example, retransmission of TCP packets in our "tunnel" will make it seem as if there is 0 loss from point of view of the original sender, which will make it impossible for them to detect congestion. (see TCP-meltdown)
Also, having all packets be ordered will introduce head-of-line blocking in the transport itself. If we are carrying UDP, this unnecessarily chkoes bandwidth. If we are carrying TCP, then we now have 2 layers of HOL blocking, where just one should suffice.
Some protocols, such as QUIC allows us to choose where any packet should be sent reliably, or unreliably. We can use that to simplify control channel and command packet code (no need for manual retransmission etc), whilst having all the forwarded packets be carried by unreliable streams.
The main protocol I envision building atop this PR is QUIC. However, I would like the approch to be useful for a TCP/UDP combination, such as seen in FTP:
Approaches
The first approach in this PR is to introduce an
enum TransportStreamthat may either contain 1 or 2 streams depending on whether the protocol wishes (is capable) of providing an unreliable stream in addition to the reliable one that is must provide.An alternative would be to define a new "downgrade" method in the
Transporttrait that consumes the reliable stream into an unreliable one. This would likely suffice given that, as soon as we start the actual forwarding, no more commands needs to be sent reliable on the data channel. The control channel will keep needing a reliable connection, but it will simply never downgrade.Either way, I don't see how to do this without requiring all
Transportimplementers define anUnreliableStreamtype. Unless of course, we just have 1Transport::Streamtype can be put into either reliable or unreliable mode. Likely, that would entail some flag or state that must be checked every time a packet is received or sent which may incur a slight amount of unwanted overhead. Yet, it may well be the simplest solution.