TCP Sequencing Sockets: Fundamentals of TCP/IP Networks

 

TCP Sequencing
Chapter 58 | Sockets: Fundamentals of TCP/IP Networks โ€” Part 3 of 5
๐Ÿ“š TLPI Ch.58
๐Ÿ”ข Sequence Numbers
๐Ÿ” In-Order Delivery

Why Does TCP Need Sequence Numbers?

Imagine you send a 3-page letter by breaking it into 3 separate envelopes. If they arrive in the order 2, 3, 1 โ€” you need page numbers to reassemble them correctly. TCP faces the same problem: segments can arrive out of order, get duplicated, or get lost. Sequence numbers solve all three problems.

TCP assigns a logical sequence number to every byte it sends. The receiver uses these numbers to reassemble the stream in the correct order and deliver it to the application as a clean, in-order byte stream.

Key Terms in This Section

Sequence Number ISN (Initial Sequence Number) In-Order Delivery Duplicate Elimination Byte Stream Out-of-Order Segments 32-bit Wrap-around

๐Ÿ”ข What is a Sequence Number?

Every byte in a TCP connection has a unique sequence number. These numbers are tracked within each data stream separately (there are two streams per TCP connection โ€” one in each direction).

Each TCP segment carries the sequence number of its first byte. For example, if a segment starts with byte 1000 and carries 500 bytes of data, the sequence number in the TCP header is 1000, and the segment covers bytes 1000โ€“1499.

Byte Stream with Sequence Numbers

H
1
e
2
l
3
l
4
o
5
6
W
7
o
8
r
9
l
10
d
11

Segment 1
seq=1, len=5
carries bytes 1โ€“5
Segment 2
seq=6, len=6
carries bytes 6โ€“11
Sequence number in TCP header = byte number of the first byte in the segment

๐ŸŽฏ Three Problems Solved by Sequence Numbers

1. In-Order Reassembly

IP does not guarantee segment ordering. Two segments sent in order may arrive in reverse order. The receiving TCP buffers out-of-order segments and delivers them to the application only after filling any gaps โ€” always in the correct sequence.

Sent order:ย  [Seg 1: seq=1] โ†’ [Seg 2: seq=501] โ†’ [Seg 3: seq=1001]
Arrived:ย ย ย  [Seg 3: seq=1001] โ†’ [Seg 1: seq=1] โ†’ [Seg 2: seq=501]
Delivered to app: 1, 501, 1001 ย โœ… (correct order restored)

2. Duplicate Detection

If a retransmission arrives after the original was already delivered, the receiver sees a duplicate sequence number. It discards the duplicate silently.

Seg (seq=501) arrives โœ… delivered to app
Seg (seq=501) arrives again (duplicate) โŒ discarded by TCP

3. Identifying Lost Segments

If seq=1001 arrives but seq=501 has not yet arrived, the receiver knows segment 501 is missing. It can hold seq=1001 in its buffer until 501 arrives (or use SACK to tell the sender exactly which segments are missing).

๐ŸŽฒ Initial Sequence Number (ISN)

TCP does not start counting bytes from 0 or 1. Each connection starts with a randomly chosen Initial Sequence Number (ISN). Two important reasons:

1. Avoid confusion with old segments

If a connection between the same two ports is restarted, segments from the old connection still in the network could be mistaken for new-connection data. A different ISN makes old segments irrelevant because their sequence numbers fall outside the new connection’s range.

2. Security โ€” prevent ISN prediction attacks

If an attacker could predict the ISN, they could inject fake segments into a connection. The ISN is generated using an algorithm that makes it hard to guess, providing basic protection against TCP session hijacking.

ISN in the Three-Way Handshake
Client โžก Server: SYN, ISN_client = 3274019832
Server โžก Client: SYN-ACK, ISN_server = 8921047213, ACK = 3274019833
Client โžก Server: ACK = 8921047214
Client data starts at byte 3274019833. Server data starts at byte 8921047214. Both are large, random starting points โ€” not 0 or 1.

The sequence number is a 32-bit unsigned integer. After reaching the maximum value (2ยณยฒ โˆ’ 1 = 4,294,967,295), it wraps around to 0 and continues. On very high-speed links, wrap-around can happen fast, which is why TCP options like Timestamps (RFC 1323) exist to handle this safely.

๐Ÿ’ป Code Example: Observing Sequence Numbers with TCP_INFO
/* tcp_seq_info.c - read TCP sequence state after connection */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

