TCP/IP Binary

How TCP segments and IP packets are constructed and parsed at the bit level

The TCP/IP Suite: A Layered Binary Architecture

In my years building and troubleshooting network systems, the TCP/IP protocol suite has been the single most important subject to master. TCP/IP is not one protocol but a layered stack where each layer adds its own binary header to the data as it travels down the stack. At the application layer, you have your data — say, an HTTP request. When it reaches the transport layer, TCP prepends a 20-byte (160-bit) header. Then at the network layer, IP prepends another 20-byte header. Finally, at the link layer, Ethernet adds a 14-byte header. The receiving end strips these headers off layer by layer, reading the binary fields at each step. Understanding how these headers are constructed in binary is what separates someone who can use networking tools from someone who can build them.

The IP Packet Header in Binary Detail

The IPv4 header is 20 bytes (160 bits) long in its most common form. Every field is positioned at a specific bit offset. Here is the complete binary layout as I've memorized it from building packet parsers:

  • Bytes 0-0 (4 bits): Version. Always 0100 for IPv4.
  • Bytes 0-0 (4 bits): IHL. Typically 0101 (20 bytes, no options).
  • Bytes 1-1 (8 bits): DSCP (6 bits) + ECN (2 bits).
  • Bytes 2-3 (16 bits): Total Length. The entire IP packet size in bytes.
  • Bytes 4-5 (16 bits): Identification. Used for fragmentation reassembly.
  • Bytes 6-6 (3 bits): Flags. Reserved (1 bit), Don't Fragment (1 bit), More Fragments (1 bit).
  • Bytes 6-6 (13 bits): Fragment Offset. Where this fragment belongs in the original packet.
  • Bytes 8-8 (8 bits): Time To Live. Decremented by each hop. 01000000 = 64, 10000000 = 128.
  • Bytes 9-9 (8 bits): Protocol. 00000110 (6) = TCP, 00010001 (17) = UDP.
  • Bytes 10-11 (16 bits): Header Checksum. Covers only the IP header.
  • Bytes 12-15 (32 bits): Source IP Address.
  • Bytes 16-19 (32 bits): Destination IP Address.

I have a personal trick for verifying IP headers manually: the checksum is calculated over the entire header treated as 16-bit words. If you sum them all (including the checksum field itself, treated as zero when calculating), you should get 0xFFFF. This one's complement trick has saved me hours of debugging corrupted packets.

The TCP Segment Header: 160 Bits of Reliability

The TCP header is the most complex of the common transport protocols, with 20 bytes of mandatory fields and potentially up to 60 bytes with options. When I teach TCP, I always start with the binary layout because the sequence numbers and flags only make sense when you see them as a contiguous block of bits. Here is the structure:

  • Source Port (16 bits): The sender's port. For a web server, this is typically 00000000 01010000 (80) or 00000000 10111101 (443).
  • Destination Port (16 bits): The receiver's port. Same encoding.
  • Sequence Number (32 bits): A 4-byte number representing the byte position of this segment's data in the overall stream. The initial value (ISN) is randomly chosen during the SYN handshake.
  • Acknowledgment Number (32 bits): The next byte the receiver expects to receive. Only valid when the ACK flag is set.
  • Data Offset (4 bits): The size of the TCP header in 32-bit words. Minimum is 0101 (5 words = 20 bytes).
  • Reserved (3 bits): Must be zero.
  • Flags (9 bits): NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN. Each is 1 bit. The three-way handshake sequence is: SYN (000000010), SYN+ACK (000100010), ACK (000100000).
  • Window Size (16 bits): How many bytes of data the receiver is willing to accept. A value of 11111111 11111111 means 65,535 bytes.
  • Checksum (16 bits): Covers the TCP header, payload, and a pseudo-header derived from the IP header.
  • Urgent Pointer (16 bits): Only meaningful when the URG flag is set. Points to the last byte of urgent data.

I have traced through TCP segments byte-by-byte on numerous occasions when debugging custom protocol implementations. The 32-bit sequence number is particularly important — if two packets have the same sequence number for different data, you have a TCP segment collision, which usually means a bug in the sender's implementation.

