The socket module provides low-level network programming. It's the foundation that higher-level libraries build on—understanding sockets helps you understand all networking.

TCP Client

Connect and send data:

import socket
 
# Create TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
# Connect to server
sock.connect(('example.com', 80))
 
# Send HTTP request
request = b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
sock.sendall(request)
 
# Receive response
response = sock.recv(4096)
print(response.decode())
 
sock.close()

TCP Server

Accept connections:

import socket
 
# Create server socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
# Bind and listen
server.bind(('localhost', 8080))
server.listen(5)
 
print("Server listening on port 8080")
 
while True:
    # Accept connection
    client, address = server.accept()
    print(f"Connection from {address}")
    
    # Handle client
    data = client.recv(1024)
    client.sendall(b'Hello from server!')
    client.close()

Context Managers

Better resource handling:

import socket
 
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect(('example.com', 80))
    sock.sendall(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
    response = sock.recv(4096)
# Socket automatically closed

UDP Client

Connectionless communication:

import socket
 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
# Send datagram (no connection needed)
sock.sendto(b'Hello, UDP!', ('localhost', 9999))
 
# Receive response
data, addr = sock.recvfrom(1024)
print(f"Received {data} from {addr}")
 
sock.close()

UDP Server

import socket
 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('localhost', 9999))
 
print("UDP server listening on port 9999")
 
while True:
    data, addr = sock.recvfrom(1024)
    print(f"Received {data} from {addr}")
    sock.sendto(b'ACK', addr)

Non-Blocking Sockets

import socket
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
 
try:
    sock.connect(('example.com', 80))
except BlockingIOError:
    pass  # Connection in progress
 
# Later, check if ready
sock.setblocking(True)
sock.sendall(b'GET / HTTP/1.1\r\n\r\n')

Timeouts

import socket
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)  # 5 second timeout
 
try:
    sock.connect(('example.com', 80))
    sock.sendall(b'GET / HTTP/1.1\r\n\r\n')
    response = sock.recv(4096)
except socket.timeout:
    print("Operation timed out")

DNS Lookup

import socket
 
# Get IP address
ip = socket.gethostbyname('example.com')
print(f"IP: {ip}")
 
# Get full info
info = socket.getaddrinfo('example.com', 80)
for family, socktype, proto, canonname, sockaddr in info:
    print(f"{sockaddr}")
 
# Reverse lookup
hostname = socket.gethostbyaddr('93.184.216.34')
print(f"Hostname: {hostname[0]}")

Get Local Info

import socket
 
# Local hostname
hostname = socket.gethostname()
print(f"Hostname: {hostname}")
 
# Local IP
local_ip = socket.gethostbyname(hostname)
print(f"Local IP: {local_ip}")
 
# Get all local IPs
for info in socket.getaddrinfo(hostname, None):
    print(info[4][0])

Simple Echo Server

import socket
 
def echo_server(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('', port))
        server.listen(1)
        
        print(f"Echo server on port {port}")
        
        while True:
            conn, addr = server.accept()
            with conn:
                print(f"Connected: {addr}")
                while True:
                    data = conn.recv(1024)
                    if not data:
                        break
                    conn.sendall(data)
 
echo_server(8888)

Threaded Server

Handle multiple clients:

import socket
import threading
 
def handle_client(conn, addr):
    print(f"Handling {addr}")
    with conn:
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data.upper())
    print(f"Disconnected: {addr}")
 
def threaded_server(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('', port))
        server.listen(5)
        
        while True:
            conn, addr = server.accept()
            thread = threading.Thread(target=handle_client, args=(conn, addr))
            thread.start()

Select-Based Server

Handle multiple connections efficiently:

import socket
import select
 
def select_server(port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.setblocking(False)
    server.bind(('', port))
    server.listen(5)
    
    inputs = [server]
    
    while inputs:
        readable, _, _ = select.select(inputs, [], [])
        
        for sock in readable:
            if sock is server:
                conn, addr = server.accept()
                conn.setblocking(False)
                inputs.append(conn)
            else:
                data = sock.recv(1024)
                if data:
                    sock.sendall(data)
                else:
                    inputs.remove(sock)
                    sock.close()

Socket Options

import socket
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
# Reuse address immediately after close
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
# Keep connection alive
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
 
# Set buffer sizes
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
 
# Disable Nagle's algorithm (send immediately)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

Unix Domain Sockets

For local inter-process communication:

import socket
import os
 
# Server
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
socket_path = '/tmp/mysocket'
 
if os.path.exists(socket_path):
    os.remove(socket_path)
 
server.bind(socket_path)
server.listen(1)
 
# Client
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect(socket_path)

When to Use socket

Use socket directly when:

  • Building custom protocols
  • Learning networking fundamentals
  • Need maximum control
  • Performance-critical applications

Use higher-level libraries when:

  • HTTP requests → requests, httpx
  • Web servers → Flask, FastAPI
  • Async networking → asyncio
  • Simple clients → urllib

The socket module is the foundation—everything else builds on it.

React to this post: