Skip to content

Network Programming

Network programming means building programs that communicate with other programs over a network. If your app talks to a website, an API, a database server, a chat service, or another machine, you are doing network programming.

This topic can feel scary at first, but the core idea is simple: one side sends data, another side receives data, and both sides follow a protocol (set of rules).

In this chapter, you will learn from basic concepts to practical patterns used in real projects.


Before writing socket or HTTP code, understand these fundamentals. They help you debug faster and reason about how data moves.

An IP address uniquely identifies a device on a network.

  • IPv4: 192.168.1.1
  • IPv6: 2001:db8::1

Think of an IP address as a home address for a device.

  • If you send data to the wrong IP, it goes to the wrong machine.
  • 127.0.0.1 (or localhost) means “this same computer.”

A port identifies a specific process/service on a machine.

Examples:

  • 80 → HTTP
  • 443 → HTTPS
  • 22 → SSH

Think of a port as a room number inside a building.

  • IP identifies the building (device)
  • Port identifies the room (service/app)

Key idea:

IP Address = Device
Port = Application inside device

A socket is an endpoint for communication between two machines.

It combines:

(IP Address + Port)

When two sockets connect, data can flow in both directions.


Protocols define how data is sent.

  • reliable delivery
  • packets arrive in correct order
  • connection-oriented
  • slower than UDP, but safer

Use TCP for:

  • web apps
  • APIs
  • logins
  • payments
  • very fast
  • no delivery guarantee
  • no strict ordering
  • connectionless

Use UDP for:

  • live voice/video
  • multiplayer games
  • telemetry where speed matters more than perfect reliability

flowchart LR A[Client] -->|Request| B[Server] B -->|Response| A

Most internet communication follows this model. The client asks, the server answers.

Examples:

  • Browser (client) requests a webpage from a web server.
  • Mobile app (client) requests user data from an API server.

Socket programming gives you low-level control of network communication.

Python provides the built-in socket module, which is a great way to understand the core mechanics behind networking.

  1. Create socket
  2. Bind socket to IP + port (server side)
  3. Listen for connections
  4. Accept a client
  5. Send/receive bytes
  6. Close socket

This lifecycle appears in many real systems, even if frameworks hide details.


Both are useful. Choose based on project needs:

  • choose TCP for correctness and reliability
  • choose UDP for low-latency streaming-style workloads

sequenceDiagram participant Client participant Server Client->>Server: connect() Client->>Server: send() Server->>Client: response Client->>Server: close()

import socket
HOST = "127.0.0.1"
PORT = 8080
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen()
print(f"Server running on {HOST}:{PORT}")
while True:
client_socket, client_addr = server.accept()
print(f"Connected by {client_addr}")
data = client_socket.recv(1024)
message = data.decode("utf-8")
print("Client says:", message)
reply = "Hello from server"
client_socket.send(reply.encode("utf-8"))
client_socket.close()
  • AF_INET chooses IPv4
  • SOCK_STREAM means TCP
  • bind() attaches server to host and port
  • listen() starts waiting for clients
  • accept() returns a new socket for that client
  • recv() receives bytes
  • send() sends bytes

import socket
HOST = "127.0.0.1"
PORT = 8080
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST, PORT))
client.send("Hello server".encode("utf-8"))
response = client.recv(1024)
print("Server says:", response.decode("utf-8"))
client.close()

Try it:

  1. Run server file first.
  2. Run client file in another terminal.
  3. Observe request/response messages.

import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("localhost", 8080))
while True:
data, addr = server.recvfrom(1024)
print("Received:", data.decode())
server.sendto(b"Hello UDP Client", addr)
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(b"Hello UDP Server", ("localhost", 8080))
data, _ = client.recvfrom(1024)
print(data.decode())

In UDP there is no connect() handshake like TCP. You send datagrams directly.


HTTP is the protocol used by browsers, APIs, and many web services.

When you open a website, your browser sends an HTTP request and receives an HTTP response.


sequenceDiagram Client->>Server: HTTP Request Server->>Client: HTTP Response

GET /index.html HTTP/1.1
Host: example.com
User-Agent: browser

This request says: “Please send me /index.html from this host.”


HTTP/1.1 200 OK
Content-Type: text/html
<html>...</html>

200 OK means success. The body contains the actual data (HTML, JSON, image, etc.).


  • GET → Retrieve data
  • POST → Send data
  • PUT → Update data
  • DELETE → Remove data

In REST APIs, these methods map to create/read/update/delete operations.


  • 200 → Success
  • 201 → Created successfully
  • 400 → Bad request from client
  • 401 → Unauthorized
  • 403 → Forbidden
  • 404 → Not Found
  • 500 → Server Error
  • 301 → Redirect

The requests library makes HTTP calls easy and readable.

It is one of the most used Python libraries in backend and automation work.


Terminal window
pip install requests

