Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
271: Sockets Programming Basics
A few years ago, I worked with a developer named Mark who was trying to build a real-time telemetry dashboard for a fleet of industrial sensors. His first instinct was to have each sensor send a standard HTTP POST request every half-second. It worked for ten sensors. When he scaled to a hundred, the overhead of the HTTP headers and the constant TCP handshake for every single tiny packet of data started melting his server's CPU. He came to me frustrated, wondering why "the web" was so slow. The answer was simple: he didn't need a web server; he needed a raw socket connection.
Sockets are the fundamental building blocks of network communication. While libraries like requests or frameworks like FastAPI handle the high-level "plumbing" for you, sockets allow you to talk directly to the transport layer of the network stack. In Python, the socket module gives you the ability to open a persistent pipe between two machines, allowing you to stream bytes back and forth without the baggage of HTTP overhead.
Setting Up the Listening Post
To get two programs talking, one has to act as the server—the "listener." Think of this as opening a specific door in your house and waiting for someone to knock. In Python, you create a socket object using socket.socket(). You'll almost always see AF_INET (which specifies IPv4) and SOCK_STREAM (which specifies TCP). TCP is crucial here because it ensures that the data arrives in the correct order and without gaps; if you don't care about reliability and just want speed, you'd use SOCK_DGRAM for UDP.
import socket
# Create a TCP/IP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the address and port
# '' means it will listen on all available network interfaces
server_socket.bind(('', 8080))
# Start listening for incoming connections
server_socket.listen(5)
print("Server is waiting for a connection...")
# accept() blocks the code until a client connects
client_connection, client_address = server_socket.accept()
print(f"Connected by {client_address}")
Notice the bind() and listen() calls. Binding tells the OS, "Any traffic coming in on port 8080 belongs to this script." The accept() method is where the magic happens—it's a blocking call, meaning your program just sits there and waits until a client actually initiates a handshake. When it does, accept() returns a new socket object specifically for that one client, leaving the original server_socket free to keep listening for other people.
Streaming Bytes Across the Wire
Once the connection is established, the server and client communicate using send() and recv(). Here is the part that trips up most beginners: sockets do not send strings. They send bytes. If you try to send a Python string directly, the interpreter will throw a TypeError. You have to encode your strings into bytes using .encode('utf-8') and decode them on the other end.
# Client-side connection logic
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('localhost', 8080))
# Send data (must be bytes!)
message = "GET_SYSTEM_STATUS"
client_socket.sendall(message.encode('utf-8'))
# Receive data (specify the buffer size, e.g., 1024 bytes)
data = client_socket.recv(1024)
print(f"Received from server: {data.decode('utf-8')}")
client_socket.close()
I prefer using sendall() over send(). The standard send() method might not actually send all the data you provide if the network buffer is full; it returns the number of bytes actually sent, leaving you to figure out how to send the rest. sendall() simply keeps looping until everything is transmitted or an error occurs. It's much safer for most general-purpose applications.
One final word of caution: recv() is also blocking. If the client is waiting for the server to speak, and the server is waiting for the client to speak, you've just created a "deadlock" where both programs hang forever. In production, you'd use timeouts or asynchronous I/O, but for basic socket programming, just ensure your communication protocol is clearly defined: "Client sends request, Server sends response, then both close."
📋 Practical Task
Exercise: Building a Remote System-Info Reporter
Your task is to create two Python scripts: a reporter.py (the server) and a collector.py (the client).
The reporter.py should:
- Listen on
localhostport9999. - Wait for a connection from the collector.
- When it receives the byte-string
b"STATUS", it should respond with a string containing the current operating system name (useplatform.system()from theplatformmodule) and the current time. - Close the connection after sending the info.
- Connect to the reporter on port
9999. - Send the command
"STATUS". - Print the received system information to the console.
- Close the socket.
sendall() or recv() calls.There are no comments for now.