Skip to content

Python Networking

Python provides comprehensive support for network programming, ranging from low-level socket access to high-level libraries for common application protocols.

  • Low-Level Access: The socket module provides access to the BSD socket interface, allowing direct implementation of clients and servers using TCP (connection-oriented) and UDP (connectionless) protocols.
  • High-Level Access: Standard libraries offer abstractions for protocols like HTTP (http.client, urllib.request), FTP (ftplib), SMTP (smtplib), POP3 (poplib), IMAP (imaplib), etc. Third-party libraries like requests further simplify HTTP interactions.

This chapter focuses on the fundamentals of low-level socket programming using the socket module.

Sockets are the endpoints of a two-way communication link between two programs running on a network. A socket is bound to a port number so that the TCP layer can identify the application that data is destined to be sent to.

Sockets facilitate communication within a single process, between processes on the same machine (using Unix domain sockets or loopback), or between processes on different machines across the internet.

Key Socket Terminology:

TermDescription
Domain (or Family)Specifies the protocol family. Common values:
AF_INET: IPv4 protocols.
AF_INET6: IPv6 protocols.
AF_UNIX: Unix domain sockets (for local inter-process communication).
TypeSpecifies the communication semantics. Common values:
SOCK_STREAM: Connection-oriented, reliable stream (TCP).
SOCK_DGRAM: Connectionless, unreliable datagrams (UDP).
ProtocolUsually 0, allowing the system to choose the appropriate protocol based on domain and type (e.g., IPPROTO_TCP for SOCK_STREAM, IPPROTO_UDP for SOCK_DGRAM).
HostnameIdentifies a network interface. Can be a domain name (e.g., ‘www.example.com’), an IPv4 address string (e.g., ‘192.168.1.1’), an IPv6 address string, an empty string ('') representing INADDR_ANY (listen on all available interfaces), or 'localhost' / '127.0.0.1' for the loopback interface.
PortA number (0-65535) identifying a specific process or service on a host. Ports 0-1023 are well-known ports usually requiring root/admin privileges.

The core of socket programming in Python is the socket module.

Use socket.socket() to create a new socket object.

s = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0)

Common parameters:

  • family: Socket domain (e.g., socket.AF_INET for IPv4).
  • type: Socket type (e.g., socket.SOCK_STREAM for TCP, socket.SOCK_DGRAM for UDP).
  • proto: Protocol number (usually 0).

Sockets should be closed when no longer needed. The with statement ensures sockets are closed automatically, similar to file handling.

import socket
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# Use the socket 's' here
print("Socket created successfully.")
# Socket 's' will be closed automatically upon exiting the 'with' block
except socket.error as err:
print(f"Socket creation failed with error: {err}")
MethodDescription
s.bind((host, port))Binds the socket to a specific network interface (host) and port number.
s.listen(backlog)Enables the server to accept connections. backlog specifies the maximum number of queued connections.
s.accept()Waits for an incoming client connection. Returns a tuple (conn, addr) where conn is a new socket object for communication with the client, and addr is the client’s address tuple.
MethodDescription
s.connect((host, port))Connects the client socket to a server at host and port.
MethodDescription
s.send(bytes)Transmits TCP data. Data must be bytes. Returns the number of bytes sent.
s.recv(bufsize)Receives TCP data. bufsize is the maximum amount of data (in bytes) to receive at once. Returns received data as bytes.
s.sendall(bytes)Transmits all data in bytes for TCP. Handles potential short sends.
s.sendto(bytes, address)Transmits UDP datagram (bytes) to the specified address tuple (host, port).
s.recvfrom(bufsize)Receives UDP datagram. Returns a tuple (bytes, address) where bytes is the data and address is the sender’s address.
s.close()Closes the socket. Automatically handled by with statement.
socket.gethostname()Returns the standard hostname for the local machine.

Important: In Python 3, socket methods send(), recv(), sendto(), and recvfrom() operate on bytes objects, not strings. You must encode strings to bytes before sending and decode bytes to strings after receiving.

This server listens on a specific port, accepts client connections, reads data from the client, and sends the same data back (echoes it).

server.py
#!/usr/bin/env python3
import socket
HOST = '127.0.0.1' # Standard loopback interface address (localhost)
PORT = 65432 # Port to listen on (non-privileged ports are > 1023)
def run_server():
# Use 'with' statement for automatic socket closing
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.bind((HOST, PORT))
s.listen()
print(f"Server listening on {HOST}:{PORT}")
# Wait for a connection
conn, addr = s.accept() # Blocking call
# Use 'with' for the client connection socket as well
with conn:
print(f"Connected by {addr}")
while True:
# Receive data (up to 1024 bytes)
data = conn.recv(1024)
if not data:
# If recv returns empty bytes, client closed connection
print(f"Client {addr} disconnected.")
break
# Decode received bytes to string for printing
print(f"Received from {addr}: {data.decode('utf-8')}")
# Echo data back to client (encode string to bytes)
conn.sendall(data)
print(f"Echoed back to {addr}")
except socket.error as e:
print(f"Socket error: {e}")
except KeyboardInterrupt:
print("\nServer shutting down.")
if __name__ == "__main__":
run_server()

This client connects to the server, sends a message, and prints the echoed response.

client.py
#!/usr/bin/env python3
import socket
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 65432 # The port used by the server
def run_client(message):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print(f"Connected to server {HOST}:{PORT}")
# Encode the message to bytes before sending
message_bytes = message.encode('utf-8')
s.sendall(message_bytes)
print(f"Sent: {message}")
# Receive the echoed data
data = s.recv(1024)
# Decode the received bytes to string
received_message = data.decode('utf-8')
print(f"Received echo: {received_message}")
except socket.error as e:
print(f"Socket error: {e}")
except ConnectionRefusedError:
print(f"Connection refused. Is the server running on {HOST}:{PORT}?")
if __name__ == "__main__":
message_to_send = "Hello, Server!"
run_client(message_to_send)
  1. Open a terminal and run the server: python server.py
  2. Open another terminal and run the client: python client.py

Server Output:

Server listening on 127.0.0.1:65432
Connected by ('127.0.0.1', <client_port>)
Received from ('127.0.0.1', <client_port>): Hello, Server!
Echoed back to ('127.0.0.1', <client_port>)
Client ('127.0.0.1', <client_port>) disconnected.

Client Output:

Connected to server 127.0.0.1:65432
Sent: Hello, Server!
Received echo: Hello, Server!

Python’s standard library includes modules for common internet protocols:

ProtocolPrimary UseCommon Port(s)Python Module(s)
HTTP/HTTPSWeb access80, 443urllib.request, http.client (Lower level). requests (Third-party, recommended)
FTPFile transfer21ftplib
SMTPSending email25, 587, 465smtplib
POP3Fetching email110, 995poplib
IMAP4Fetching email143, 993imaplib
NNTPUsenet news119, 563nntplib
TelnetRemote login23telnetlib
XML-RPCRemote procedure callsVariesxmlrpc.client, xmlrpc.server

For web interactions (HTTP/HTTPS), the third-party requests library is widely used and generally preferred over urllib for its simpler API.

Socket programming is a deep topic. Explore these resources: