Top Stories

Python Network Programming: A Complete Practical Guide
Ankaj Gupta
June 03, 2026

Python Network Programming: A Complete Practical Guide

Python Programming

Python Network Programming: Build Real Clients & Servers

Go from zero to running your own TCP echo server, HTTP client, and async chat backend — with hands-on code you can run in two terminals today.

~35 min read Python 3.10+ 6 phases Beginner → Intermediate
Python Network Programming — clients, servers, and network protocols

6

Learning phases

15+

Runnable examples

4

Practice projects

0

Paid libs required

Quick wins — What you will learn

IP, ports & sockets (TCP vs UDP)

TCP echo server + client

Multi-client with threads & asyncio

HTTP, TLS & secure connections

Your learning path

Pro tip: For production web apps, use FastAPI or Django. Learn raw sockets first — you will understand timeouts, connection pools, and WebSockets much faster.

Before you start

You will need

  • • Python 3.10 or newer installed
  • • Basic Python (functions, classes, exceptions)
  • • Terminal access to run two processes (server + client)
  • • Optional: pip install requests for HTTP examples

Helpful tools

  • • netstat / ss — see open ports
  • • telnet host port or nc — quick connectivity tests
  • • Wireshark — inspect packets (advanced)

Network basics every Python developer should know

When your Python program talks to another machine, it uses a stack of protocols. You rarely touch layers 1–3 directly; most app code lives at the transport and application layers.

Protocol stack (simplified)
┌─────────────────────────────────────────┐
│  Application   HTTP · DNS · your app    │  ← you write here
├─────────────────────────────────────────┤
│  Transport     TCP  │  UDP              │  ← socket module
├─────────────────────────────────────────┤
│  Network       IP (192.168.x.x)         │
├─────────────────────────────────────────┤
│  Link          Wi‑Fi · Ethernet         │  ← OS / hardware
└─────────────────────────────────────────┘

IP address + port = endpoint

A socket is identified by (IP, port, protocol). Example: your browser connects to 142.250.80.46:443 (Google, HTTPS).

TCP Reliable
  • Connection handshake first
  • Guaranteed order & delivery
  • Retransmits lost packets

HTTP · SSH · MongoDB · most APIs

UDP Fast
  • No connection setup
  • Lower latency
  • May lose or reorder packets

DNS · gaming · video · broadcasts

How TCP connects (3-way handshake)

1. SYN

Client → Server

2. SYN-ACK

Server → Client

3. ACK

Connection ready

Ports you will see every day

:80 HTTP :443 HTTPS :22 SSH :27017 MongoDB :9999 our demos
Term Meaning
127.0.0.1 Loopback — same machine only (localhost)
0.0.0.0 Bind — listen on all network interfaces
Port 0–65535; ports < 1024 often need admin on Linux
Hostname Resolved to IP via DNS (socket.gethostbyname)

The Python socket module

The standard library socket module wraps Berkeley sockets — the same API used in C, Go, and Rust.

Server flow

socket() → bind() → listen() → accept() → recv/send

Client flow

socket() → connect() → send/recv
create_socket.py Python
import socket

# IPv4 + TCP (most common)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1

Phase 1 — TCP client & server

~10 min · Your first working network app

Step 1: Minimal TCP echo server

This server listens on port 9999, accepts one connection at a time, and echoes bytes back.

server.py Python
# server.py
import socket

HOST = "127.0.0.1"   # localhost only — safe for learning
PORT = 9999

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((HOST, PORT))
    server.listen(5)
    print(f"Listening on {HOST}:{PORT}...")

    conn, addr = server.accept()
    with conn:
        print(f"Connected by {addr}")
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)  # echo back

SO_REUSEADDR: Allows rebinding immediately after restart instead of waiting for TIME_WAIT.

Step 2: TCP client

Run the server in one terminal, then run the client in another:

client.py Python
# client.py
import socket

HOST = "127.0.0.1"
PORT = 9999

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT))
    sock.sendall(b"Hello from Python client!")
    response = sock.recv(1024)

print("Server replied:", response.decode())

Try it yourself — 60 seconds

  1. Save server.py and client.py
  2. Terminal 1: python server.py
  3. Terminal 2: python client.py

Expected output

# server.py
Listening on 127.0.0.1:9999...
Connected by ('127.0.0.1', 54321)

