Python socket: Build TCP and UDP Networks

Published on: August 27, 2026
Reading time: 6 minutes
Rustic exposed brick wall featuring aged electrical sockets and metal conduit.

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.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Hands typing code on a laptop in a workspace. Indoor setting focused on software development.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python multiprocessing: Use Multiple Cores

    Learn Python multiprocessing with processes, pools, queues, pipes, shared memory, cancellation, security, and correct shutdown.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    concurrent.futures: Threads and Processes in Parallel

    Learn Python concurrent.futures with threads, processes, Future objects, timeouts, cancellation, backpressure, and deadlock prevention.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    Blurry spinning vinyl record with needle on turntable, capturing the essence of analog music.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python winsound: Play Audio on Windows

    Learn Python winsound to play WAV files, system sounds, beeps, loops, and asynchronous notifications safely on Windows.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python winreg: Manage Windows Registry

    Learn Python winreg to read and write the Windows Registry, manage types, permissions, WOW64 views, deletion, migrations, and security.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python posix: Direct Unix System Calls

    Understand Python posix, Unix calls, descriptors, permissions, processes, security, and why most programs should use os instead.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026
    Vivid close-up of code on a computer screen showcasing programming details.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python curses: Build Terminal Interfaces

    Learn Python curses to build terminal interfaces with windows, colors, keyboard input, resize handling, Unicode, and safe cleanup.

    Ler mais

    Tempo de leitura: 6 minutos
    26/08/2026