Networking, Low-Level Sockets, and IP Address Calculations
Networking, Low-Level Sockets, and IP Address Calculations
After reading this chapter, you will master low-level networking primitives using the standard library: construct TCP client and server sockets with socket, dissect and sanitize URLs with urllib.parse, and calculate subnets, masks, and IP ranges with ipaddress.
Mental model
Python provides direct wrappers around operating system Berkeley sockets and IP networking primitives:
Socket Lifecycle (TCP Server):
socket(AF_INET, SOCK_STREAM) ──▶ Creates OS socket file descriptor
│
▼ bind(('127.0.0.1', 8080))
Binds to interface and port
│
▼ listen(backlog=5)
Enters listening state for incoming SYN packets
│
▼ accept() ──▶ Blocks until 3-way handshake completes
│ Returns (client_socket, client_address)
│
▼ sendall() / recv() (Stream bytes over TCP connection)
│
▼ close() (Sends FIN packet, releases file descriptor)
The ipaddress module models IPv4 and IPv6 addresses, subnets, and host ranges as first-class objects with mathematical comparison and containment operations.
Minimal example
Save as sockets_and_ipaddress.py:
# sockets_and_ipaddress.py
import ipaddress
import urllib.parse
def main() -> None:
# 1. IP Network & CIDR Calculations
subnet = ipaddress.ip_network("10.0.1.0/24")
print(f"Subnet CIDR : {subnet}")
print(f"Network Address : {subnet.network_address}")
print(f"Broadcast Address : {subnet.broadcast_address}")
print(f"Netmask : {subnet.netmask}")
print(f"Usable Host Count : {subnet.num_addresses - 2}")
# Membership test (O(1) bitmask check)
target_ip = ipaddress.ip_address("10.0.1.55")
print(f"Is {target_ip} in {subnet}? {target_ip in subnet}")
# 2. URL Parsing with urllib.parse
raw_url = "https://api.internal.net:8443/v1/telemetry?filter=active&limit=50#summary"
parsed = urllib.parse.urlparse(raw_url)
print(f"\nParsed URL Components:")
print(f" Scheme : {parsed.scheme}")
print(f" Host : {parsed.hostname}")
print(f" Port : {parsed.port}")
print(f" Path : {parsed.path}")
# Parse query string parameters into a dictionary
params = urllib.parse.parse_qs(parsed.query)
print(f" Query Parameters: {params}")
if __name__ == "__main__":
main()Run via uv run python sockets_and_ipaddress.py:
Subnet CIDR : 10.0.1.0/24
Network Address : 10.0.1.0
Broadcast Address : 10.0.1.255
Netmask : 255.255.255.0
Usable Host Count : 254
Is 10.0.1.55 in 10.0.1.0/24? True
Parsed URL Components:
Scheme : https
Host : api.internal.net
Port : 8443
Path : /v1/telemetry
Query Parameters: {'filter': ['active'], 'limit': ['50']}
Worked examples
Case 1: Low-Level TCP Loopback Echo Service
Writing a raw TCP client and server directly demonstrates socket buffers, timeouts, and byte transfers:
# tcp_loopback_echo.py
import socket
import threading
import time
def start_echo_server(port: int, stop_event: threading.Event) -> None:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# SO_REUSEADDR allows instant restart without waiting for TIME_WAIT
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", port))
server.listen(1)
server.settimeout(1.0) # Unblock periodically to check stop_event
print(f"[Server] Listening on 127.0.0.1:{port}...")
while not stop_event.is_set():
try:
client_conn, client_addr = server.accept()
except socket.timeout:
continue
with client_conn:
data = client_conn.recv(1024)
if data:
print(f"[Server] Received: {data.decode()} from {client_addr}")
client_conn.sendall(b"ECHO:" + data)
server.close()
def main() -> None:
test_port = 18888
stop_event = threading.Event()
server_thread = threading.Thread(target=start_echo_server, args=(test_port, stop_event))
server_thread.start()
time.sleep(0.05) # Wait for server to bind
# Client connection
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.settimeout(2.0)
try:
client.connect(("127.0.0.1", test_port))
message = b"PING_CLUSTER"
client.sendall(message)
response = client.recv(1024)
print(f"[Client] Received back: {response.decode()}")
finally:
client.close()
stop_event.set()
server_thread.join()
if __name__ == "__main__":
main()Run:
uv run python tcp_loopback_echo.pyOutput:
[Server] Listening on 127.0.0.1:18888...
[Server] Received: PING_CLUSTER from ('127.0.0.1', ...)
[Client] Received back: ECHO:PING_CLUSTER
Case 2: Subnet Splitting and Overlap Detection
Automating IP Address Management (IPAM) requires calculating available subnets and ensuring IP blocks never overlap:
# ipam_calculator.py
import ipaddress
def plan_datacenter_subnets() -> None:
vpc_cidr = ipaddress.ip_network("172.16.0.0/16")
print(f"Allocating subnets from VPC CIDR: {vpc_cidr}")
# Subdivide /16 into /24 subnets
subnets = list(vpc_cidr.subnets(new_prefix=24))
print(f"Generated {len(subnets)} /24 subnets. First 3:")
for sn in subnets[:3]:
print(f" - {sn} (Hosts: {sn.num_addresses - 2})")
# Check for overlaps
public_subnet = ipaddress.ip_network("172.16.1.0/24")
private_subnet = ipaddress.ip_network("172.16.2.0/24")
print(f"\nDo public and private subnets overlap? {public_subnet.overlaps(private_subnet)}")
if __name__ == "__main__":
plan_datacenter_subnets()Run:
uv run python ipam_calculator.pyOutput:
Allocating subnets from VPC CIDR: 172.16.0.0/16
Generated 256 /24 subnets. First 3:
- 172.16.0.0/24 (Hosts: 254)
- 172.16.1.0/24 (Hosts: 254)
- 172.16.2.0/24 (Hosts: 254)
Do public and private subnets overlap? False
Pitfalls
Pitfall 1: Host Bits Set in IPv4Network
Creating an IPv4Network("192.168.1.15/24") raises ValueError: has host bits set because the last octet is non-zero in a /24 network:
# THE BUG:
net = ipaddress.ip_network("192.168.1.15/24") # ValueError!
# THE FIX: If you want the network containing this host IP, pass strict=False
net = ipaddress.ip_network("192.168.1.15/24", strict=False) # Resolves to 192.168.1.0/24
# OR use IPv4Interface:
iface = ipaddress.ip_interface("192.168.1.15/24") # Preserves both host IP and networkPitfall 2: Assuming recv() Returns a Full Packet
TCP is a stream-oriented protocol, not message-oriented. Calling sock.recv(1024) may return 10 bytes, 100 bytes, or 1024 bytes depending on OS buffer chunks and network fragmentation. Always loop until you reach an expected length or delimiter (like \n).
Exercises
- Write a script that checks whether an IP address is a private RFC 1918 address using
ipaddress.IPv4Address.is_private. - Construct a URL query string from a dictionary of parameters using
urllib.parse.urlencode(). - Create a non-blocking TCP socket and demonstrate catching
BlockingIOErrorwhen reading without incoming data. - Calculate the supernet of two adjacent
/24subnets usingipaddress.collapse_addresses().
Further reading
- Python Standard Library:
socket,ipaddress, andurllib.parsemodules. - RFC 793: Transmission Control Protocol.
- RFC 4632: Classless Inter-domain Routing (CIDR).