A couple commenters have disagreed with the author saying “That’s one reason for the TCP three-way handshake”. But I’ve been implementing uTP, the UDP protocol that torrents use (https://www.bittorrent.org/beps/bep_0029.html) and it seems to avoid amplification attacks thanks to a three-way handshake.
So the author seems correct to say it’s one (good) reason for the three-way handshake.
(It turns out when you want to spider libgen for all epubs for training data, the most straightforward way to do this, apart from downloading >40TB, is to write your own torrent client that selectively recognizes epub files and fetches only those.)
The uTP spec (BEP 29, linked above) also gives a good overview of why UDP is sometimes the correct choice over TCP in modern times. uTP automatically throttles itself to yield excess bandwidth to any TCP connection that wants it. Imagine trying to write an app that uses all the bandwidth on your network, without impacting anyone else on the network. You’d find it quite hard. uTP does it by embedding time deltas in the packets and throttling itself whenever the timestamp goes over ~100ms, which indicates either a connection dropout or bandwidth saturation.
I.e. if your ping suddenly spikes, it’s because someone is hogging all the bandwidth. Normally you have to track down who’s doing it, like a detective hunting a murderer. But uTP knows that it’s the murderer, so it throttles itself back. Presto, perfect bandwidth utilization.
But why bother with UDP? Why can’t you do this with a TCP connection? Just measure the time deltas and throttle yourself if it spikes, right? Good question, and I don’t have a good answer. Perhaps one of you can give a persuasive one, lest you agree that ISPs should just throttle UDP by design.
It’s certainly simpler to solve this at a protocol level, but one could imagine a BEP that adds time deltas to the torrent spec and prevents sending pieces too quickly (“if the deltas spike, send fewer blocks per second"). It might even be simpler than bothering to reimplement TCP over UDP. But perhaps there’s a good reason.
One idea that comes to mind is that the goal is to throttle sends and receives. You control your sends, but you’re at the mercy of the counterparty for receives. You’d need to keep throttle info for every peer, and notice when their pings spike, not just yours. Then you’d send fewer packets. But that’s what uTP does, and that doesn’t answer “Why do it in UDP instead of TCP?”
The reason for uTP using UDP is clear. When you are sending a complete file from one computer to another, you can send it in any order. With TCP the packets need to arrive in order (at least within the sliding window) or they get rejected which is wasteful.
Another reason is the custom flow control. The uTP protocol uses a scheme called LEDBAT (RFC 6817) which is a high bandwidth, high latency "scavenger" protocol that uses all the available bandwidth but tries to take a lower priority than TCP connections. This allows your web browsing to be smooth while your OS updates or Torrents are chugging along in the background. Microsoft and Apple both use a LEDBAT based UDP protocol for updates.
On the other side of the spectrum there are video games and audio/video live streams. They use UDP protocols but with a flow control that minimizes latency at the expense of bandwidth. The polar opposite of what uTP/LEDBAT does.
With TCP you get the flow control algorithm of the protocol. While there is a little configurability in the kernel, you can't adjust it from the application.
UDP protocols also need a handshake not unlike TCP three way, but you can include payload bytes or crypto key exchange in those packets to avoid TCP+TLS "six way handshake".
The LEDBAT flow control scheme is absolutely brilliant, using just some simple timestamps to evaluate network congestion. If you're looking for a nice Saturday geek reading, RFC 6817 is a good candidate.
Latency due to head-of-line blocking is a big issue with TCP. Any packet loss causes a backup of all other data while waiting for retransmit. Window negotiation would make your control spotty because you’d be layering two different/incompatible flow mechanisms
> But why bother with UDP? Why can’t you do this with a TCP connection? Just measure the time deltas and throttle yourself if it spikes, right? Good question, and I don’t have a good answer.
Well if you're not using no-delay you are not really on control of the actual TCP sending rate, the kernel might assemble or split the buffer you're send()ing.
Head-of-line blocking has nothing to do with Nagle (default delay behavior, that is disabled by every single TCP application I have ever seen). Head of line blocking is stochastic pile-up of data that happens during packet loss
I was answering the specific question of GP: "Why can’t you do this with a TCP connection? Just measure the time deltas and throttle yourself if it spikes, right" which wasn't limited to head of line blocking but more precise handling of transfer-rate of packets. Still, TCP is controlled by receiver so if you have a have on both hides you might be able to do what GP suggests, but you'd still pay the jitter of sender buffer and the noise of fragmentation of your messages.
Which might be a good thing if you're sending a bunch of tiny little packets to a consumer on wifi (cough lots of golang projects) instead of assembling/splitting the buffer yourself.
It uses UDP instead of TCP because it does not care about in-order delivery of the data, only that it eventually has all of the data.
Fragmenting and coalescing the data happens at a higher layer, so there is no reason to duplicate that effort at the transport layer (which is needed to provide a efficient in-order stream abstraction).
TCP (and other protocols) presenting a in-order stream abstraction is beneficial in a lot of ways, but it impacts the design and guarantees you can make in a way that is harmful to a application that only cares about eventual completion.
> But why bother with UDP? Why can’t you do this with a TCP connection? Just measure the time deltas and throttle yourself if it spikes, right? Good question, and I don’t have a good answer. Perhaps one of you can give a persuasive one, lest you agree that ISPs should just throttle UDP by design.
The issue is microsoft windows. It doesn't have pluggable congestion controllers. On linux you could insmod a ledbat.ko and have userspace applications choose that as congestion controller for their TCP connections.
Another reason for uTP is that nat traversal is easier with UDP than it is with TCP. Especially if the NAT gateways use endpoint-idependent mappings for UDP but address-dependent mappings for TCP.
But that's more an argument for IPv6 than for UDP.
> One idea that comes to mind is that the goal is to throttle sends and receives. You control your sends, but you’re at the mercy of the counterparty for receives. You’d need to keep throttle info for every peer, and notice when their pings spike, not just yours. Then you’d send fewer packets. But that’s what uTP does, and that doesn’t answer “Why do it in UDP instead of TCP?”
You can do this in bittorrent actually by throttling your request messages. But request pacing is tricky because userspace applications will have a difficult time estimating the receive-bottleneck-buffering across multiple TCP streams. Though maybe it could be done on linux via socket timestamping, I haven't looked into that.
BiglyBT implements a crude version of request throttling, but it's not adaptive, you have to set a download rate limit. But when it does kick in it manages to converge its requests on a few high-bandwidth/low-latency peers which should result in less long-distance traffic and shallower queues.
I suspect video games use UDP for the same reasons bidirectional real-time communications protocols (WebRTC, VoIP, etc) use them.
Generally speaking when you have an interactive session with > 1 parties (especially humans) when/if packet loss or out of order packets occur there's no reason to attempt re-transmission because the clock, people, and application continually move forward in time. Consider a VoIP call - by the time a packet is retransmitted the conversation has moved on. Most audio codecs and implementations have a default packetization interval of 20ms of audio per packet so this is pretty granular.
This has been mitigated somewhat with audio codecs like Opus that have packet loss concealment and forward error correction techniques that provide relatively high robustness to this issue. Variable latency (jitter) and out-of-order packets are typically handled by dynamic jitter buffers on each receiving side that can detect when the finely-clocked streams are wobbling. They then buffer incoming frames, potentially with re-ordering, and play them out smoothly with the side-effect being slightly higher latency to perform this (that's why they're dynamic).
This is right observation but not so right reasoning.
Loosing a few pixels or missing some object updates won't be noticable in high fps games, but where correctness and completeness of data is needed, UDP based applications end up implementing TCP features in application layer.
These application layer implementations can be more performant (low latency and/or high throughput) with UDP, utilizing the special cases, than TCP, which needs to provide generic facilities to every application running on the same host. And then it becomes a question of available resources vs benefits.
Example:
DNS started as UDP and later used TCP when expectations on correctness grew.
That said, We have high capacity and high quality backbones that UDP looks right choice for many cases, and should be used where it makes sense.
DNS supports TCP largely because of packet fragmentation issues with UDP. I don't have a lot of experience with it but from what I understand DNS attempts to switch to TCP (with varying degrees of success by implementation) when the size of the response exceeds or is expected to exceed the MTU.
Video games have a very good reason to use UDP: latency and lack of head of the line blocking. This also applies to video and audio live streams.
But it's not the only reason to use UDP. It can open connections faster, accept out of order packets and generally offers a low overhead datagrams for building custom protocols.
TCP's throttling mechanisms are quite coarse compared to what is described above, and they don't measure packet latency, only packet loss (after suitable timeout).
Yes, TCP has flow control and congestion avoidance.
But it's a one size fits all model designed for unreliable connections of yesteryear. It's a trade off between latency and throughput and not particularly good at either when network conditions get bad.
UDP requires the application protocol to implement a flow control mechanism, but offers an opportunity to emphasize latency or throughput at the expense of the other.
So the author seems correct to say it’s one (good) reason for the three-way handshake.
(It turns out when you want to spider libgen for all epubs for training data, the most straightforward way to do this, apart from downloading >40TB, is to write your own torrent client that selectively recognizes epub files and fetches only those.)
The uTP spec (BEP 29, linked above) also gives a good overview of why UDP is sometimes the correct choice over TCP in modern times. uTP automatically throttles itself to yield excess bandwidth to any TCP connection that wants it. Imagine trying to write an app that uses all the bandwidth on your network, without impacting anyone else on the network. You’d find it quite hard. uTP does it by embedding time deltas in the packets and throttling itself whenever the timestamp goes over ~100ms, which indicates either a connection dropout or bandwidth saturation.
I.e. if your ping suddenly spikes, it’s because someone is hogging all the bandwidth. Normally you have to track down who’s doing it, like a detective hunting a murderer. But uTP knows that it’s the murderer, so it throttles itself back. Presto, perfect bandwidth utilization.
But why bother with UDP? Why can’t you do this with a TCP connection? Just measure the time deltas and throttle yourself if it spikes, right? Good question, and I don’t have a good answer. Perhaps one of you can give a persuasive one, lest you agree that ISPs should just throttle UDP by design.
It’s certainly simpler to solve this at a protocol level, but one could imagine a BEP that adds time deltas to the torrent spec and prevents sending pieces too quickly (“if the deltas spike, send fewer blocks per second"). It might even be simpler than bothering to reimplement TCP over UDP. But perhaps there’s a good reason.
One idea that comes to mind is that the goal is to throttle sends and receives. You control your sends, but you’re at the mercy of the counterparty for receives. You’d need to keep throttle info for every peer, and notice when their pings spike, not just yours. Then you’d send fewer packets. But that’s what uTP does, and that doesn’t answer “Why do it in UDP instead of TCP?”