pete > courses > CS 431 Spring 25 > lecture 13: TCP concepts
Lecture 13: TCP concepts
Goals
- interpret a ladder diagram
- explain the purpose and use of sequence numbers in TCP
- explain the purpose of the TCP window size
- explain the phenomenon, consequences, and remediation for congestion
recall that there are many problems IP does not solve
we solved one of them in UDP: the addition of port numbers allows us to send stuff (datagrams) to an individual process that has bound to a particular port on the receiving host
but several potential problems remain:
- UDP does not notice when datagrams don’t make it to their destination
- UDP does not maintain any notion of datagrams arriving out of order
- UDP does not attempt to re-send datagrams if the data gets corrupted along the way
I say "potential" because it’s possible the functionality we want to implement doesn’t suffer from these much
but there are a lot of applications that are sensitive to these problems, and so it makes sense to have a protocol that doesn’t have them
enter: the Transmission Control Protocol (TCP)
like UDP, TCP is built directly on top of IP
also like UDP, TCP includes a notion of ports (also using 16-bit values) that allow data to be addressed to individual processes
unlike UDP, TCP is a stream-oriented protocol
in UDP, which is a datagram-oriented protocol, each unit of communication is of finite size, the sender sends a datagram and the receiver receives that datagram
in TCP, the sender just sends a stream of bytes and the receiver receives that stream of bytes: the receiver does not know if/how the data it receives was divided up into smaller parts for sending
in UDP, if a datagram is sent but doesn’t make it to the receiver, the receiver doesn’t know (unless there is some data embedded in the datagram itself, by the sender, that the receiver can use to figure this out…)
in TCP, if a unit of data disappears, it will automatically be detected and re-sent, and the receiver won’t even know: it will just see the uninterrupted sequence of bytes
now, some terminology
in Ethernet, the data unit is a frame
in IP, it’s a packet
in UDP, it’s a datagram
in TCP, we’re going to use the term segment
so a TCP stream consists of a sequence of segments that contain the data being transmitted
because TCP attempts to handle many different circumstances, some of which are somewhat complicated, we need a good way to describe and think about the sending and receipt of segments
for that purpose, we will use ladder diagrams
in a ladder diagram, the entities involved (eg, hosts) are represented by vertical lines and frames/packets/segments sent between them are represented by arrows between those vertical lines
time starts at the top, and proceeds down, so the frame/packet/segment sent furthest in the past will be at the top of the diagram and the most recent will be at the bottom
example:
Process A | segment 1 | Process B | ------------> | | | | segment 2 | | <------------ | | | | segment 3 | | <------------ | | | | segment 4 | | ------------> | | |
this diagram represents the situation wherein Process A sends segment 1 to Process B
then Process B sends back segment 2, followed by segment 3
and finally Process A sends segment 4 back to process B
using ladder diagrams, we can illustrate the situations that we want TCP to identify and fix for us
a segment is lost:
Process A | segment 1 | Process B | ------------> | | | | segment 2 | | ------> | | | | segment 3 | | ------------> | | |
a segment arrives out of order:
Process A | segment 1 | Process B | ------+ | | | | | segment 2 | | ------------> | | | | | +-----> |
the "segment is corrupted during transmission" case is handled by more checksums, where the segment is discarded if the checksum fails, and therefore operates just like a lost segment
how can we know if a segment is lost or arrives out of order?
the simple answer is that every segment sent includes some notion of its place in the sequence of all segments
so, just as I’ve labeled the segments with monotonically-increasing numbers in all the diagrams above, the TCP metadata should include similar information
in the "segment is lost" ladder diagram above, when it receives segment 3, Process B says "hold on, I just received segment 1 and now I’m receiving segment 3, therefore segment 2 must have disappeared!"
the "segment arrives out of order" ladder diagram represents a similar situation: process B receives segment 2 first and concludes that it missed segment 1
the difference in this latter case is that segment 1 magically shows up shortly thereafter, whereas in the former case the missing segment does not
which means that, in the former case, Process B needs some way to tell Process A to re-send segment 2
alternatively, when it receives a segment, Process B could send back to Process A a message saying "I just received segment 1"
if Process A notices a gap in the acknowledgements, it can choose to re-send segments
for example: if Process A receives a segment from Process B saying "I just received segment 1" followed by another segment from Process B saying "I just received segment 3", then Process A could conclude that segment 2 was lost in transit, and choose to re-send just that segment
all of this leads to two medium-level ideas:
segments contain sequence numbers that identify where the segment payload belongs in the overall stream of data
when it receives a segment, the recipient sends back an acknowledgement that indicates which segments it has received
so the conversation is going to look (conceptually) more like this:
Process A | segment 1 | Process B | ------------> | | | | ack segment 1 | | <------------ | | | | segment 2 | | ------------> | | | | ack segment 2 | | <------------ | | | | segment 3 | | ------------> | | | | ack segment 3 | | <------------ | | |
the above diagram shows segments containing data flowing from A to B, and acknowledgements flowing from B to A
but TCP allows data to travel both directions, which means acknowledgements have to be able to travel in both directions as well
since one end of the connection may be way more talkative than the other, this leads to the idea that both directions use separate sets of sequence numbers
that is, segment 1 sent from A to B has no relation at all to segment 1 send from B to A
if A is really chatty, it could have sent segments 1 through 1000 before B even sends a segment 1 in the opposite direction
what happens if a segment doesn’t arrive? how does the system even know if a segment didn’t arrive?
Process A | segment 1 | Process B | ------------> | | | | ack segment 1 | | <------------ | | | | segment 2 | | ----> X | | | | segment 3 | | ------------> | | | | ack segment 3 | | <------------ | | |
in this example, Process B first notes and acknowledges the arrival of segment 1
and the next thing that arrives is segment 3, therefore it knows that segment 2 was lost
at which point it makes sense for Process B to ask Process A to re-send segment 2, the exact details of which we will deal with later, when we see exactly how these sequence numbers are realized
what if the very first segment is lost, though?
Process B doesn’t know it was supposed to receive anything at all, so it doesn’t know it should ask Process A to re-send
relatedly, what if Process A sends a segment but Process B doesn’t even exist?
it probably seems silly to allow Process A to send data before even verifying that anybody is around to receive it
and indeed this leads us to the idea of establishing a connection before actually sending application-level data over it
if we think back to the system calls that establish TCP connections, there are two: a client process uses connect(2) to establish a connection with a server process and a server process uses accept(2) to accept an incoming connection request from a client
the lower-level implication of these two syscalls is that connect(2) MUST NOT return until the server has confirmed creation of the connection and, similarly, accept(2) MUST NOT return until the client has confirmed creation of the connection
(this latter point may sound trivial, but it has consequences, which we will see soon)
conceptually, then, before sending any data, the two ends of the connection must agree that a connection exists between themselves and also inform the opposite end what sequence numbers to expect
thus, every TCP connection begins with an exchange of three packets
this is called the "TCP Three-Step Handshake"
remember that the purpose of accept(2) is to passively create connections: that is, to wait until somebody else comes knocking
on the opposite end, the purpose of connect(2) is to actively create a connection: it does that knocking
so if process B has called accept(2) and is awaiting an incoming connection request…
and process A calls connect(2) to create a connection to process B…
- Process A sends a packet that says "I’d like to connect, here is the sequence number I’m going to use for sending data to you (ie, A to B)"
- Process B sends back a packet that says "I acknowledge that connection request and the sequence number for data you’re sending to me (A to B) AND here is the sequence number I’m going to use to send data back to you (B to A)"
- Process A sends a final packet back to process B that says "I acknowledge the sequence number for data from B to A"
in ladder diagram form ("ISN" means "initial sequence number" and "ACK" means "acknowledge"):
Process A | | Process B | | | | accept(2) | | | ISN A to B | connect() | ----------------------------> | | | | ACK(ISN A to B), ISN B to A | | <---------------------------- | | | | ACK(ISN B to A) | | ----------------------------> | | |
after having exchanged these three packets, both A and B know several things:
- they both know the sequence number used to send data from A to B
- they both know the sequence number used to send data from B to A
- they both know that the other knows these things, too
next time, we will look at how all this stuff is realized on the wire
because the details are, predictably, a bit more subtle and complex than I’ve presented today