Which Conversation?
Mira’s chat program is the first version of Courtyard. It runs on her machine, at port 5222, and anyone in the courtyard can message it. You use it, Theo uses it, and it works. Then you open a second window.
You want a private conversation with Mira alongside the group one, so you open another chat window on your machine. Both windows connect to the same place, Mira’s machine at port 5222. Your operating system, as it learned to do in the last chapter, gives each window its own ephemeral port; the group window gets 51844 and the private one gets 51845. That makes two conversations, both to 5222, both from the same machine, on different source ports.
Now Mira types a reply in the private conversation, something she would rather Theo did not read. Her program calls the operating system and says: send this to the conversation on 5222. That is all it knows how to say, because so far a conversation has been identified by the port it is on. The operating system looks at what has arrived on 5222, which is bytes from you on 51844, bytes from you on 51845, and bytes from Theo on his own port, and it has to pick a return address. It picks the most recent sender, which was your group window. Mira’s private reply lands in the group window, and Theo reads it.
Let’s be precise about what broke. In the last chapter you named the program: 5222 means chat. But three separate conversations are arriving at 5222, and the program has no way to tell them apart and no way to reply to one without replying to the wrong one. The port names the door. It does not name who is standing at it.
Look at the three conversations from Mira’s side:
from 203.0.113.9 port 51844 → to 203.0.113.7 port 5222 (your group window)
from 203.0.113.9 port 51845 → to 203.0.113.7 port 5222 (your private window)
from 203.0.113.21 port 40122 → to 203.0.113.7 port 5222 (Theo)
The destination is the same in all three, which is why the destination alone cannot distinguish them. But no two rows are identical. Your two windows share a location and differ in port. You and Theo differ in location. Take all four values together, where from, which port from, where to, and which port to, and every conversation in the world that is currently happening has a unique combination. That is the name of a conversation. Not the destination port, but all four numbers.
Once the operating system keys on all four, the rest falls into place. Bytes arriving from 203.0.113.9:51845 go into a buffer that belongs to that conversation and no other. When Mira’s program replies on that conversation, the operating system already knows the return address, since it is the other half of the four-tuple, and the reply goes to 51845, your private window, and nowhere else.
The operating system’s conversation table. Each row is one four-tuple and one buffer. Incoming bytes are matched on all four fields and land in one row and no other.
But what does Mira’s program actually hold? In the last chapter it claimed a port. Now it needs to tell three conversations apart and write to one at a time, and we should ask what that looks like from inside the program. It looks like two different kinds of thing, and separating them is the point of this chapter.
The first kind is what the program had last chapter, the claim that it is the program for 5222. Call it the waiting handle. It represents a willingness to have conversations at that port. It does not have any conversations, and no bytes ever arrive through it. It is a door with a sign on it that says knock here.
The second kind is one conversation. When bytes arrive at 5222 from a four-tuple the operating system has not seen before, that is a knock, a new conversation beginning. The operating system creates a conversation handle for it, bound to that one four-tuple, and gives it to the program. Reading from that handle gets you only the bytes from that conversation. Writing to it sends only to that peer. The program never has to say where it is sending; the handle already knows.
So Mira’s program holds one waiting handle and, at the moment, three conversation handles. When you open a fourth window there will be four. When Theo disconnects there will be three again. The waiting handle never changes; it keeps producing conversation handles for as long as people keep knocking.
One waiting handle at 5222. Clients arrive, and each knock produces a new conversation handle bound to that client’s four-tuple. The waiting handle never carries data. The conversation handles carry nothing else.
These handles are called sockets. The waiting kind is a listening socket and the conversation kind is a connected socket. The operation a program performs on a listening socket to receive the next conversation handle is called accept, and a program that serves many people spends most of its life doing this and little else: accept, get a new socket, talk on it, accept again. The people who settled on this arrangement were the group at Berkeley who built the networking into their version of Unix in the early 1980s, Bill Joy foremost among them, and they were solving precisely Mira’s problem, one program that had to hold many conversations at once.
There are two things people get wrong about this for years, and you are now in a position to get both right. The first is the question of whether a server with a thousand clients uses a thousand ports. It does not. Every one of those thousand conversations is on port 5222 at the server’s end. What differs is the client’s half of the four-tuple, a thousand different locations, or a thousand different ports, or some mix of the two. There is one port, one listening socket, and a thousand connected sockets. The server’s port is the thing that is shared; the client’s location and port are what make each conversation unique.
The second is the question of whether a socket is a port. It is not. A port is a sixteen-bit number. A socket is a thing the operating system gives to a program, a handle with a buffer behind it. A listening socket is associated with a port. A connected socket is associated with a four-tuple, of which a port is one quarter. You can have thousands of sockets involving one port, and you could, in principle, have a port with no socket on it at all, since it is only a number that nobody has claimed.
Let’s put the whole thing together from the receiving end, because this is the procedure that runs on every machine for every message. Bytes arrive with a four-tuple. The operating system looks in its table of connected sockets for an exact match on all four fields. If it finds one, the bytes go into that socket’s buffer, and the program that owns it can read them whenever it likes.
If there is no exact match, this may be a new conversation. The operating system looks in its table of listening sockets for one on the destination port. If it finds one, it creates a new connected socket for this four-tuple, queues it up for the program to accept, and delivers the bytes there.
If there is no listening socket on that port either, nobody is home. The operating system sends back a short message saying so, a refusal, so that the sender does not wait forever for a reply that will never come. You have seen the other side of this: “connection refused” is your operating system reporting that it knocked on a door with nobody behind it. Three lookups, in order, the conversation, then the door, then the refusal, and that is all of it.
There is another way to do this, and it is worth knowing because you will meet it later. Instead of the operating system keeping a socket per conversation, you could keep one socket on 5222 and have every incoming message arrive tagged with where it came from. The program reads a message, sees that it was from 203.0.113.9:51845, and does whatever it wants with that. When it replies, it says explicitly: send this to 203.0.113.9:51845. There are no connected sockets and no accept. The program manages its own idea of conversations, if it wants one at all.
Courtyard now serves the whole courtyard correctly. Each person, and each window, is its own conversation. Mira’s program reads from one, writes to one, and the operating system makes sure nothing crosses. She can hold a private exchange with you while Theo talks in the group, and nobody reads anything not meant for them.
For eight chapters you have been looking at the tower from the bytes’ point of view, following a message down through the headers and up again. In the next chapter the camera turns around, and you look instead at what Mira’s program is doing during the long stretches when no bytes are arriving at all.
Coming up · I·9 — Waiting on Two ThingsMira’s chat program calls read on a conversation handle, and there is nothing there yet. What happens next is the shape of every server you will ever write.
→