🐍 Python Course · Stage 3 · Lesson 57 of 89 · socket — Networking
Course Overview →

🔵 Read Beginner and Intermediate tabs. The Expert tab has the internals — worth reading once you finish Stage 3.

← queue — Thread-Safe Queues http.server — HTTP Server →
thecodex.expert · The Codex Family of Knowledge
Python Standard Library

socket — Low-Level Networking

The socket module is Python’s direct interface to the network — the foundation beneath every HTTP library, web framework, and networked application.

import socket docs.python.org Last verified:
Canonical Definition

The socket module provides access to the BSD socket interface, the low-level networking primitive used to send and receive data across a network. A socket is an endpoint of a communication channel, identified by an address and port, supporting protocols such as TCP (reliable streams) and UDP (datagrams).

🟩 Beginner

The phone line between programs

What you will learn: what a socket is, the TCP-vs-UDP distinction, and how to write a basic TCP client that connects and exchanges bytes.

How to read this tab: Sockets send bytes, not strings — encode before sending, decode after receiving. That trips up everyone first.

⏱ 30 min📄 2 sections🔶 Prerequisite: Concurrency
The idea

A socket is a phone line between two programs over a network. One side listens for a call (the server), the other dials in (the client), and once connected they send bytes back and forth. Every web request, chat message, and API call ultimately rides on a socket.

Sockets send bytes, not strings. Everything over a socket is bytes — encode before sending ("hi".encode()) and decode after receiving (data.decode()). Forgetting this is the most common first-time socket error.

What a socket is

A socket is an endpoint for network communication, identified by an IP address and a port number. The two main types are TCP (a reliable, ordered byte stream — like a phone call) and UDP (fast, connectionless messages — like postcards that may arrive out of order or not at all).

Pythonsocket_basics.py
import socket

# Create a TCP socket
# AF_INET = IPv4 addressing, SOCK_STREAM = TCP (reliable stream)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# A UDP socket would use SOCK_DGRAM instead
# u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Look up the IP address behind a hostname (DNS)
print(socket.gethostbyname("example.com"))   # e.g. '93.184.216.34'

# Your own machine's hostname
print(socket.gethostname())

# Always close a socket when done (or use a with-block)
s.close()

# Ports below 1024 are "well-known" (80=HTTP, 443=HTTPS, 22=SSH).
# Use ports above 1024 for your own applications.
Sockets send bytes, not strings

Everything sent over a socket is bytes, not str. Encode before sending ("hello".encode()) and decode after receiving (data.decode()). Forgetting this is the most common first-time socket error.

recv(n) reads up to n bytes, not exactly n. Data arrives in chunks, so a single recv may return less than requested. Loop until you have the full message or recv returns empty bytes (which means the connection closed). Use sendall (not send) to transmit everything.

A TCP client

Pythonclient.py
import socket

# Connect to a server and exchange data — using a with-block for cleanup
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(("example.com", 80))      # (host, port) tuple

    # Send an HTTP request (must be bytes)
    request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"
    s.sendall(request.encode())          # sendall sends ALL the bytes

    # Receive the response in chunks until the server closes
    chunks = []
    while True:
        data = s.recv(4096)              # read up to 4096 bytes
        if not data:                     # empty bytes = connection closed
            break
        chunks.append(data)

    response = b"".join(chunks).decode("utf-8", errors="replace")
    print(response[:200])                # first 200 chars of the response

# In real code you would use the 'requests' library or urllib — this
# shows what those libraries do underneath.

✅ Beginner tab complete

  • I know a socket is a network endpoint (address + port)
  • I know TCP is a reliable stream; UDP is fast connectionless messages
  • I can write a TCP client: connect, sendall, recv in a loop
  • I always encode strings to bytes before sending

Continue to http.server →

🔵 Intermediate

Servers, UDP, and timeouts

What you will learn: the bind/listen/accept/recv server lifecycle, UDP send/recv, and using timeouts to avoid blocking forever.

How to read this tab: The four-step server pattern (bind, listen, accept, recv/send) is the heart of every network server.

⏱ 30 min📄 2 sections🔶 Prerequisite: Beginner tab

The four steps: bind, listen, accept, recv/send. This lifecycle is the heart of every network server. accept() blocks until a client connects and returns a new socket for that client. The basic loop serves one client at a time — real servers add threads, selectors, or asyncio.

A TCP server

A server binds to an address, listens for connections, accepts them one at a time, and communicates with each client. This four-step pattern — bind, listen, accept, recv/send — is the heart of every network server.

Pythonserver.py
import socket

HOST = "127.0.0.1"      # localhost — only this machine can connect
PORT = 65432            # a port above 1024

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
    # Allow reusing the address immediately after the program restarts
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    server.bind((HOST, PORT))    # claim the address and port
    server.listen()              # start listening for connections
    print(f"Listening on {HOST}:{PORT}")

    while True:
        # accept() blocks until a client connects, returning a NEW
        # socket for that specific client plus their address
        conn, addr = server.accept()
        with conn:
            print(f"Connected by {addr}")
            while True:
                data = conn.recv(1024)
                if not data:
                    break             # client disconnected
                # Echo the data back, uppercased
                conn.sendall(data.upper())
This server handles one client at a time