int main(void)
{
    int sockfd;
    struct sockaddr_in srv;
    struct tcp_info ti;
    socklen_t tilen = sizeof(ti);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) { perror("socket"); exit(EXIT_FAILURE); }

    memset(&srv, 0, sizeof(srv));
    srv.sin_family      = AF_INET;
    srv.sin_port        = htons(80);               /* connect to a real HTTP server */
    srv.sin_addr.s_addr = inet_addr("93.184.216.34"); /* example.com */

    if (connect(sockfd, (struct sockaddr *)&srv, sizeof(srv)) == 0) {
        printf("Connected!\n");

        /*
         * TCP_INFO gives us the kernel's view of this connection:
         * - tcpi_snd_mss:    Maximum Segment Size for sending
         * - tcpi_rcv_mss:    Maximum Segment Size for receiving
         * - tcpi_last_data_sent: ms since last data was sent
         * - tcpi_rtt:        smoothed RTT in microseconds
         * - tcpi_snd_cwnd:   current congestion window (segments)
         * - tcpi_snd_ssthresh: slow-start threshold
         */
        if (getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &ti, &tilen) == 0) {
            printf("Send MSS:         %u bytes\n",         ti.tcpi_snd_mss);
            printf("Recv MSS:         %u bytes\n",         ti.tcpi_rcv_mss);
            printf("RTT (smoothed):   %u microseconds\n",  ti.tcpi_rtt);
            printf("RTT variance:     %u microseconds\n",  ti.tcpi_rttvar);
            printf("Congestion window:%u segments\n",      ti.tcpi_snd_cwnd);
            printf("Retransmits:      %u\n",               ti.tcpi_retransmits);
        } else {
            perror("getsockopt TCP_INFO");
        }
    } else {
        perror("connect");
    }

    close(sockfd);
    return 0;
}
/*
 * Observing sequence numbers with tcpdump (shell command, not C)
 * Run this in a terminal alongside your program:
 *
 *   sudo tcpdump -i lo -n -S tcp port 8080
 *
 * The -S flag shows ABSOLUTE sequence numbers (the actual ISN-based values)
 * Without -S, tcpdump shows RELATIVE sequence numbers (starting from 0 for readability)
 *
 * Example output:
 *   13:42:01 IP 127.0.0.1.45678 > 127.0.0.1.8080: Flags [S],  seq 3274019832
 *   13:42:01 IP 127.0.0.1.8080  > 127.0.0.1.45678: Flags [S.], seq 8921047213, ack 3274019833
 *   13:42:01 IP 127.0.0.1.45678 > 127.0.0.1.8080: Flags [.],  ack 8921047214
 *
 * Flags: S=SYN, .=ACK, P=PSH (data), F=FIN, R=RST
 */

๐Ÿ…พ Interview Questions โ€” TCP Sequencing
Q1. What does a TCP sequence number represent?

A sequence number identifies the position of the first byte of data in that segment within the overall byte stream. If a segment has seq=1000 and carries 500 bytes, it covers the stream positions 1000โ€“1499. The next segment should have seq=1500.

Q2. Why doesn’t TCP start sequence numbers at 0?

Starting at a random ISN avoids two problems: (1) old segments from a previous incarnation of the same connection (same ports, same hosts) being confused with new connection data, and (2) an attacker predicting the sequence number and injecting forged segments (session hijacking).

Q3. How does TCP handle out-of-order segments?

The receiving TCP buffers out-of-order segments (using the sequence numbers to know where they belong). It waits until the gap is filled โ€” either by receiving the missing segment or via retransmission. Only after filling the gap does it deliver data to the application in correct order.

Q4. How does TCP eliminate duplicate segments?

The receiver tracks the next expected sequence number. If a segment arrives with a sequence number below this value (meaning it was already received and acknowledged), the receiver discards it and sends an ACK for the sequence number it actually expects next. The duplicate data is never passed to the application.

Q5. What is the maximum size of a TCP sequence number, and what happens when it wraps?

The sequence number is 32 bits, so it can hold values from 0 to 4,294,967,295 (about 4 GB). After reaching the max, it wraps back to 0. On 10 Gbps links this can happen in under 4 seconds. TCP handles wrap-around through the Timestamps option (RFC 1323), which adds a timestamp to segments for PAWS (Protection Against Wrapped Sequence numbers).

Q6. What is the ACK number field in TCP, and how is it related to sequence numbers?

The ACK number is the sequence number of the next byte the receiver expects. If the receiver has successfully received bytes 1โ€“1000, it sends ACK=1001. This is a cumulative ACK โ€” it implicitly confirms all bytes up to 1000 were received correctly.

Next: TCP Flow Control
Learn the sliding window algorithm and how TCP prevents a fast sender from overwhelming a slow receiver

Part 4 โ†’ โ† Part 2

Leave a Reply

Your email address will not be published. Required fields are marked *