Python 网络编程
Python 网络编程
Section titled “Python 网络编程”Python 为网络编程提供了全面的支持,范围涵盖从低级套接字(socket)访问到常见应用协议的高级库。
- 低级访问:
socket模块提供对 BSD 套接字(socket)接口的访问,允许使用 TCP(面向连接)和 UDP(无连接)协议直接实现客户端和服务器。 - 高级访问:标准库提供了诸如 HTTP (
http.client,urllib.request)、FTP (ftplib)、SMTP (smtplib)、POP3 (poplib)、IMAP (imaplib) 等协议的抽象。第三方库,例如requests,进一步简化了 HTTP 交互。
本章重点介绍使用 socket 模块进行低级套接字编程的基础知识。
什么是套接字?
Section titled “什么是套接字?”套接字(Socket)是网络上运行的两个程序之间双向通信链接的端点。套接字绑定到一个端口号(port number)上,以便 TCP 层能够识别数据要发送到的应用程序。
套接字促进了同一进程内、同一机器上进程间(使用 Unix 域套接字或环回接口),或互联网上不同机器间进程之间的通信。
关键套接字术语:
| 术语 | 描述 |
|---|---|
| 域(或族) | 指定协议族。常见值:AF_INET: IPv4 协议。AF_INET6: IPv6 协议。AF_UNIX: Unix 域套接字(用于本地进程间通信)。 |
| 类型 | 指定通信语义。常见值:SOCK_STREAM: 面向连接的可靠流(TCP)。SOCK_DGRAM: 无连接的不可靠数据报(UDP)。 |
| 协议 | 通常为 0,允许系统根据域和类型选择适当的协议(例如,SOCK_STREAM 对应 IPPROTO_TCP,SOCK_DGRAM 对应 IPPROTO_UDP)。 |
| 主机名 | 标识一个网络接口。可以是域名(例如 ‘www.example.com’)、IPv4 地址字符串(例如 ‘192.168.1.1’)、IPv6 地址字符串、代表 INADDR_ANY 的空字符串 ('')(监听所有可用接口),或用于环回接口的 'localhost' / '127.0.0.1'。 |
| 端口 | 一个数字 (0-65535),用于标识主机上的特定进程或服务。端口 0-1023 是众所周知端口,通常需要 root/管理员权限。 |
socket 模块
Section titled “socket 模块”Python 中套接字编程的核心是 socket 模块。
创建套接字:
Section titled “创建套接字:”使用 socket.socket() 创建一个新的套接字对象。
s = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0)常见参数:
family: 套接字域(例如,IPv4 使用socket.AF_INET)。type: 套接字类型(例如,TCP 使用socket.SOCK_STREAM,UDP 使用socket.SOCK_DGRAM)。proto: 协议号(通常为 0)。
使用 with 语句(推荐):
Section titled “使用 with 语句(推荐):”不再需要套接字时应该将其关闭。with 语句确保套接字自动关闭,类似于文件处理。
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' blockexcept socket.error as err: print(f"Socket creation failed with error: {err}")基本套接字方法:
Section titled “基本套接字方法:”服务器套接字方法(TCP):
Section titled “服务器套接字方法(TCP):”| 方法 | 描述 |
|---|---|
s.bind((host, port)) | 将套接字绑定到特定的网络接口 (host) 和 port 端口号上。 |
s.listen(backlog) | 使服务器能够接受连接。backlog 指定排队连接的最大数量。 |
s.accept() | 等待传入的客户端连接。返回一个元组 (conn, addr),其中 conn 是用于与客户端通信的新的套接字对象,addr 是客户端的地址元组。 |
客户端套接字方法(TCP):
Section titled “客户端套接字方法(TCP):”| 方法 | 描述 |
|---|---|
s.connect((host, port)) | 将客户端套接字连接到位于 host 和 port 的服务器。 |
通用套接字方法(TCP 和 UDP):
Section titled “通用套接字方法(TCP 和 UDP):”| 方法 | 描述 |
|---|---|
s.send(bytes) | 发送 TCP 数据。数据必须是 bytes 类型。返回发送的字节数。 |
s.recv(bufsize) | 接收 TCP 数据。bufsize 是单次接收的最大数据量(字节)。返回接收到的数据作为 bytes。 |
s.sendall(bytes) | 发送 bytes 中的所有 TCP 数据。处理潜在的短发送问题。 |
s.sendto(bytes, address) | 将 UDP 数据报 (bytes) 发送到指定的地址元组 (host, port)。 |
s.recvfrom(bufsize) | 接收 UDP 数据报。返回一个元组 (bytes, address),其中 bytes 是数据,address 是发送方的地址。 |
s.close() | 关闭套接字。with 语句会自动处理。 |
socket.gethostname() | 返回本地机器的标准主机名。 |
**重要提示:**在 Python 3 中,套接字方法 send()、recv()、sendto() 和 recvfrom() 操作的是 bytes 对象,而不是字符串。发送前必须将字符串编码为字节,接收后必须将字节解码为字符串。
一个简单的 TCP Echo 服务器
Section titled “一个简单的 TCP Echo 服务器”此服务器监听特定端口,接受客户端连接,读取来自客户端的数据,然后将相同数据发送回去(回显)。
#!/usr/bin/env python3import 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()一个简单的 TCP Echo 客户端
Section titled “一个简单的 TCP Echo 客户端”此客户端连接到服务器,发送一条消息,然后打印回显响应。
#!/usr/bin/env python3import socket
HOST = '127.0.0.1' # The server's hostname or IP addressPORT = 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)运行服务器和客户端:
Section titled “运行服务器和客户端:”- 打开一个终端并运行服务器:
python server.py - 打开另一个终端并运行客户端:
python client.py
服务器输出:
Server listening on 127.0.0.1:65432Connected by ('127.0.0.1', <client_port>)Received from ('172.17.0.1', <client_port>): Hello, Server!Echoed back to ('172.17.0.1', <client_port>)Client ('172.17.0.1', <client_port>) disconnected.客户端输出:
Connected to server 127.0.0.1:65432Sent: Hello, Server!Received echo: Hello, Server!Python 高级互联网模块
Section titled “Python 高级互联网模块”Python 标准库包含了常见互联网协议的模块:
| 协议 | 主要用途 | 常见端口 | Python 模块 |
|---|---|---|---|
| HTTP/HTTPS | 网页访问 | 80, 443 | urllib.request, http.client (较低级)。requests (第三方,推荐) |
| FTP | 文件传输 | 21 | ftplib |
| SMTP | 发送电子邮件 | 25, 587, 465 | smtplib |
| POP3 | 获取电子邮件 | 110, 995 | poplib |
| IMAP4 | 获取电子邮件 | 143, 993 | imaplib |
| NNTP | Usenet 新闻 | 119, 563 | nntplib |
| Telnet | 远程登录 | 23 | telnetlib |
| XML-RPC | 远程过程调用 | 变化 | xmlrpc.client, xmlrpc.server |
对于 Web 交互 (HTTP/HTTPS),第三方库 requests 被广泛使用,并且通常因其更简单的 API 而比 urllib 更受欢迎。
套接字编程是一个深入的话题。请查阅以下资源:
- Python 官方
socket模块文档:https://docs.python.org/3/library/socket.html - Python 套接字编程 HOWTO:https://docs.python.org/3/howto/sockets.html
- Beej 网络编程指南(通用 C 语言指南,概念适用):https://beej.us/guide/bgnet/