Everyone knows that the Internet is a series of tubes, but until a few weeks ago, I had never really thought about how the whole journey from computer A to B unfolds, on a low level.
For google.com to travel from a remote server to my laptop, an ungodly amount of technical wizardry unfolds. The beauty of the system is that you as a programmer don’t have to think about any of it, because you sit atop layers of clever abstractions.
There are four key layers, starting from the bottom one that physically decodes radiowaves and voltages into 1s and 0s, all the way up to the application layer, where you can request a file by name and expect it to appear, complete and uncorrupted. As incoming data moves up the layer chain, it evolves from chunks of (out of order) bits into readable text. Data you send out likewise devolves from a file into thousands of radio frequencies that might or might not reach their destination.
The layer many of us have heard of, TCP/IP, lives at layer 3. TCP is more of an idea than a hard technology. It’s essentially a set of algorithms that guarantees your data will arrive in order, have minimal errors, and arrive at a reasonable rate. So when I download song, it looks like I receive a contiguous file of mp3 data, but TCP is actually combining lots of random packets (and requesting those that didn’t make it through the Internet), before presenting me with a glued-together final version. If you agree that this process is fascinating, read on to learn how TCP does its thing.
The first thing you need guaranteed is that all your requested data will arrive. TCP is an acknowledgement-based protocol, meaning that when a client receives a packet, it sends back an acknowledgment (called an ACK) to the server for that packet. Once the server receives that ACK, it can send the next sequential packet, and so on.
It’s actually a touch more complicated than that, but not much. Because it would be inefficient to wait a full round-trip-time for each packet, the server sends a series of packets at once. When the client receives them, it sends back an ACK for the highest-numbered packet, thereby confirming it received the packet it’s acknowledging as well as all packets with a lower sequence number. This way a single ACK can acknowledge many packets. (This is part of the sliding window protocol.)
Data can get lost on the wire, and so can ACKs. If an ACK goes missing en route from a client to a server, how does the server ever know it can send more packets? The server uses timeouts to solve this problem. After sending a packet, it starts a timer. If it doesn’t receive an ACK for the packets it just sent within x milliseconds, it simply re-sends them. Likewise, if the client hasn’t received a packet in a while, a timeout expires and it re-sends the last ACK it dispatched, in an attempt to keep the process moving.
One component I found pretty fascinating is error correction in the context of data transmission. When a computer receives a string of data, say 100110111, who’s to say that a bit here or there didn’t become corrupted in transit? All it would take is a stray voltage or interference to flip a charge from a 0 to a 1.
There are a number of ways to approach this problem, and they’re not specific to TCP. In fact, most error correction happens in layer 2, whereas TCP most often lives in layer 3. The first way you could ensure correctness is to send multiple copies of the data in a packet. So instead of sending:
I would instead send:
100110111 100110111 100110111
When the client receives the data, it can compare the 3 copies. If they’re different, something became corrupted in transit, and it can either re-request the packet or attempt to correct the error itself.
Of course this is terribly inefficient; imagine having to download a file 3 times before you could use it. A better and much more widely used approach is the checksum method. The basic idea behind a checksum is to add all the bits of a packet together, then stash the result in the packet header. When the client receives the data, it also adds the packet’s bits together, and if its sum is different than the server’s sum saved in the header, you know some of the bits got currupted in transit. This is a simplified explanation; in reality there are dozens of different checksum algorithms, most of which do additional logic, like take the 2’s complement of the result. The main point is that the entire packet gets condensed to a small checksum field, which is a much quicker and more efficient way of detecting errors than sending multiple copies of the actual data.
That’s a brief foray into TCP, but of course that’s just scratching the service of the 4-layer Internet model. There are fascinating algorithms below it that solve interesting problems, like how to split a long string of bits into the sender’s intended frame, or how to share a physical transmission medium among many computers trying to broadcast. Check back soon for write ups on these other layers.