import requests
response = requests.get("https://api.github.com", timeout=10)
print(response.status_code)
print(response.json())

Use timeout so your program does not hang forever if a server is slow.


import requests
data = {"username": "sahil", "password": "1234"}
response = requests.post("https://httpbin.org/post", json=data)
print(response.json())

Use json=data when the API expects JSON payload.


headers = {
"Authorization": "Bearer token",
"Content-Type": "application/json"
}
response = requests.get("https://api.example.com", headers=headers)

Headers are metadata (auth token, content type, language, etc.).


import requests
response = requests.get("https://api.example.com")
if response.status_code == 200:
print(response.json())
else:
print("Error:", response.status_code)

Better production-style handling:

import requests
try:
response = requests.get("https://api.example.com", timeout=10)
response.raise_for_status() # raises HTTPError for 4xx/5xx
data = response.json()
print(data)
except requests.exceptions.Timeout:
print("Request timed out")
except requests.exceptions.RequestException as exc:
print("Request failed:", exc)

This prevents crashes and gives better diagnostics.


APIs usually send/receive JSON.


{
"name": "Sahil",
"age": 21
}

data = response.json()
print(data["name"])

When parsing JSON from APIs:

  • validate expected keys
  • handle missing fields safely
  • do not assume every response has same shape

Example:

user = response.json()
name = user.get("name", "Unknown")
print(name)

Real servers serve many clients at the same time. If you handle one client at a time, other users wait.

Common concurrency options:

  • threads (easy to start)
  • processes (for CPU-heavy work)
  • async I/O (great for many network waits)

import socket
import threading
def handle_client(client_socket):
data = client_socket.recv(1024)
client_socket.send(b"OK")
client_socket.close()
server = socket.socket()
server.bind(("localhost", 8080))
server.listen()
while True:
client, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(client,))
thread.start()

This approach is simple but can consume many resources for huge traffic.


flowchart TD A[Client 1] --> S[Server] B[Client 2] --> S C[Client 3] --> S S -->|Threaded Handling| D[Parallel Execution]

Async is useful when tasks spend most time waiting on network I/O.

Instead of blocking one thread per connection, async lets one event loop manage many connections efficiently.


import asyncio
async def main():
print("Start")
await asyncio.sleep(2)
print("End")
asyncio.run(main())

Real-world async networking usually uses libraries like aiohttp, httpx (async mode), or async web frameworks.


flowchart LR A[Start Task] --> B[Await I/O] B --> C[Switch Task] C --> D[Resume Task]

Before your request reaches a server, DNS translates domain names to IP addresses.

Example:

  • You request https://example.com
  • DNS returns the server IP
  • TCP/TLS connection is created
  • HTTP request is sent
  • Server sends response

Understanding this helps when debugging “it works on one network but not another” issues.


Network calls fail in real life. Servers go down, packets drop, and latency spikes happen.

Always design for failure:

  • set timeouts
  • add retries for temporary errors
  • log failures clearly

Simple retry example:

import time
import requests
url = "https://api.example.com/health"
for attempt in range(3):
try:
r = requests.get(url, timeout=5)
r.raise_for_status()
print("Service is healthy")
break
except requests.exceptions.RequestException:
if attempt == 2:
print("Service check failed after retries")
else:
time.sleep(1)


  • Secure version of HTTP
  • Uses SSL/TLS encryption

Never send passwords or tokens over plain HTTP in real applications.


  • Man-in-the-middle attack
  • Data interception
  • Injection attacks
  • weak authentication
  • leaking secrets in logs

  • Always use HTTPS
  • Validate inputs
  • Use authentication tokens
  • never hardcode secrets in source code
  • rate-limit sensitive endpoints
  • sanitize and log safely

Network programming is used in:

  • Web servers (Django, Flask)
  • APIs
  • Chat applications
  • Multiplayer games
  • Microservices

Also used in:

  • IoT devices
  • payment gateways
  • cloud monitoring systems

  • forgetting encode()/decode() with sockets
  • not setting timeouts on HTTP calls
  • assuming network requests always succeed
  • testing only on localhost and ignoring real network behavior
  • mixing business logic and networking logic in one large function

  1. Build simple TCP client/server on localhost
  2. Build UDP echo app
  3. Use requests to call public APIs
  4. Add error handling and retries
  5. Build threaded server for multiple clients
  6. Learn async networking for high concurrency
  7. Add authentication, logging, and observability

  1. Create an echo server: whatever client sends, server returns back.
  2. Build a mini HTTP API checker that prints status and response time.
  3. Add timeout + retry to your API checker.
  4. Build a tiny chat app using sockets (single room).

These projects make networking concepts stick quickly.


flowchart TD A[Client] --> B[DNS] B --> C[Server IP] A --> D[HTTP Request] D --> C C --> E[Process Request] E --> F[Response] F --> A