# client.py
Server replied: Hello from Python client!

Step 3: Send and receive strings safely

Sockets work with bytes, not str. Always encode before send and decode after recv. For structured messages, prefix length or use a delimiter/newline protocol.

def send_msg(sock: socket.socket, text: str) -> None:
    sock.sendall(text.encode("utf-8"))

def recv_line(sock: socket.socket, bufsize: int = 4096) -> str:
    data = sock.recv(bufsize)
    if not data:
        raise ConnectionError("Peer closed connection")
    return data.decode("utf-8")

Important: recv(1024) may return fewer than 1024 bytes. For large payloads, loop until you have the full message or use a framing scheme.

2

Phase 2 — UDP (connectionless)

~5 min · Fire-and-forget messaging

UDP has no connect() handshake on the server side — you recvfrom() and reply with sendto().

UDP server

# udp_server.py
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1", 9998))
print("UDP server on 9998...")

while True:
    data, addr = sock.recvfrom(1024)
    print(f"From {addr}: {data.decode()}")
    sock.sendto(b"ACK: " + data, addr)

UDP client

# udp_client.py
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b"Ping", ("127.0.0.1", 9998))
reply, _ = sock.recvfrom(1024)
print(reply.decode())
3

Phase 3 — Handling multiple clients

~8 min · Scale beyond one connection

A single-threaded server blocks on accept() — only one client at a time. Pick your strategy:

1

Thread per client

Simple for moderate load.

Best for: learning, <100 clients
2

select / poll

One thread, many sockets.

Best for: legacy servers
Recommended 3

asyncio

Thousands of idle connections.

Best for: chat, WebSockets

Threaded echo server

# threaded_server.py
import socket
import threading

HOST, PORT = "127.0.0.1", 9999

def handle_client(conn: socket.socket, addr):
    with conn:
        print(f"Thread {threading.current_thread().name} serving {addr}")
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((HOST, PORT))
        server.listen()
        print(f"Threaded server on {HOST}:{PORT}")
        while True:
            conn, addr = server.accept()
            t = threading.Thread(target=handle_client, args=(conn, addr), daemon=True)
            t.start()

if __name__ == "__main__":
    main()
4

Phase 4 — HTTP networking

~8 min · How the web actually works

HTTP runs on top of TCP (port 80 or 443). You can craft raw HTTP by hand to learn the protocol, but production code uses higher-level libraries.

Raw HTTP over a socket (educational)

import socket

host = "example.com"
port = 80

request = (
    f"GET / HTTP/1.1\r\n"
    f"Host: {host}\r\n"
    f"Connection: close\r\n"
    f"\r\n"
).encode()

with socket.create_connection((host, port)) as sock:
    sock.sendall(request)
    chunks = []
    while True:
        part = sock.recv(4096)
        if not part:
            break
        chunks.append(part)

response = b"".join(chunks).decode("utf-8", errors="replace")
print(response[:500])  # status line + headers + start of body

urllib (standard library)

from urllib.request import urlopen
from urllib.error import URLError, HTTPError

url = "https://httpbin.org/get"

try:
    with urlopen(url, timeout=10) as resp:
        print("Status:", resp.status)
        print("Headers:", dict(resp.headers))
        body = resp.read().decode()
        print(body[:300])
except HTTPError as e:
    print("HTTP error:", e.code, e.reason)
except URLError as e:
    print("Network error:", e.reason)

requests library (recommended for apps)

pip install requests
import requests

resp = requests.get(
    "https://api.github.com/users/python",
    timeout=10,
    headers={"Accept": "application/json"},
)
resp.raise_for_status()
data = resp.json()
print(data["login"], data.get("public_repos"))
5

Phase 5 — TLS / SSL encryption

~7 min · Encrypt traffic in transit

Wrap a TCP socket with the ssl module to get HTTPS-style encryption. Never invent your own crypto — use TLS.

TLS client (connect to HTTPS server)

import socket
import ssl

hostname = "www.python.org"
port = 443

context = ssl.create_default_context()  # verifies server certificate

