Network Programming
Network programming means writing programs that talk to other programs over a network. If your app fetches data from a website, calls an API, connects to a database server, or sends messages to another computer - that is network programming.
It can sound scary at first, but the main idea is simple: one side sends data, the other side receives it, and both sides follow a set of agreed-upon rules called a protocol.
In this chapter, you will start from the basics and work your way up to practical patterns used in real projects.
Networking Basics
Section titled “Networking Basics”Before writing any code, it helps to understand a few fundamental ideas. These will help you debug problems faster and understand how data actually travels from one place to another.
IP Addresses
Section titled “IP Addresses”An IP address is a unique label that identifies a device on a network - like a home address for your computer.
- IPv4:
192.168.1.1 - IPv6:
2001:db8::1
If you send data to the wrong IP address, it goes to the wrong machine - just like sending a letter to the wrong house.
127.0.0.1 (also called localhost) is a special address that means “this same computer.”
A port identifies a specific app or service running on a machine. Think of it like a room number inside a building.
- The IP address tells you which building (device) to go to
- The port tells you which room (app) inside that building to knock on
Common examples:
80-> HTTP (regular websites)443-> HTTPS (secure websites)22-> SSH (remote terminal access)
Key idea:
IP Address = DevicePort = Application inside deviceSockets
Section titled “Sockets”A socket is the connection point between two machines. It combines an IP address and a port number into one endpoint:
(IP Address + Port)When two sockets connect to each other, data can flow in both directions between them.
Protocols: TCP and UDP
Section titled “Protocols: TCP and UDP”A protocol is just a set of rules that decides how data is sent. The two most common ones are TCP and UDP.
TCP (Transmission Control Protocol)
Section titled “TCP (Transmission Control Protocol)”- Makes sure data is delivered reliably
- Data arrives in the correct order
- A connection is established before any data is sent
- A little slower than UDP, but much safer
Use TCP for:
- web apps
- APIs
- logins
- payments
UDP (User Datagram Protocol)
Section titled “UDP (User Datagram Protocol)”- Very fast
- No guarantee that data arrives
- No strict ordering of packets
- No connection setup needed
Use UDP for:
- live voice or video calls
- multiplayer games
- situations where speed matters more than perfect delivery
Client-Server Flow
Section titled “Client-Server Flow”Most internet communication follows this simple pattern. The client asks for something, the server sends it back.
Examples:
- A browser (client) asks a web server for a page.
- A mobile app (client) asks an API server for user data.
Socket Programming in Python
Section titled “Socket Programming in Python”Socket programming gives you low-level control over how your program communicates over a network. Python has a built-in socket module that lets you do this, and it’s a great way to really understand how networking works under the hood.
Socket Lifecycle (TCP)
Section titled “Socket Lifecycle (TCP)”- Create a socket
- Bind it to an IP address and port (server side only)
- Start listening for incoming connections
- Accept a client that wants to connect
- Send and receive data as bytes
- Close the socket when done
This same pattern shows up in many real systems, even when a framework is hiding the details from you.
TCP vs UDP
Section titled “TCP vs UDP”Both have their place. Pick based on what your project needs:
- use TCP when correctness and reliability matter
- use UDP when speed matters and occasional data loss is acceptable
TCP Communication Flow
Section titled “TCP Communication Flow”TCP Server Example (Beginner Version)
Section titled “TCP Server Example (Beginner Version)”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()Explanation
Section titled “Explanation”AF_INETmeans we are using IPv4 addressesSOCK_STREAMmeans we are using TCPbind()attaches the server to a specific IP and portlisten()tells the server to start waiting for clientsaccept()waits until a client connects and returns a new socket for that clientrecv()receives incoming bytessend()sends bytes back
TCP Client Example
Section titled “TCP Client Example”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:
- Run the server file first.
- Run the client file in a separate terminal window.
- Watch the messages appear on both sides.
UDP Example
Section titled “UDP Example”UDP Server
Section titled “UDP Server”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)UDP Client
Section titled “UDP Client”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())With UDP there is no connect() handshake like TCP has. You just send data packets (called datagrams) directly to the other side.
HTTP Concepts
Section titled “HTTP Concepts”HTTP is the protocol that browsers, APIs, and most web services use to talk to each other. When you open a website, your browser sends an HTTP request and the server sends back an HTTP response.
Request-Response Cycle
Section titled “Request-Response Cycle”HTTP Request Structure
Section titled “HTTP Request Structure”GET /index.html HTTP/1.1Host: example.comUser-Agent: browserThis request is saying: “Please send me the file at /index.html from this website.”
HTTP Response Structure
Section titled “HTTP Response Structure”HTTP/1.1 200 OKContent-Type: text/html
<html>...</html>200 OK means the request worked. The body below the headers contains the actual content - HTML, JSON, an image, or whatever was requested.
HTTP Methods
Section titled “HTTP Methods”- GET -> fetch/read data
- POST -> send/create data
- PUT -> update existing data
- DELETE -> remove data
In REST APIs, these four methods map to the four basic operations: create, read, update, and delete.
Status Codes
Section titled “Status Codes”200-> Success201-> Created successfully400-> Bad request from the client401-> Unauthorized (not logged in)403-> Forbidden (logged in but not allowed)404-> Not Found500-> Server Error301-> Redirect (the page has moved)
Using the requests Library
Section titled “Using the requests Library”The requests library makes HTTP calls simple and easy to read. It is one of the most used Python libraries in backend development and automation work.
Installation
Section titled “Installation”pip install requestsGET Request
Section titled “GET Request”import requests
response = requests.get("https://api.github.com", timeout=10)
print(response.status_code)print(response.json())Always set a timeout so your program does not freeze and wait forever if a server is slow or unreachable.
POST Request
Section titled “POST Request”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 the body to be in JSON format.
Headers
Section titled “Headers”headers = { "Authorization": "Bearer token", "Content-Type": "application/json"}
response = requests.get("https://api.example.com", headers=headers)Headers carry extra information about the request - things like your auth token, what kind of data you are sending, or what language you prefer.
Handling Errors
Section titled “Handling Errors”import requests
response = requests.get("https://api.example.com")
if response.status_code == 200: print(response.json())else: print("Error:", response.status_code)Better way to handle errors in real projects:
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 approach prevents your program from crashing and gives you much clearer error messages when something goes wrong.
JSON and APIs
Section titled “JSON and APIs”Most APIs send and receive data in JSON format. JSON is just a way to represent structured data as text.
JSON Example
Section titled “JSON Example”{ "name": "Sahil", "age": 21}Python Handling
Section titled “Python Handling”data = response.json()print(data["name"])When reading JSON from an API, keep these things in mind:
- check that the keys you expect are actually there
- handle missing fields safely
- do not assume every response from an API looks exactly the same
Safe example using .get():
user = response.json()name = user.get("name", "Unknown")print(name)If the "name" key is missing, .get() returns "Unknown" instead of crashing.
Concurrency in Networking
Section titled “Concurrency in Networking”Real servers need to handle many clients at the same time. If you handle one client at a time and make all the others wait in line, your app will feel very slow.
Common ways to handle many clients at once:
- threads - easy to set up and understand
- processes - good for tasks that use a lot of CPU power
- async I/O - great when you have many connections that spend most of their time waiting
Threaded Server Example
Section titled “Threaded Server Example”import socketimport 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 is a simple approach and easy to understand. The downside is that it can use a lot of memory and system resources if thousands of clients connect at the same time.
Threading Flow
Section titled “Threading Flow”Async Networking with asyncio
Section titled “Async Networking with asyncio”Async is a good fit when your program spends most of its time waiting for network responses rather than doing heavy computation.
Instead of creating one thread per connection (which wastes resources), async lets a single event loop manage many connections at the same time by switching between them whenever one is waiting.
Async Example
Section titled “Async Example”import asyncio
async def main(): print("Start") await asyncio.sleep(2) print("End")
asyncio.run(main())For real async networking, most developers use libraries built on top of asyncio like aiohttp, httpx (in async mode), or async web frameworks.
Async Flow
Section titled “Async Flow”DNS and URL Flow
Section titled “DNS and URL Flow”Before your request can reach a server, something called DNS (Domain Name System) has to translate the website name into an IP address - because computers use IP addresses, not names.
Here is what happens step by step when you visit a website:
- You request
https://example.com - DNS looks up the IP address for
example.com - A TCP/TLS connection is created to that IP
- Your HTTP request is sent
- The server sends back a response
Understanding this flow helps a lot when debugging issues like “it works on my computer but not on a different network.”
Timeouts, Retries, and Reliability
Section titled “Timeouts, Retries, and Reliability”Network calls fail in the real world. Servers go down, internet connections drop, and responses can be slow. You should always write your networking code with the assumption that something might go wrong.
Good habits:
- always set a timeout so your program does not hang
- retry the request a few times before giving up
- log what went wrong so you can diagnose issues later
Simple retry example:
import timeimport 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)Security Basics
Section titled “Security Basics”- HTTPS is the secure version of HTTP
- It uses SSL/TLS encryption to protect data in transit
Never send passwords, tokens, or any sensitive data over plain HTTP in a real app. Always use HTTPS.
Common Risks
Section titled “Common Risks”- Man-in-the-middle attack - someone intercepts data between client and server
- Data interception - sensitive data is read by an unintended party
- Injection attacks - malicious input is used to manipulate the server
- Weak authentication - using easy-to-guess or reused passwords/tokens
- Leaking secrets in logs - accidentally printing API keys or passwords to log files
Best Practices
Section titled “Best Practices”- Always use HTTPS
- Validate all inputs before using them
- Use authentication tokens instead of passwords where possible
- Never hardcode secrets (API keys, passwords) in your source code
- Rate-limit sensitive endpoints to slow down attackers
- Be careful about what you log - never log sensitive data
Real-World Applications
Section titled “Real-World Applications”Network programming is used in:
- Web servers (Django, Flask)
- APIs
- Chat applications
- Multiplayer games
- Microservices (small services that talk to each other)
It is also used in:
- IoT devices (smart home gadgets, sensors)
- Payment gateways
- Cloud monitoring systems
Common Beginner Mistakes
Section titled “Common Beginner Mistakes”- forgetting to use
.encode()and.decode()when sending/receiving socket data - not setting a timeout on HTTP calls
- assuming network requests will always succeed
- only testing on your own machine and not thinking about real network conditions
- mixing business logic and networking code together in one big function
From Basic to Advanced Learning Path
Section titled “From Basic to Advanced Learning Path”- Build a simple TCP client and server that talk on your own machine
- Build a UDP echo app
- Use
requeststo call a public API - Add error handling and retries to your API calls
- Build a threaded server that handles multiple clients at the same time
- Learn async networking for handling many connections efficiently
- Add authentication, logging, and monitoring
Quick Practice Tasks
Section titled “Quick Practice Tasks”- Create an echo server - whatever the client sends, the server sends straight back.
- Build a small HTTP API checker that prints the status code and how long the request took.
- Add a timeout and retry logic to your API checker.
- Build a tiny single-room chat app using sockets.
These small projects are the fastest way to make networking concepts click.