The TCP Three-Way Handshake in Binary

Every TCP connection starts with a three-way handshake, and the flag bits are the key to understanding it. When I capture a new connection in Wireshark, I always look at the flags byte. The handshake goes like this:

  1. SYN (Flags: 000000010): The client sends a segment with only the SYN bit set. The sequence number is set to a random ISN. No data is in this segment.
  2. SYN+ACK (Flags: 000100010): The server responds with both SYN and ACK bits set. The acknowledgment number is the client's ISN + 1. The server also picks its own random ISN.
  3. ACK (Flags: 000100000): The client sends a segment with only the ACK bit set. The acknowledgment number is the server's ISN + 1. Now data transfer can begin.

I've diagnosed countless connection failures by checking whether the three-way handshake completes. If you see a SYN followed by a RST instead of SYN+ACK, the port is closed. If you see SYN, SYN+ACK, but no final ACK, something is dropping the client's response. Each of these scenarios maps to a specific binary flag pattern in the TCP header.

TCP Window Scaling and the 16-Bit Window Field

The TCP window size field is only 16 bits, which means the maximum value it can express is 65,535. For modern high-bandwidth networks, this is far too small to keep the pipe full. The solution is the Window Scale TCP option, which adds a shift count (from 0 to 14) to the window field during the SYN handshake. A window scale of 7, for example, means the actual window is window_field × 128 (27). With a scale factor of 14 and a window field of 65,535, the effective window is about 1 GB. I've tuned window scaling on many high-latency links to maximize throughput, and it always comes back to understanding that 16-bit binary field and the 3-byte TCP option that modifies it.

TCP Segmentation and the MSS Option

TCP segments application data into chunks that fit within the Maximum Segment Size (MSS), which is negotiated during the SYN handshake. The MSS is a 16-bit field in a TCP option, typically set to 1460 bytes for Ethernet networks (1500 MTU minus 20 bytes IP header minus 20 bytes TCP header). The binary value for 1460 is 00000101 10110100. When a segment arrives and the Total Length in the IP header minus the IHL minus the TCP Data Offset equals more than the MSS, the packet is either fragmented at the IP layer or the sender has ignored the MSS negotiation. I always check the MSS early when debugging packet loss on a path with unusual MTU configurations.

Pro Tip: The TCP flags are always in the same order in the header: NS CWR ECE URG ACK PSH RST SYN FIN. I use the mnemonic "Never Could Expect Unicorns, All Pink Rhinos Sing Funny" to remember them. The flags byte is at offset 13 in the TCP header, so byte 13 of the TCP segment gives you all 9 flags in a single 8-bit plus 1-bit arrangement.

How TCP and IP Interact at the Binary Level

The IP header's Protocol field (byte 9) tells the receiver which transport protocol to hand the payload to. When it reads 00000110 (6), it knows to pass the payload to the TCP handler. The TCP handler then reads its own header to extract the port numbers, sequence numbers, flags, and window. The interaction is seamless because both headers are designed to be processed sequentially: the IP header is parsed first, the IP payload is the TCP header plus data, and the TCP header is parsed next. In a raw packet dump, you can trace the entire chain: Ethernet header (14 bytes), IP header (20 bytes), TCP header (20+ bytes), application data. Every byte is accounted for, and every field has a known bit offset.

TCP Connection Termination: The FIN Bit

When a TCP connection closes, each side sends a segment with the FIN flag set. The FIN flag is bit 0 in the flags group (the last bit). A FIN segment looks like 000000001 in the flags field. The receiver acknowledges with ACK (000100000), and then sends its own FIN (000000001). This four-way close sequence (FIN, ACK, FIN, ACK) ensures both sides agree the connection is done. I've seen many connection leak bugs where the application closes its socket but the kernel never sends the FIN segment — usually because the socket was already closed on the other end and the kernel is waiting for a timeout. Checking for the presence of the FIN bit in packet captures is the fastest way to diagnose these half-open connection issues.

Try It Yourself

Open the Binary Code Decoder in a new tab and enter some binary patterns to see the results instantly. All conversions happen in your browser — no data is sent to any server.