with socket.create_connection((hostname, port)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print("TLS version:", ssock.version())
        ssock.sendall(b"GET / HTTP/1.1\r\nHost: www.python.org\r\n\r\n")
        print(ssock.recv(2048).decode()[:400])

Production tip: Use requests or httpx for HTTPS — they handle certificates, redirects, and timeouts correctly.

TLS server (self-signed for local testing)

Generate a cert with OpenSSL, then wrap the server socket:

OpenSSL: create self-signed cert (click to expand)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -days 365 -nodes -subj "/CN=localhost"
import socket
import ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain("cert.pem", "key.pem")

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.bind(("127.0.0.1", 10023))
    sock.listen(5)
    with context.wrap_socket(sock, server_side=True) as ssock:
        conn, addr = ssock.accept()
        with conn:
            data = conn.recv(1024)
            conn.sendall(b"Secure echo: " + data)
6

Phase 6 — asyncio streams (modern Python)

~8 min · High-concurrency servers

asyncio uses a single thread and an event loop. asyncio.start_server is the high-level API for TCP servers.

Async echo server

# async_server.py
import asyncio

async def handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
    addr = writer.get_extra_info("peername")
    print(f"Client connected: {addr}")
    while True:
        data = await reader.read(1024)
        if not data:
            break
        writer.write(data)
        await writer.drain()
    writer.close()
    await writer.wait_closed()

async def main():
    server = await asyncio.start_server(handle, "127.0.0.1", 9997)
    addrs = ", ".join(str(s.getsockname()) for s in server.sockets)
    print(f"Serving on {addrs}")
    async with server:
        await server.serve_forever()

asyncio.run(main())

Async client

# async_client.py
import asyncio

async def main():
    reader, writer = await asyncio.open_connection("127.0.0.1", 9997)
    writer.write(b"Hello asyncio!")
    await writer.drain()
    reply = await reader.read(1024)
    print("Reply:", reply.decode())
    writer.close()
    await writer.wait_closed()

asyncio.run(main())

Which Python module should I use?

socket

Raw TCP/UDP

asyncio

Async TCP servers

urllib

Simple HTTP GET

requests

Production HTTP client

ssl

TLS on sockets

httpx / aiohttp

Async HTTP

Choosing the right approach

Use case Best tool
REST API consumer requests or httpx
Custom TCP protocol socket or asyncio streams
Web app / JSON API server FastAPI, Flask, Django
DNS / discovery / gaming UDP sockets
Encrypted custom protocol ssl wrapping socket + asyncio

Self-check quiz

Click each question to reveal the answer. Be honest — if you miss one, re-read that section.

What is the difference between TCP and UDP? ▼
TCP is connection-oriented and guarantees delivery order; UDP is connectionless and faster but may drop packets.
Why must you use bytes with sockets, not str? ▼
Sockets transfer raw binary on the wire. Encode strings with .encode("utf-8") before send; decode after recv.
When should you use asyncio instead of threads? ▼
When you have many connections that spend most of their time waiting (I/O-bound). asyncio handles thousands of idle clients with one thread.
What does 127.0.0.1 mean vs 0.0.0.0 when binding? ▼
127.0.0.1 = localhost only. 0.0.0.0 = listen on all network interfaces (use with firewall care).

Common mistakes to avoid

Assuming one recv() returns a full message

Forgetting to set socket timeouts (hangs forever)

Binding to 0.0.0.0 on untrusted networks without a firewall

Mixing threads and asyncio on the same socket

Sending str instead of bytes over sockets

Disabling TLS certificate verification in production

# Always set a timeout on blocking sockets
sock.settimeout(30.0)

# Prefer context managers
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    ...

Practice projects

Level up by building these — each maps to a phase in this guide.

Easy 01

Line-based chat server

Broadcast each line to all clients. Use \n as delimiter. → Phase 3

Medium 02

Port scanner

connect_ex() on ports 1–1024 with a thread pool. → Phase 1 & 3

Medium 03

File transfer over TCP

Send 8-byte size header, then chunks + SHA-256 verify. → Phase 1

Hard 04

Async weather fetcher

Fetch 5 cities concurrently with asyncio + aiohttp. → Phase 4 & 6

Interactive learning checklist

Check off skills as you master them — your browser will remember (local only).

You're ready to go deeper

Python network programming starts with sockets: bind, listen, connect, send, and receive bytes. Layer on HTTP, TLS, and asyncio as you grow.

Next steps: build the chat server → try FastAPI → explore WebSockets. The socket mental model stays with you.

Python python network programming Python programming
Read
Ankaj Gupta
December 18, 2022

ModuleNotFoundError: No module named 'phonenumbers'

Installing Django Phone Number Field

This article provides a comprehensive guide for installing and setting up django-phonenumber-field in your Django project. This library provides a Django model field and form fields for storing and validating phone numbers using the phonenumbers library.

Prerequisites

Before installing django-phonenumber-field, ensure you have the following installed:

  • Python 3.6 or higher
  • Django 2.2 or higher
  • pip package manager

Installation Steps

Step 1: Install django-phonenumber-field

Install the main package using pip:

pip install django-phonenumber-field

Step 2: Install django-phonenumbers

Install the phonenumbers package which provides phone number parsing and validation:

pip install django-phonenumbers

Step 3: Add to INSTALLED_APPS

Add phonenumber_field to your INSTALLED_APPS in your Django settings file:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'phonenumber_field',  # Add this line
    'myapp',
]

