Chapter 16 real isolated TCP lifecycle experiment, 2026-09-20 Run: sudo python3 lab.py inside an owned Linux VM; stdout is JSON Lines. Linux6.18.10-200.fc43.aarch64, Python3.14.3. Fresh A/B network namespaces per scenario; A10.16.0.1 is client, B10.16.0.2:46016 is server. veth only. No physical interface, host route, production service or dependency install. The observer binds AF_PACKET to B/eth0 before server listen/client connect. Piped command/response barriers coordinate every application operation; /proc/net/tcp is queried only at the designated observed phases. No inference of these states from flags alone is substituted for the actual state records. Request bytes are really sent and read (hex72657175657374). Each socket has a five-second timeout; endpoint command loop20, capture deadline20sec, main capture bound256 frames, child/process command timeouts and FIN_WAIT2 wait2sec. These are test bounds, not protocol timeout defaults or measured state duration. Raw Linux state hex values: 01 ESTABLISHED, 05 FIN_WAIT2, 08 CLOSE_WAIT, 06 TIME_WAIT. Address/port fields are retained exactly as /proc/net/tcp prints them; port numbers are hexadecimal and IPv4 address word representation is kernel-platform-specific. Decoded capture five-tuple fields use dotted IPv4 and decimal ports; protocol is TCP (the decoder filters IPv4 protocol6). Normal case net16-2e1110c3: Both states01. Server B initiates SHUT_WR; client A recv returns b''. Actual A state08, B state05. Client then SHUT_WR, server recv b''. Final A table empty, B state06. Thus TIME_WAIT belongs to the server in this case, not automatically to a client role. Captured TCP flags in order: 2,18,16,24,16,17,16,17,16. Two FIN/ACK frames, no captured RST. Half-close case net16-46b016bd: Both states01. Client A SHUT_WR; server B recv returns b''. Actual A state05, B state08. Server then really sends response (hex 726573706f6e7365), and client still receives those8 bytes after SHUT_WR. Server SHUT_WR, client recv b''; final A state06, B table empty. Captured flags: 2,18,16,24,16,17,16,24,16,17,16; no captured RST. The application direction that reached EOF does not prevent reverse traffic. Reset case net16-210f15b7: Both states01 after server has read the entire request. Server sets Linux SO_LINGER using native struct('ii',1,0), then closes. Client recv raises actual ConnectionResetError errno104. Both matching /proc/net/tcp entries are absent. Captured flags: 2,18,16,24,16,20; final20 is RST|ACK from B to A. This is an explicit abortive close after request consumption, not an unread-data inference. Raw frames remain in capture.raw_frames; decoded entries reference frame_index. The minimal decoder handles this untagged IPv4/TCP traffic only and reports flags and endpoints, not checksums, complete TCP options or packet-loss stats. Capture is observation at B veth, not physical wire or production end-to-end. Snapshots do not show every intermediate state, duration, retransmission timer, TIME_WAIT expiry, port reuse, process restart or simultaneous close. Deleting owned namespaces ends the experiment instead of waiting for TIME_WAIT expiry. Three scenarios succeeded in one complete VM run. Six owned namespaces were removed in finally; cleanup names are logged. Enclosing shell reported VM stopped and exit0. No unexpected RST was present in the two FIN scenarios of this run; extra frames are never removed to make a cleaner narrative. CLI actual results: --help0, --bad2, macOS default guard1 before network change. Python no-excuse checker zero violations; no commit or push performed.