The loop above serves connections sequentially — a second client waits until the first disconnects. Real servers handle many clients concurrently using threads, selectors, or asyncio. For anything beyond learning, a framework built on these primitives is the right choice.

💡

TCP for order and reliability; UDP for speed. TCP guarantees delivery and ordering (web, email, files). UDP is faster but does neither (video, games, DNS). UDP needs no connect/accept — you sendto an address directly. Always set a timeout to avoid blocking forever.

UDP and connection details

Pythonudp_details.py
import socket

# UDP — connectionless. No connect/accept; you send to an address directly.
# UDP server
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("127.0.0.1", 9999))
data, addr = server.recvfrom(1024)   # returns (data, sender_address)
server.sendto(b"reply", addr)        # send back to that address

# UDP client
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(b"hello", ("127.0.0.1", 9999))   # no connection needed
reply, _ = client.recvfrom(1024)

# Timeouts prevent a socket from blocking forever
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5.0)                    # raise socket.timeout after 5 seconds
try:
    s.connect(("example.com", 80))
except socket.timeout:
    print("Connection timed out")
except socket.gaierror:
    print("Could not resolve the hostname (DNS failure)")
finally:
    s.close()

# TCP guarantees order and delivery; UDP is faster but does neither.
# Use TCP for web, email, files. Use UDP for video, games, DNS.
Commonly confused
send vs sendall. send() may transmit only part of the data and returns how many bytes were sent; you would have to loop. sendall() keeps going until everything is sent (or it errors). Use sendall unless you have a specific reason not to.
recv(n) reads up to n bytes, not exactly n. A single recv may return fewer bytes than requested — data arrives in chunks. To read a complete message you must loop until you have everything, or until recv returns empty bytes (connection closed).

✅ Intermediate tab complete

  • I can write a TCP server: bind, listen, accept, recv/send
  • I know this basic server handles one client at a time
  • I can use UDP with sendto/recvfrom
  • I use settimeout to prevent indefinite blocking

Continue to http.server →

🔴 Expert

Non-blocking I/O, selectors, and asyncio

What you will learn: blocking vs non-blocking sockets, using selectors to handle many connections in one thread, and the ladder up to socketserver and asyncio.

How to read this tab: Read to understand what every HTTP library and web framework does underneath.

⏱ 20 min📄 1 section🔶 Prerequisite: After Stage 4

Blocking vs non-blocking, selectors, and the path to asyncio

By default sockets are blocking: recv, accept, and connect halt the thread until they complete. This is simple but does not scale — one thread per connection is expensive at thousands of connections. The solutions are non-blocking sockets combined with an I/O multiplexer (selectors), or the higher-level asyncio framework built precisely on these primitives.

Pythonsocket_advanced.py
import socket, selectors

# A selector lets ONE thread watch many sockets, reacting only to
# those that are ready — the foundation of high-concurrency servers.
sel = selectors.DefaultSelector()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 65432))
server.listen()
server.setblocking(False)            # non-blocking mode
sel.register(server, selectors.EVENT_READ, data=None)

def event_loop():
    while True:
        events = sel.select(timeout=None)   # wait for ANY socket to be ready
        for key, mask in events:
            if key.data is None:
                # The listening socket is ready -> accept a new client
                conn, addr = key.fileobj.accept()
                conn.setblocking(False)
                sel.register(conn, selectors.EVENT_READ, data=addr)
            else:
                # A client socket is ready -> read without blocking
                data = key.fileobj.recv(1024)
                if data:
                    key.fileobj.sendall(data.upper())
                else:
                    sel.unregister(key.fileobj)
                    key.fileobj.close()

# socketserver — the stdlib's higher-level server framework
import socketserver
class EchoHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024)
        self.request.sendall(data.upper())
# with socketserver.ThreadingTCPServer(("127.0.0.1", 0), EchoHandler) as srv:
#     srv.serve_forever()   # threaded server, one thread per client

# socket.create_connection — convenience for TCP clients (handles DNS,
# IPv4/IPv6 fallback, and timeout in one call)
with socket.create_connection(("example.com", 80), timeout=5) as s:
    s.sendall(b"GET / HTTP/1.0\r\n\r\n")

The progression in Python networking is a ladder of abstraction: raw socket for full control, selectors for efficient multiplexing of many connections in one thread, socketserver for a threaded or forking server framework, and asyncio for high-concurrency asynchronous I/O with async/await syntax. Above all of these sit application libraries — http.client, urllib, requests, and full web frameworks — that almost everyone uses in practice. Understanding the socket layer matters not because you will write raw sockets often, but because it demystifies everything built on top: when a request hangs, a connection resets, or a timeout fires, the behaviour makes sense once you know the bind/listen/accept/recv lifecycle underneath.

✅ Expert tab complete

  • I know blocking sockets halt the thread until they complete
  • I can use selectors to watch many sockets in one thread
  • I know socketserver provides a threaded server framework
  • I know asyncio is built on these same socket primitives

Continue to http.server →

Sources

1
Python Standard Library — socket. docs.python.org/3/library/socket.html.
2
Python HOWTO — Socket Programming HOWTO. docs.python.org/3/howto/sockets.html.
3
Python Standard Library — selectors. docs.python.org/3/library/selectors.html.
4
Python Standard Library — socketserver. docs.python.org/3/library/socketserver.html.
Source confidence: High Last verified: Primary source: docs.python.org socket