Step 4: Configure Settings (Optional)

Configure the default region for phone number parsing in your settings:

# In settings.py
PHONENUMBER_DEFAULT_REGION = 'US'  # Default to US region

Note: The default region is used when parsing phone numbers that don't include a country code. Change this to match your target audience's primary region.

Usage Example

Here's how to use PhoneNumberField in your Django models:

from django.db import models
from phonenumber_field.modelfields import PhoneNumberField

class UserProfile(models.Model):
    name = models.CharField(max_length=100)
    phone_number = PhoneNumberField()  # International format
    local_phone = PhoneNumberField(region='US')  # US format
    
    def __str__(self):
        return self.name

Using in Forms

The library also provides form fields for easy integration:

from django import forms
from phonenumber_field.formfields import PhoneNumberField

class UserProfileForm(forms.Form):
    name = forms.CharField(max_length=100)
    phone_number = PhoneNumberField()
    
    def clean_phone_number(self):
        phone_number = self.cleaned_data['phone_number']
        # Automatic validation happens here
        return phone_number

Key Features

International Format

Stores phone numbers in E.164 international format

Automatic Validation

Validates phone numbers during form submission

Region Support

Supports different regions and formats

Database Efficient

Uses CharField with max_length=128 in database

Common Installation Commands

Quick Install: Use these commands if you need to install both packages together:

pip install django-phonenumber-field django-phonenumbers

Summary

django-phonenumber-field provides a robust solution for handling phone numbers in Django applications. It offers international format support, automatic validation, and seamless integration with Django models and forms.

After installation, simply add phonenumber_field to your INSTALLED_APPS and start using PhoneNumberField in your models.

django Python programming
Read
Ankaj Gupta
August 31, 2020

How to calculate content-length python

Python Networking

Calculate Content Length with Python Requests

Need to know how much data a URL returns? The requests package makes it simple to fetch a page and inspect the raw payload size. This walkthrough covers installation, a sample script, and best practices.

Prerequisites

Environment

  • Python 3.8 or higher
  • Terminal access (Command Prompt, PowerShell, or shell)
  • Internet connection to reach target URLs

Install Requests

# Windows
pip install requests

# macOS / Linux
pip3 install requests

Already installed? Run pip show requests to confirm the version.

Step-by-Step Implementation

  1. 1. Import the library.

    Use a simple import requests. If you see no errors, the installation succeeded.

  2. 2. Send a GET request.

    Wrap the call in requests.get(). Consider using timeout to avoid long waits on slow servers.

  3. 3. Inspect the response.

    The content attribute returns the raw bytes. Use Python’s len() to measure the payload size.

Example Script

import requests

def get_content_length(url: str, timeout: int = 10) -> int:
    """Return the size (in bytes) of the response body for a URL."""

    response = requests.get(url, timeout=timeout)
    response.raise_for_status()  # raise an exception for 4xx/5xx errors
    return len(response.content)

if __name__ == "__main__":
    target_url = "https://www.example.com"
    try:
        size_in_bytes = get_content_length(target_url)
        print(f"Content length for {target_url}: {size_in_bytes} bytes")
    except requests.exceptions.RequestException as exc:
        print(f"Request failed: {exc}")

Why handle exceptions? Production scripts should catch network issues (timeouts, DNS errors) and HTTP errors to avoid silent failures or half-fetched data.

