Transport layer: UDP
We drop down one layer, from the messages applications exchange (Layer 7) to the datagrams and segments that carry them (Layer 4). The transport layer answers a question the network layer cannot: given that a packet has arrived at the right host, which of the dozens of programs running there should receive it? Those two operations are multiplexing and demultiplexing, and they are the one service that both the Transmission Control Protocol (TCP) and the User Datagram Protocol (UDP) provide. Today we work through them in detail, trace how each layer adds a header on the way down and strips it on the way back up, and then look closely at UDP, which is nearly the thinnest possible transport protocol — ports, a length, and a checksum. TCP and its reliability machinery come next class.
Files:
- send_udp.txt
- receive_udp.txt
- send_udp.c
- receive_udp.c
Python (rename as .py)
C
Additional information:
Agenda
These notes cover what the transport layer provides, how a message accumulates headers on its way down the stack, how arriving data gets to the right application on a host, and UDP in full — header, checksum, and code. TCP appears here only as a point of contrast; reliable data transfer, connection setup, flow control, and congestion control are next class.
- Transport services: logical connections
- An analogy: the house and the postal service
- What the transport layer actually does
- The two Internet transport protocols
- Headers, encapsulation, and the trip down the stack
- Multiplexing and demultiplexing
- UDP in detail
- The Internet checksum
- UDP in code
- Exercises
- Terms
Notes
We begin with the service the transport layer offers to the application above it, then work downward into the mechanics that deliver the service.
Transport services: logical connections
The transport layer provides logical communication between application processes running on different hosts. From the application's point of view it is as if the two hosts were directly connected — as if there were a wire between the two processes. In reality the hosts may be thousands of miles apart, connected through many routers and several kinds of links. The application does not need to know or care how the data got across the network. That word logical is doing the work: the connection is real in its effects and fictional in its implementation.
A crucial structural fact: transport protocol actions happen only in end systems. Routers do not act on Layer 4 (though network address translators (NATs) and firewalls, which we see later, do read and rewrite ports — a deliberate violation of the layering). They read the Layer 3 Internet Protocol (IP) header and forward the packet; they determine the path through the network and nothing above it. Concretely, the sender's Layer 4 transport layer breaks application messages into segments (TCP) or datagrams (UDP) and passes them to the Layer 3 network layer; the receiver's Layer 4 transport layer reassembles them into messages and passes them up to the application through a socket.
We need a transport layer at all because packets may be lost or delayed on the Internet, and the network layer does not guarantee delivery. The end systems must build anything better than best effort at Layer 4 or above, out of nothing but the ability to send more Layer 3 packets.
Two protocols dominate: TCP and UDP. Others exist — the Stream Control Transmission Protocol (SCTP) and the Datagram Congestion Control Protocol (DCCP), for instance — but the Internet essentially runs on these two. (QUIC, from last class, is also a transport protocol, though it runs above UDP; the Internet Control Message Protocol (ICMP), despite carrying control information, is a Layer 3 protocol.)
An analogy: the house and the postal service
Several people live in a house and write letters to their cousins in another city. One person in each house collects all the outbound letters and distributes all the arriving ones. That person hands each outbound letter to the Postal Service and receives each arriving letter from it, and the Postal Service handles moving letters between cities.
The mapping:
- House = host on the Internet
- Letters = application messages
- People = processes. One house can have many residents; one host can run many processes.
- The one person who sorts the mail = the transport layer. Their job is getting messages to and from the right people.
- Postal Service = the network layer, handling routing between post offices (routers) and houses (hosts)
- Trucks = the physical layer
The one-sentence version, which is worth memorizing: the transport layer connects processes; the network layer connects hosts. The Postal Service gets a letter to the right house and stops there. Someone inside has to know to which resident it belongs, and that sorting is a service the Postal Service neither provides nor recognizes.
What the transport layer actually does
On the sending side, the transport layer:
- receives an application-layer message through a socket,
- determines the values for the header fields,
- creates the datagram or segment, and
- passes it down to the network layer.
The primary header fields are the source and destination port numbers, plus an error-detection checksum. Ports identify specific applications on hosts, and recall that a host may be running many copies of the same application — several browsers, or several tabs.
On the receiving side, the transport layer:
- receives a datagram from the network layer,
- checks the checksum in the header,
- extracts the application-layer message, and
- demultiplexes the message up to the right application through its socket.
Those four steps are the whole job for UDP. TCP adds a great deal on top, but the skeleton is the same.
The two Internet transport protocols
TCP (Transmission Control Protocol) is connection-oriented and provides reliable, in-order delivery, congestion control, flow control, and connection setup. It reassembles out-of-order packets and asks for retransmission of missing ones.
UDP (User Datagram Protocol) is connectionless and provides unreliable, unordered delivery — a no-frills extension of best-effort IP. It does not reassemble out-of-order packets and does not ask for retransmission.
Neither one provides delay guarantees or bandwidth guarantees. That omission is not an oversight: those properties lie outside the transport layer's control. Delay and available bandwidth are properties of the path, which belongs to the network, and no amount of end-system cleverness can make a link faster or a queue shorter.
The application developer has to choose which protocol to use, and that choice is essentially a decision about what to do with the fact that datagrams may be lost or delayed.
Headers, encapsulation, and the trip down the stack
Before we reach the mechanics of ports, it is worth being concrete about what each layer adds, because the whole stack is less mysterious than it sounds. Here is the critical information each layer contributes on the way down:
- Layer 7 (application) — the message the host sends across the network. This message is the only part the application wrote.
- Layer 4 (transport) — prepends a TCP or UDP header carrying the source and destination ports (and, today, the other six UDP header bytes). At Layer 4 the payload is the Layer 7 message.
- Layer 3 (network) — prepends an IP header carrying the source and destination IP addresses, which is what makes routing possible. At Layer 3 the payload is the whole Layer 4 datagram: Layer 4 header plus the Layer 7 message.
- Layer 2 (link) — prepends a Media Access Control (MAC) header carrying the source and destination MAC addresses, which is what makes switching within a local area network (LAN) possible, and also appends a footer that supports error detection. At Layer 2 the payload is the whole Layer 3 packet.
- Layer 1 (physical) — puts the bits on the wire, fiber, or air.
The key point, and one that readers easily lose behind the diagrams: each layer simply adds some bytes to the front of the bytes it received. Nothing more exotic occurs. Encapsulation is prepending.
So a send from client to server goes down the stack, Layer 7 to Layer 1, accumulating headers; the physical layer moves the bits to the other endpoint; and the receiver decapsulates in the reverse order, stripping headers from Layer 2 up to Layer 7:
- Strip the Layer 2 header, validate the MAC address.
- Strip the Layer 3 header, validate the IP address, and read the protocol field to learn what is inside — UDP, TCP, or ICMP. This field is how Layer 3 knows to which Layer 4 handler it should hand the payload.
- Strip the Layer 4 header, validate the checksum, and use the destination port to deliver the data to the right application through its socket.
The server then processes the data and the reply reverses the whole process back to the client, down the server's stack and up the client's. That symmetry is worth remembering: there is one mechanism here, run twice in opposite directions, not two different mechanisms.
Multiplexing and demultiplexing
Recall ports. Your computer runs many applications at once, and without a way
to tell them apart, all incoming data would go to the same place — the web browser would
receive email traffic and vice versa. A port is a number from 0 to
65,535 (216 − 1). Servers listen on well-known ports and clients connect to
them: 20 and 21 for the File Transfer Protocol (FTP), 22 for Secure Shell (SSH), 23 for
telnet, 53 for the Domain Name System (DNS), 80 for the HyperText Transfer Protocol (HTTP),
443 for HTTP Secure (HTTPS), 25 for Simple Mail Transfer Protocol (SMTP) relay, and 587 for
client submission to an email server.
The ranges are 0–1023 well-known (commonly reserved for system applications),
1024–49,151 user or registered, and
49,152–65,535 ephemeral. The list on your machine is in /etc/services.
Note the asymmetry between the two ends. On the server side, a port identifies an application: different services listen on different, agreed-upon numbers, which is the only reason a client can find them. On the client side, a port identifies an instance of an application — you may have several copies running — and the operating system (OS) chooses the number arbitrarily from the ephemeral range. Nobody needs to know the chosen port in advance, because the only traffic that arrives on that port is a reply to something the client sent first.
Now the two operations that give this section its name:
- Multiplexing happens at the sender: gathering data from multiple sockets, adding transport headers, and handing the datagrams down to the network layer. Choosing the socket through which to send is multiplexing.
- Demultiplexing happens at the receiver: taking arriving datagrams and delivering each to the correct socket.
Work through the example from class. Host B is running two processes, P1 and P2. P1 opens a socket to talk to P3 on host A, and the OS gives that socket an unused port — 19,157. P2 opens a second socket to talk to P4 on host C, and gets a different port, 63,241. When B sends a datagram from P1, the transport layer sets the header's source port to that socket's port (19,157), sets the destination port to the port the other host's process is using (4242), adds a checksum, and passes the datagram to the network layer, which hands it to the link layer, which hands it to the physical layer. Sending from P2 works identically with its own pair of numbers (63,241 and 11,438). Choosing which of B's two sockets a given outbound message leaves through is the multiplexing.
Coming back the other way: a datagram arrives at B, traverses up the stack with the MAC and IP addresses checked at Layers 2 and 3, and Layer 4 reads the destination port from the transport header — 19,157 — and delivers the data to the socket bound to that port. That delivery is the demultiplexing. Notice also that a reply simply flips the source and destination ports of the datagram it is answering.
A concrete version of the same question: a client is running X (Twitter), Netflix, and Firefox, and an HTTP response arrives. How does the transport layer know to deliver it to Firefox rather than to Netflix or X? The port number in the header.
Mechanically, demultiplexing works as follows. The host receives Layer 3 IP packets, each carrying a source and destination IP address. If the destination IP is this host, the payload goes up to Layer 4. Each Layer 3 packet carries exactly one transport-layer datagram as its payload, and each datagram carries source and destination port numbers. The host uses the port numbers to direct the datagram to the appropriate socket.
UDP: connectionless demultiplexing
When you create a UDP socket to receive, you specify the port to bind to it:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((ip_addr, 9090))
(If you do not bind, the OS picks a random unused source port — fine for a client, useless for a server that others need to find.) When you send, you specify the destination address and port:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (dest_addr, 9090))
When a UDP datagram arrives, the receiving host checks the destination port number in the
datagram and directs it to the socket bound to that port. This behavior is called
connectionless demultiplexing, and the consequence worth noticing is that the
receiver consults the destination port and nothing else:
UDP datagrams with the same destination port, but different source IP addresses,
different source ports, or both, all arrive at the same socket. One UDP socket receives from
everybody. For that reason a UDP server must print the sender's address returned by
recvfrom if it needs to know from whom it heard; the socket itself does not
distinguish senders. TCP works differently, and next class we see exactly how and why.
UDP in detail
UDP is the "no frills," "bare bones" Internet transport protocol, offering best-effort service: datagrams may be lost, or delivered out of order to the application. It is connectionless — no handshaking between sender and receiver, and each datagram handled independently of the others.
Given all that, why does UDP exist? Because everything TCP adds has a cost, and sometimes you do not want to pay the cost:
- No connection establishment; a connection would add one round-trip time (RTT) of delay before any data moves.
- Simplicity: no connection state at either the sender or the receiver, so a server can handle enormous numbers of clients without remembering anything about them.
- A small header: 8 bytes, versus 20 for TCP.
- No congestion control, so UDP can blast away as fast as it likes and can keep functioning when the network is congested (or, less charitably, can make congestion worse).
Put another way: apart from multiplexing and demultiplexing to get data to the right application, plus checksum validation, UDP is essentially the network layer handed straight to your program.
One more property that distinguishes it from TCP: UDP is message oriented.
UDP does not segment your data into multiple packets the way TCP segments a byte stream. One
sendto produces one datagram, and one recvfrom returns the contents
of exactly one datagram — never data from two datagrams run together. The flip side is
that if you want to send more than the maximum datagram size, you must split the data
and reassemble it yourself. We return to that trade-off when we study TCP's byte stream, where
it runs in the opposite direction.
Use UDP when the arrival, or the in-order arrival, of every datagram is not critical and speed or rate matters: streaming multimedia, DNS, the Simple Network Management Protocol (SNMP), and HTTP/3. Use TCP when you need reliable, in-order delivery and can accept the extra latency. And note the third option, which is the approach QUIC takes: build reliability and congestion control yourself, at the application layer, on top of UDP. That approach gets you exactly the guarantees you want and nothing more — but UDP alone gives you none.
A Request for Comment (RFC) defines UDP: RFC 768, which is three pages long. The RFC is worth reading, if only to see how small a protocol specification can be.
The UDP header
The entire header is 64 bits — 8 bytes — in four 16-bit fields:
- Source port (2 bytes)
- Destination port (2 bytes)
- Length (2 bytes)
- Checksum (2 bytes)
Each port is 2 bytes, which is the origin of the 0–65,535 range. Length counts the bytes in the header plus the Layer 7 data, so sending 20 bytes of application data gives a length of 8 + 20 = 28. Length is what tells the receiver where the datagram ends. Since the field is 16 bits, the theoretical maximum UDP datagram is 65,535 bytes, leaving 65,527 bytes of data after the 8-byte header. (In practice you send far less, for reasons involving the link layer's maximum frame size — the Ethernet maximum transmission unit (MTU) is 1500 bytes — which we discuss in a later class; and in any case the IP length field is also 16 bits, so with the minimum 20-byte IPv4 Layer 3 header: 65,535 − 20 − 8 = 65,507 bytes of data.)
Checksum does error detection but not error correction. It detects — some of the time — whether a noisy link or a router's memory altered bits in transit. Note all three halves of that sentence: the checksum tells you something went wrong, it cannot tell you what went wrong, and it does not always notice.
The checksum is also where Layer 4's receive path begins. When a datagram comes up the stack, the receiver's transport layer computes the checksum over what arrived, compares it against the value in the header, and only then uses the destination port to pass the data up through the socket.
The Internet checksum
The idea in miniature. Suppose you send two numbers, 5 and 6, and you also send their sum, 11. The receiver gets 4 and 6, adds them to get 10, compares that sum to the 11 in the header, and 10 ≠ 11, so something corrupted the data. That comparison is the whole concept: the sender computes a function of the data and sends the result alongside the data; the receiver computes the checksum based on the data it received and compares the result with the checksum in the header. If both checksums match, the comparison did not detect an error. If they do not match, the receiver knows that the data contains at least one bit error.
The real algorithm works on bits. The sender treats the contents of the UDP datagram — including the UDP header fields and the IP addresses — as a sequence of 16-bit integers, adds them using one's complement addition, then puts the one's complement of that sum into the checksum field (worked through below). Bit addition behaves as you would expect, with one twist:
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0, carry 1
The twist is the wraparound: a carry out of the most significant bit must be added back into the result. Working the example from class, adding two 16-bit integers:
1110 0110 0110 0110
+ 1101 0101 0101 0101
---------------------
1 1011 1011 1011 1011 (17 bits -- carry out of the top)
The leading 1 is the carry out. Add it back into the low end:
1011 1011 1011 1011
+ 1
---------------------
1011 1011 1011 1100 sum
The checksum is then the one's complement of that sum — flip every bit:
sum: 1011 1011 1011 1100
checksum: 0100 0100 0100 0011
That checksum goes into the UDP header.
At the receiver, the check is elegant. The receiver computes the sum of the data it received exactly as the sender did, then adds the checksum from the header to it:
sum: 1011 1011 1011 1100
checksum: 0100 0100 0100 0011
---------------------------
1111 1111 1111 1111
The result should be all 1s. That outcome follows from the definition of the checksum: a number plus its own bitwise complement is always all 1s. The receiver therefore does not need to compare two values, and can simply look for a field of ones.
Now suppose one bit of the received data is wrong — the sender sent a 1 where the receiver read a 0. The receiver's sum comes out different, and adding the header's checksum gives:
1111 1011 1111 1111
The result is not all 1s, so the receiver detects an error.
Where the checksum fails
The Internet checksum is weak protection, and the way it fails is worth knowing. Consider errors in two bits, arranged so that the flips cancel: one integer has a bit flipped from 0 to 1, and the other has the corresponding bit flipped from 1 to 0. Both numbers have changed, but their sum has not, so the checksum still comes out right and the receiver detects nothing.
In general, some patterns of an even number of bit errors pass the check. A checksum that matches therefore means "probably no errors," not "no errors" — which is why the slides word the receiver-side rule carefully: unequal means the receiver detected an error; equal means the receiver detected no error. Convince yourself of the cancellation case outside of class; the exercise is a good way to confirm that you understand the arithmetic.
UDP summary
A no-frills protocol: datagrams may be lost or delivered out of order, and the service model is send-and-hope-for-the-best. It is message oriented, so splitting large payloads is your responsibility. But it has real virtues. No setup or handshaking, so no RTT penalty. It can keep functioning when network service is congested. The checksum gives some help with error detection. And you can build anything more you need on top of it at the application layer — which, as HTTP/3 and QUIC demonstrate, is not merely a theoretical option.
UDP in code
Sending a UDP datagram through a socket in Python is about as short as network code gets.
send_udp.py:
import socket
dest_addr = "127.0.0.1"
port = 9090
msg = b'Hello world!'
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (dest_addr, port))
sock.close()
SOCK_DGRAM selects UDP, and SOCK_STREAM would select TCP.
Python hides a great deal of complexity here, filling in every header that we traced in the
section above.
Note what is not in the code: we never set a source port on the client side. The OS picks a random unused port. We do have to specify the destination port, because the client needs to know the port on which the server listens. That asymmetry from the ports discussion appears directly in the application programming interface (API).
You can watch the program work without writing a server at all, using netcat as a UDP listener
(-l listen, -u UDP, -v verbose):
Terminal 1 Terminal 2
$ python3 send_udp.py $ nc -luv 9090
Hello world!
The receiver, receive_udp.py:
import socket
MAX_SIZE = 1500 # max message size in bytes
ip_addr = "0.0.0.0" # 0.0.0.0 means bind to all interfaces
port = 9090 # listen on this port
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((ip_addr, port))
while True:
msg, (ip, port) = sock.recvfrom(MAX_SIZE)
print(f"{ip}:{port} {msg.decode('utf-8')}")
Several points deserve attention:
-
We set up the socket for UDP as we did in the sender, and then
bindattaches it to an address and port. Here we choose the port, because this program is the server side. -
A machine may have several network interface cards (NICs). Binding to
0.0.0.0tells the OS to accept datagrams arriving on any of them, rather than on just one. -
MAX_SIZEis the receive-buffer size — the most this call hands back. -
recvfromblocks until a datagram arrives, and returns both the data and the sender's address and port. Userecvfromfor UDP;recvis the TCP form. Recall why UDP needs the sender's address returned this way: all senders to this port share this one socket. -
The
while Trueloop is what makes the program a server rather than a one-shot program.
Running the receiver against netcat as the sender:
Terminal 1 (receiver) Terminal 2 (sender)
$ python3 receive_udp.py $ nc -u 127.0.0.1 9090
127.0.0.1:64369 hello hello
Look closely at that printed port, 64369. It is not 9090. The server listens on port 9090; the program prints the source port of the arriving datagram, which the OS assigned to netcat arbitrarily. Everything in this section makes the earlier discussion of multiplexing and demultiplexing visible: the destination port got the datagram to the right application, and the source port identifies the instance that sent it.
That last point becomes obvious with two senders at once:
Terminal 1 (receiver) Terminal 2 (sender)
$ python3 receive_udp.py $ nc -u 127.0.0.1 9090
127.0.0.1:53072 hello hello
127.0.0.1:52815 sup?
Terminal 3 (sender)
$ nc -u 127.0.0.1 9090
sup?
Two instances of the same application on the same host, two different source ports, one socket on the receiver — and the receiver can still tell them apart, and can direct a reply back to the right instance, because the header carried the source port.
Exercises
Complete these exercises before or around the next class. Start from send_udp.py and
receive_udp.py above (downloaded as .txt — rename them to
.py).
For each exercise, write down your prediction before you run anything. Then run it and record what actually happened. For learning, a prediction that turned out wrong is worth more than one that was right, as long as you can explain why it was wrong.
Where to run these. Use your CS 60 virtual machine (VM) — the same one from Lab 1. You do
not need the Docker containers. Exercise 1 sends across the network to a classmate's VM;
exercises 2 and 3 run entirely inside your own VM, as two processes talking over the loopback
address 127.0.0.1, so open two terminals. If you want to capture packets on
loopback the interface is lo, so sudo tcpdump -i lo and Wireshark
both work. Because both ends are the same machine in those cases, captures and error messages
will show 127.0.0.1 as the peer — that is expected, and does not affect
anything these exercises are testing.
-
Write a UDP client and server. Working with a neighbor in class, build a
pair of programs that talk to each other across the network.
- Client: read a message from the keyboard, send it in a UDP datagram to the server application on your neighbor's VM, then listen for the reply and print it.
- Server: loop forever, receiving datagrams containing messages from clients and replying to each client with its own message in ALL CAPS.
You will need the virtual private network (VPN) running to reach another student's VM (otherwise the Dartmouth network blocks the traffic). Find your addresses with
ip a(orifconfig) and use the address on thetun0interface, not the address you would normally use. Note the origin of each port number in your code: which ones did you choose, which ones did the OS choose, and which ones did your server learn from the datagram it received?Then take a Wireshark capture of the exchange and examine it. Confirm that the UDP header really is only 8 bytes, and find all four fields in it. Check that the reply's source and destination ports are the flip of the request's. Hand in the capture along with a sentence on each of two questions: where in the frame does the UDP header start, and how did you determine where it ended?
-
Datagram boundaries. UDP is message oriented, and this exercise examines
what that property actually buys you. Modify the client to send five short messages back to back, and
modify the server to print the length and the
reprof the bytes returned by every single call torecvfrom:# client for i in range(5): sock.sendto(f"MSG{i}".encode(), (dest_addr, port)) # server, inside the recvfrom loop msg, (ip, port) = sock.recvfrom(MAX_SIZE) print(f"recvfrom returned {len(msg)} bytes: {msg!r}")Run it three ways and predict each outcome first: with
time.sleep(0.5)after each send and the server reading at full speed; with no delay on the client buttime.sleep(1.0)before the server's firstrecvfrom; and with neither delay, run several times. Does the number ofrecvfromcalls ever depend on the timing? Explain why not, and name the property of UDP that guarantees the result.Now send a single 2000-byte UDP payload and read it with
recvfrom(1024): how many bytes do you get back, and what became of the rest of the datagram? Then try sending a payload larger than UDP can carry at all and record the error.Finally, answer in writing: you are designing a protocol that carries variable-length messages over UDP. What, if anything, must you add to the data you send so the receiver can tell where each message ends? Keep your answer — we will ask the same question about TCP next class, and the answers differ.
-
Send to nobody. Send a UDP datagram to a port on your VM where nothing
listens — use 9099 — and capture the response on the
loopback interface. Identify the ICMP type and code, and name the earlier point in this
course where you saw that message.
Now run these three variants and record, for each one, which call failed and when:
- An unconnected UDP socket, a single
sendtoto the dead port. - An unconnected UDP socket:
sendto, then a brief sleep, thensendtoagain. - A UDP socket you
connect()to the dead port first, thensend, sleep,sendagain. (A UDP socket may callconnect, but the call does something much weaker than the TCP equivalent: it records a default peer and enables ICMP error reporting, and exchanges no packets.)
In the third case the call that raises the error is not the call that sent the offending datagram. Explain why, and explain why the kernel can attribute the ICMP error to a socket in the connected case but not in the unconnected case. (The error name is
ConnectionRefusedErrorin Python, wrappingECONNREFUSED— errno 111.)Your UDP
sendtoreturned successfully even though nothing received the data. Explain in one paragraph what a successful return from a send call actually tells you, and what it does not tell you. - An unconnected UDP socket, a single
Terms
Not a comprehensive reference — the textbook has much more — but these are the terms from Day 5 that recur throughout the course.
- logical communication
- The service the transport layer provides: two processes behave as though directly connected, regardless of the distance and infrastructure between their hosts.
- segment, datagram
- The transport layer's unit of data — segment for TCP, datagram for UDP. Carried as the payload of exactly one IP packet.
- encapsulation, decapsulation
- Encapsulation prepends a layer's header to the data handed down from above (and, at Layer 2, appends a footer); decapsulation strips those headers in reverse order on the way up at the receiver.
- protocol field
- The field in the Layer 3 IP header naming the Layer 4 protocol in the payload — UDP, TCP, ICMP — so the receiver knows to which handler it should pass the payload.
- end system
- A host, as opposed to a router. Transport protocol actions occur only in end systems; routers do not process Layer 4 headers.
- TCP (Transmission Control Protocol)
- Connection-oriented transport providing reliable, in-order delivery, connection setup, flow control, and congestion control. Reassembles out-of-order segments and requests retransmission of missing ones. 20-byte header. Next class.
- UDP (User Datagram Protocol)
- Connectionless best-effort transport, defined in RFC 768. No handshake, no reliability, no ordering, no congestion control; 8-byte header. Provides multiplexing, demultiplexing, and a checksum, and little else.
- port
- A 16-bit number (0–65,535) in the transport header identifying an application on a host (server side) or an instance of an application (client side). Well-known 0–1023, registered 1024–49,151, ephemeral 49,152–65,535.
- socket
- The interface through which an application sends and receives transport-layer data, identified on the local host by an address and port.
- multiplexing
- At the sender: collecting data from multiple sockets, adding transport headers, and passing datagrams to the network layer. Choosing the socket through which to send.
- demultiplexing
- At the receiver: using header field values to deliver each arriving datagram to the correct socket.
- connectionless demultiplexing
- UDP's approach: direct a datagram to the socket bound to its destination port, and nothing else. Datagrams from different sources with the same destination port land in the same socket.
- message oriented
- UDP's delivery model: one
sendtois one datagram, and onerecvfromreturns data from exactly one datagram. Splitting large payloads is the application's job. - SOCK_DGRAM, SOCK_STREAM
- Socket type arguments selecting UDP and TCP, respectively.
- bind, sendto, recvfrom
- The UDP socket calls:
bindattaches a socket to a local address and port;sendtosends one datagram to a named destination;recvfromblocks until a datagram arrives and returns the data together with the sender's address and port. - length field
- The UDP header field counting header bytes (8) plus application data bytes, which tells the receiver where the datagram ends. The field is 16 bits, so the maximum is 65,535 bytes in total — but the 16-bit IP length field caps the real payload at 65,535 − 20 (IP header) − 8 = 65,507 bytes, and the link layer MTU caps it far lower in practice.
- checksum
- A 16-bit field for error detection only. The sender treats the datagram (with UDP header fields and IP addresses) as 16-bit integers, sums them with wraparound, and stores the one's complement of the sum.
- one's complement
- Flipping every bit of a value. A number added to its own one's complement is all 1s, which is why the receiver's check is simply "is the result all ones?"
- wraparound
- In checksum arithmetic, adding a carry out of the most significant bit back into the low end of the sum.
- error detection versus error correction
- Detection reveals that data changed; correction recovers the original. The Internet checksum does only the former, and imperfectly — some patterns of an even number of bit flips cancel out and go unnoticed.