The socket module provides Python’s low-level network communication interface. It can create TCP clients and servers, exchange UDP datagrams, work with IPv4, IPv6, and local sockets, configure timeouts, resolve names, and access operating-system socket options. Higher-level HTTP, SMTP, database, and messaging libraries build on similar transport concepts.
Sockets carry bytes, not Python messages. An application must define framing, encoding, size limits, deadlines, authentication, and disconnect handling. Prefer a mature high-level library for a standard protocol. Use socket when implementing or studying a protocol, integrating a specialized service, or requiring detailed transport control.
Address families and socket types
AF_INET represents IPv4, AF_INET6 represents IPv6, and AF_UNIX, where available, provides local pathname-based communication. TCP normally uses SOCK_STREAM; UDP uses SOCK_DGRAM.
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client.settimeout(5)
client.connect(("example.com", 80))
finally:
client.close()
Prefer a context manager so the descriptor closes even when an exception occurs.
Connect with create_connection
socket.create_connection() resolves the host and tries suitable addresses, simplifying TCP clients.
import socket
with socket.create_connection(("example.com", 80), timeout=5) as sock:
sock.sendall(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
response = sock.recv(4096)
print(response[:100])
Use a real HTTP library for production HTTP. This example shows only the transport and does not implement the complete protocol.
TCP is a byte stream
TCP delivers an ordered stream of bytes. One send() call on one side does not correspond to one recv() call on the other. Data may arrive split into smaller blocks or combined with later writes.
The application protocol must say where a message ends, using a validated delimiter, a fixed-size header, a length prefix, or a self-describing format.
send and sendall
send() may transmit only part of the buffer and returns the number of bytes accepted. sendall() continues until everything is submitted or an error occurs.
message = b"important data"
sock.sendall(message)
Even after sendall(), the receiver must reconstruct its complete application message. Success only means the local system accepted the bytes, not that the remote application processed them.
recv and orderly shutdown
recv(size) returns up to the requested number of bytes. An empty byte string indicates that the peer closed its sending direction cleanly.
parts = []
while True:
block = sock.recv(4096)
if not block:
break
parts.append(block)
data = b"".join(parts)
Do not wait for EOF when the protocol keeps a connection open for multiple messages. Use framing.
Length-prefixed framing
A common design sends a fixed-size length followed by the payload.
import struct
def receive_exact(sock, amount):
parts = []
remaining = amount
while remaining:
block = sock.recv(remaining)
if not block:
raise ConnectionError("connection closed")
parts.append(block)
remaining -= len(block)
return b"".join(parts)
header = receive_exact(sock, 4)
length = struct.unpack("!I", header)[0]
if length > 1_000_000:
raise ValueError("message is too large")
payload = receive_exact(sock, length)
Validate the declared size before allocating memory or reading indefinitely.
A basic TCP server
A server creates a socket, binds an address, starts listening, and accepts connections.
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", 9000))
server.listen()
connection, address = server.accept()
with connection:
connection.sendall(b"hello\n")
Binding to 127.0.0.1 restricts access to the local machine. Binding to all interfaces exposes the service to the network and requires authentication, firewall rules, limits, and hardening.
listen and backlog
listen() places the socket into passive mode. Its backlog influences the queue of pending connections, although exact behavior depends on the operating system.
A larger backlog does not fix a slow server. Accept quickly, limit active clients, and use timeouts.
accept returns another socket
accept() returns a new connected socket and a remote address. The listening socket remains available for more clients.
Close both independently. Leaking accepted connections eventually exhausts file descriptors.
Server concurrency
A minimal server handles one client at a time. Multiple clients require threads, processes, selectors, or asyncio. The right model depends on connection counts, workload, latency, and operational complexity.
Python select explains descriptor multiplexing. Do not create an unlimited thread for every connection.
Timeouts
settimeout(seconds) limits blocking operations.
sock.settimeout(10)
try:
data = sock.recv(4096)
except socket.timeout:
log_warning("client was inactive")
Set connection, read, and write deadlines according to the protocol. A connection without a deadline can occupy a worker forever.
Nonblocking mode
setblocking(False) makes calls return immediately and raise BlockingIOError when progress is not currently possible.
Do not build a busy loop. Use selectors, select, or an event loop to wait for readiness.
UDP
UDP sends independent datagrams without reliable delivery, ordering, retransmission, or connection state.
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.settimeout(2)
sock.sendto(b"query", ("127.0.0.1", 9999))
data, source = sock.recvfrom(2048)
print(source, data)
Datagrams may be lost, duplicated, reordered, or truncated when the receiving buffer is too small. The application protocol must define retries and idempotency.
UDP size
Although UDP can represent large datagrams, real networks have MTU and fragmentation limits. Small messages are more robust. Use TCP or a suitable protocol for large or reliably delivered payloads.
Name resolution
getaddrinfo() resolves a hostname and returns alternatives with family, socket type, protocol, and address.
for result in socket.getaddrinfo("example.com", 443, type=socket.SOCK_STREAM):
family, kind, protocol, canonical, address = result
print(family, address)
DNS may be slow, fail, or return several addresses. Do not cache one answer forever without a policy.
IPv4 and IPv6
Use getaddrinfo() and try returned addresses instead of assuming IPv4. IPv6 address tuples differ and dual-stack behavior can require platform-specific configuration.
Test IPv4-only, IPv6-only, and dual-stack deployments.
Ports and byte order
Ports range from 0 to 65535, and low-numbered ports may require privileges. Functions such as htons(), htonl(), ntohs(), and ntohl() convert integer byte order. Binary protocols often use struct with the ! network-order prefix.
SO_REUSEADDR
SO_REUSEADDR can make server restart easier when previous connections remain in closing states. Its semantics vary by platform and it should not be interpreted as permission for several independent servers to share a port.
Set options before bind().
Keepalive
SO_KEEPALIVE enables operating-system probes for broken connections, but default intervals may be long and detailed tuning is platform-specific.
Keepalive does not replace application heartbeats and request deadlines.
shutdown
shutdown(socket.SHUT_WR) says that the application will send no more data while allowing it to continue receiving. close() releases the local descriptor.
Use half-close only when the protocol defines its meaning. Some peers treat it as a complete disconnect.
TLS
A plain TCP socket provides no confidentiality, peer authentication, or protection against active intermediaries. Wrap it with a correctly configured ssl.SSLContext or use a high-level secure client.
Do not invent encryption and do not disable certificate verification as a permanent workaround.
Text encoding
The network transports bytes. Define text encoding explicitly.
message = "hello".encode("utf-8")
sock.sendall(message)
text = data.decode("utf-8", errors="strict")
Delimiters must be defined as bytes, and invalid character sequences require a protocol-level policy.
Input security
Limit message size, nesting depth, field counts, and processing time. Never pass network data directly to eval(), a shell, SQL strings, or unsafe deserializers.
Authenticate clients where required and authorize individual operations.
Slow clients
A client can send a few bytes slowly and occupy a worker. Use deadlines, minimum progress requirements, connection limits, and multiplexing.
Rate limiting and backpressure should protect memory and downstream services.
Transient failures
Connections may fail because of DNS, timeout, reset, routing, refusal, or temporary unavailability. Retry only idempotent operations, with a limit and exponential backoff.
Do not retry forever or treat authentication failure as transient.
Unix local sockets
Where AF_UNIX is available, processes on one machine can communicate through a pathname with less network exposure.
Manage file permissions, remove stale paths carefully, and restrict access to the containing directory.
Testing
Test split frames, several frames in one read, EOF, timeout, reset, invalid addresses, IPv6, DNS with multiple answers, slow clients, maximum payloads, simultaneous connections, shutdown, and network loss.
Use local sockets and ephemeral ports in tests. Avoid depending on public services.
Observability
Record remote address, duration, byte counts, protocol state, and error category without logging secrets or sensitive payloads. Metrics for active connections, timeouts, resets, and queue depth reveal saturation.
Common mistakes
Common failures include assuming recv() returns one complete message, ignoring partial send(), omitting timeouts, binding publicly by accident, expecting UDP reliability, assigning an unlimited thread per client, treating DNS as identity, and sending sensitive data without TLS.
Conclusion
socket gives direct network control, but it requires an explicit application protocol. Define framing, limits, timeouts, security, lifecycle, and concurrency before deploying a service.
Consult the official socket documentation and Python select for multiplexing many connections.