Sample Output

Content length for https://www.example.com: 12564 bytes

Troubleshooting

Common Issues

  • Timeouts: Increase the timeout value or verify connectivity.
  • Redirect loops: Check response.history for multiple hops.
  • Compressed responses: Some servers gzip content—length represents compressed bytes.

Tips

  • Inspect response.headers["Content-Length"] if provided.
  • Use streaming (stream=True) for large downloads to avoid memory spikes.
  • Combine with logging to audit request performance.

Where to Go Next

  • Convert byte counts to kilobytes/megabytes for easier reporting.
  • Integrate with schedulers (cron, Windows Task Scheduler) to track changes over time.
  • Benchmark multiple URLs and visualize results in a dashboard (e.g., Grafana, Tableau).
Python programming
Read
AttributeError: module 'whois' has no attribute 'whois'
Ankaj Gupta
August 04, 2020

AttributeError: module 'whois' has no attribute 'whois'

How to Fix AttributeError: Module 'whois' Has No Attribute 'whois'

In this post, I am going to tell you how you can easily fix the problem of AttributeError: module 'whois' has no attribute 'whois' error.

The Problem

Python code given below:

import whois

host = 'google.com'
res = whois.whois(host)
print(res)

This code results in: AttributeError: module 'whois' has no attribute 'whois'

The Solution

Switch module from pip install whois to pip install python-whois.

About python-whois:

  • • Create a simple importable Python module which will produce parsed WHOIS data for a given domain
  • • Able to extract data for all the popular TLDs (com, net, org, etc.)
  • • Query a WHOIS server directly instead of going through an intermediate web service
  • • Works with Python 2 & 3

Step-by-Step Fix

Step 1: Uninstall whois Module

Uninstall the whois module from your distribution:

pip uninstall whois

Step 2: Install python-whois

Now, install python-whois:

pip install python-whois

Step 3: Execute Your Code

Finally, execute your Python script:

python whois.py

Windows

python3 whois.py

Linux/Mac

Corrected Code

After installing python-whois, your code will work correctly:

import whois

host = 'google.com'
res = whois.whois(host)
print(res)

Example Output:

{
    "domain_name": "google.com",
    "whois_server": "whois.markmonitor.com",
    "registrar": "MarkMonitor, Inc.",
    "name_servers": [...],
    "creation_date": "1997-09-15T00:00:00",
    "expiration_date": "2028-09-14T00:00:00",
    ...
}

Why Does This Happen?

❌ Package Name Conflict

The whois package is different from python-whois. They have incompatible APIs.

✅ Correct Package

The python-whois package provides the WHOIS functionality you need.

Package Comparison

Package Description Status
whois Different package with incompatible API ❌ Not for this use case
python-whois Parsed WHOIS data for domains ✅ Recommended

Advanced Usage

Fetching Multiple Domains

import whois

domains = ['google.com', 'github.com', 'stackoverflow.com']

for domain in domains:
    try:
        info = whois.whois(domain)
        print(f"{domain}: {info.registrar}")
    except whois.parser.PywhoisError as e:
        print(f"{domain}: Error - {e}")

Extracting Specific Information

import whois

domain = 'google.com'
info = whois.whois(domain)

print(f"Domain: {info.domain_name}")
print(f"Registrar: {info.registrar}")
print(f"Creation Date: {info.creation_date}")
print(f"Expiration Date: {info.expiration_date}")
print(f"Name Servers: {info.name_servers}")

Troubleshooting

Still Not Working?

If there is still a problem, try the following:

  1. Restart your device and run it again
  2. Clear Python cache: find . -type d -name __pycache__ -exec rm -r {} +
  3. Check Python version compatibility
  4. Verify installation: pip list | grep -i whois

Common Errors

  • • Connection timeout: Check your internet connection
  • • TLD not supported: Some TLDs may not be supported by python-whois
  • • Rate limiting: Some WHOIS servers limit query frequency

Summary

To fix the AttributeError: module 'whois' has no attribute 'whois' error:

  1. Uninstall the incorrect package: pip uninstall whois
  2. Install the correct package: pip install python-whois
  3. Run your code again: python whois.py

The python-whois package provides a clean API to fetch and parse WHOIS data for any domain.

Python programming
Read