4082 字
20 分钟

Python 网络编程完全指南:Socket / HTTP / aiohttp / WebSocket 从入门到实战

Python 网络编程是构建网络应用、爬虫、API 客户端、实时通信系统的核心技能。从底层 Socket 到高层 HTTP 客户端库,再到异步编程和 WebSocket,掌握网络编程能极大拓展 Python 的应用范围。

Python 网络编程完全指南

本文全面讲解 Python 网络编程核心知识:

  • 网络基础:OSI 模型与 TCP/IP 协议栈
  • Socket 编程:TCP/UDP 服务端与客户端实现
  • HTTP 客户端:urllib / requests / httpx 对比与实战
  • 异步网络:aiohttp + asyncio 高并发请求
  • WebSocket 编程:实时双向通信实现
  • 实战项目:构建高效网络请求工具

一、网络编程基础#

1.1 OSI 七层模型与 TCP/IP 协议栈#

OSI 七层模型 TCP/IP 四层模型 常见协议
─────────────────────────────────────────────────────────
7. 应用层 4. 应用层 HTTP, HTTPS, FTP, SMTP, DNS, WebSocket
6. 表示层 ↑ SSL/TLS, JPEG, ASCII
5. 会话层 ↑ RPC, NetBIOS
4. 传输层 3. 传输层 TCP, UDP, QUIC, SCTP
3. 网络层 2. 网络层 IP, ICMP, ARP
2. 数据链路层 1. 网络接口层 Ethernet, WiFi (802.11), PPP
1. 物理层 ↑ 光纤, 双绞线, 无线信号
# 网络编程中最常接触的是传输层和应用层
# Python socket 模块工作在传输层(TCP/UDP)
# requests/aiohttp 工作在应用层(HTTP/HTTPS)
import socket
# 查看本机协议支持
print(socket.has_ipv6) # 是否支持 IPv6
print(socket.IPPROTO_TCP) # TCP 协议编号
print(socket.IPPROTO_UDP) # UDP 协议编号

1.2 TCP 三次握手与四次挥手#

# TCP 连接建立(三次握手)
"""
客户端 → SYN → 服务端
客户端 ← SYN+ACK ← 服务端
客户端 → ACK → 服务端
# 连接建立,可以开始传输数据
TCP 连接关闭(四次挥手)
客户端 → FIN → 服务端
客户端 ← ACK ← 服务端 (服务端进入半关闭状态)
客户端 ← FIN ← 服务端
客户端 → ACK → 服务端
# 连接完全关闭
"""
# 使用 socket 模块模拟 TCP 连接
import socket
import struct
# TCP 套接字创建
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# AF_INET: IPv4, SOCK_STREAM: TCP
# AF_INET6: IPv6, SOCK_DGRAM: UDP

1.3 常用网络端口速查#

端口号 协议 用途
─────────────────────────────
20 FTP-DATA 文件传输数据
21 FTP 文件传输控制
22 SSH 安全远程登录
23 Telnet 远程登录
25 SMTP 邮件发送
53 DNS 域名解析
80 HTTP 超文本传输
110 POP3 邮件接收
143 IMAP 邮件访问
443 HTTPS 加密 HTTP
3000 常用开发端口
3306 MySQL 数据库
6379 Redis 缓存
8080 HTTP 备用端口
8443 HTTPS 备用端口

二、Socket 编程基础#

2.1 TCP 服务端实现#

import socket
import threading
import time
class TCPServer:
def __init__(self, host='0.0.0.0', port=8888):
self.host = host
self.port = port
self.server_socket = None
self.clients = []
def start(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(5)
print(f"[服务端] 监听 {self.host}:{self.port}")
try:
while True:
client_socket, addr = self.server_socket.accept()
self.clients.append(client_socket)
print(f"[服务端] 新连接: {addr}")
thread = threading.Thread(
target=self.handle_client,
args=(client_socket, addr),
daemon=True
)
thread.start()
except KeyboardInterrupt:
self.stop()
def handle_client(self, client_socket, addr):
try:
while True:
data = client_socket.recv(4096)
if not data:
break
message = data.decode('utf-8')
print(f"[客户端 {addr}] {message}")
response = f"服务器收到: {message}"
client_socket.sendall(response.encode('utf-8'))
except ConnectionResetError:
print(f"[客户端 {addr}] 断开连接")
finally:
client_socket.close()
self.clients.remove(client_socket)
def stop(self):
for client in self.clients:
client.close()
if self.server_socket:
self.server_socket.close()
print("[服务端] 已关闭")
if __name__ == '__main__':
server = TCPServer()
server.start()

2.2 TCP 客户端实现#

import socket
import sys
class TCPClient:
def __init__(self, host='127.0.0.1', port=8888):
self.host = host
self.port = port
self.client_socket = None
def connect(self):
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client_socket.connect((self.host, self.port))
print(f"[客户端] 已连接到 {self.host}:{self.port}")
def send(self, message):
self.client_socket.sendall(message.encode('utf-8'))
response = self.client_socket.recv(4096)
return response.decode('utf-8')
def close(self):
if self.client_socket:
self.client_socket.close()
print("[客户端] 已断开")
if __name__ == '__main__':
client = TCPClient()
client.connect()
try:
while True:
msg = input("输入消息 (q 退出): ")
if msg == 'q':
break
response = client.send(msg)
print(f"服务器回复: {response}")
finally:
client.close()

2.3 UDP 编程实现#

import socket
# UDP 服务端
class UDPServer:
def __init__(self, host='0.0.0.0', port=9999):
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def start(self):
self.sock.bind((self.host, self.port))
print(f"[UDP 服务端] 监听 {self.host}:{self.port}")
try:
while True:
data, addr = self.sock.recvfrom(4096)
message = data.decode('utf-8')
print(f"[来自 {addr}] {message}")
response = f"UDP 收到: {message}"
self.sock.sendto(response.encode('utf-8'), addr)
except KeyboardInterrupt:
self.sock.close()
print("[UDP 服务端] 已关闭")
# UDP 客户端
class UDPClient:
def __init__(self, host='127.0.0.1', port=9999):
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def send(self, message):
self.sock.sendto(message.encode('utf-8'), (self.host, self.port))
data, addr = self.sock.recvfrom(4096)
return data.decode('utf-8')
def close(self):
self.sock.close()
# 使用示例
if __name__ == '__main__':
import threading
import time
server = UDPServer(port=9999)
server_thread = threading.Thread(target=server.start, daemon=True)
server_thread.start()
time.sleep(0.5)
client = UDPClient(port=9999)
response = client.send("Hello UDP!")
print(f"回复: {response}")
client.close()

2.4 Socket 编程注意事项#

# 1. SO_REUSEADDR:端口复用,避免 TIME_WAIT 错误
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. TCP_NODELAY:禁用 Nagle 算法,适用于低延迟场景
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# 3. 超时设置:避免 recv 永久阻塞
sock.settimeout(5.0) # 5 秒超时
# 4. 发送/接收缓冲区调整
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
# 5. 非阻塞模式
sock.setblocking(False)
# 6. 大文件传输的分包策略
def send_large_file(sock, filepath):
import os
file_size = os.path.getsize(filepath)
sock.sendall(str(file_size).encode() + b'\n')
with open(filepath, 'rb') as f:
while True:
chunk = f.read(65536) # 64KB 分片
if not chunk:
break
sock.sendall(chunk)
def recv_all(sock, size):
data = b''
while len(data) < size:
chunk = sock.recv(size - len(data))
if not chunk:
raise ConnectionError("连接中断")
data += chunk
return data

三、HTTP 客户端编程#

3.1 urllib 标准库#

import urllib.request
import urllib.parse
import urllib.error
import json
# 基础 GET 请求
url = "https://api.github.com/repos/python/cpython"
req = urllib.request.Request(
url,
headers={'User-Agent': 'Python-Network-Guide/1.0'}
)
try:
with urllib.request.urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
print(f"状态码: {response.status}")
print(f"项目: {data['full_name']}")
print(f"Stars: {data['stargazers_count']}")
except urllib.error.HTTPError as e:
print(f"HTTP 错误: {e.code} - {e.reason}")
except urllib.error.URLError as e:
print(f"URL 错误: {e.reason}")
# 带参数的 GET 请求
params = urllib.parse.urlencode({
'q': 'python networking',
'sort': 'stars',
'per_page': 5
})
search_url = f"https://api.github.com/search/repositories?{params}"
# POST 请求
data = json.dumps({'name': 'test'}).encode('utf-8')
post_req = urllib.request.Request(
"https://httpbin.org/post",
data=data,
headers={'Content-Type': 'application/json'},
method='POST'
)
with urllib.request.urlopen(post_req) as resp:
result = json.loads(resp.read())
print(f"POST 响应: {result['json']}")

3.2 requests 库(最流行)#

# pip install requests
import requests
# 基础 GET 请求
response = requests.get(
"https://api.github.com/repos/python/cpython",
params={'sort': 'stars'},
headers={'User-Agent': 'MyApp/1.0'},
timeout=10
)
# 自动解析 JSON
data = response.json()
print(f"状态: {response.status_code}")
print(f"编码: {response.encoding}")
print(f"耗时: {response.elapsed.total_seconds():.3f}s")
# 请求参数
print(f"URL: {response.url}")
print(f"Headers: {dict(response.headers)}")
# POST 请求
payload = {'username': 'admin', 'password': 'secret'}
resp = requests.post("https://httpbin.org/post", json=payload)
# Session 复用(连接池,性能优势)
session = requests.Session()
session.headers.update({'User-Agent': 'MyApp/1.0'})
# Cookie 自动管理
session.get("https://httpbin.org/cookies/set", params={'name': 'value'})
resp = session.get("https://httpbin.org/cookies")
print(f"Cookies: {resp.json()}")
# 文件上传
with open("config.json", 'rb') as f:
resp = session.post("https://httpbin.org/post", files={'file': f})
# 文件下载(流式)
with session.get("https://example.com/large-file.zip", stream=True) as resp:
resp.raise_for_status()
with open("downloaded.zip", 'wb') as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
# 错误处理
try:
resp = session.get("https://api.example.com", timeout=5)
resp.raise_for_status() # 4xx/5xx 抛出异常
except requests.exceptions.Timeout:
print("请求超时")
except requests.exceptions.ConnectionError:
print("连接错误")
except requests.exceptions.HTTPError as e:
print(f"HTTP 错误: {e}")

3.3 httpx 库(现代替代品)#

# pip install httpx
import httpx
# httpx 优势:
# 1. 原生支持异步
# 2. 支持 HTTP/2
# 3. API 与 requests 高度兼容
# 4. 自动跟随重定向(可配置)
# 5. 支持 WebSocket
# 同步使用(类似 requests)
with httpx.Client(timeout=10, follow_redirects=True) as client:
response = client.get(
"https://api.github.com/repos/python/cpython",
headers={'User-Agent': 'MyApp/1.0'}
)
print(f"状态: {response.status_code}")
data = response.json()
# 异步使用(高性能)
import asyncio
async def fetch_multiple():
urls = [
"https://api.github.com/repos/python/cpython",
"https://api.github.com/repos/python/python-docs-samples",
"https://api.github.com/repos/python/peps",
]
async with httpx.AsyncClient(timeout=10) as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
for url, resp in zip(urls, responses):
data = resp.json()
print(f"{url.split('/')[-1]}: {data.get('stargazers_count', 'N/A')} stars")
# HTTP/2 支持
with httpx.Client(http2=True) as client:
resp = client.get("https://example.com")
# 重试策略
transport = httpx.HTTPTransport(retries=3)
with httpx.Client(transport=transport) as client:
resp = client.get("https://api.example.com")

3.4 三大库对比#

特性 urllib requests httpx
─────────────────────────────────────────────────────────
异步支持 ❌ ❌ ✅
HTTP/2 ❌ ❌ ✅
WebSocket ❌ ❌ ✅
连接池 基础 ✅ ✅
自动 JSON 解析 需 json.loads ✅ ✅
Session 管理 手动 ✅ ✅
流式上传/下载 基础 ✅ ✅
超时/重试 基础 需配置 原生支持
API 兼容性 独立 API 经典 API 兼容 requests
推荐场景 简单脚本 通用开发 现代/异步项目

四、异步网络编程#

4.1 asyncio 基础#

import asyncio
import time
# 协程基础
async def hello(name, delay=1):
print(f"[{time.strftime('%H:%M:%S')}] 你好, {name}!")
await asyncio.sleep(delay)
print(f"[{time.strftime('%H:%M:%S')}] {name} 再见!")
return f"Hello {name}"
# 单协程运行
async def main_single():
result = await hello("Alice", 1)
print(f"结果: {result}")
# 并发执行多个协程
async def main_parallel():
start = time.time()
# gather 并发执行多个协程
results = await asyncio.gather(
hello("Alice", 2),
hello("Bob", 1),
hello("Charlie", 3),
)
elapsed = time.time() - start
print(f"耗时: {elapsed:.2f}s (串行需 6s)")
print(f"结果: {results}")
# 超时控制
async def with_timeout():
try:
result = await asyncio.wait_for(
hello("Slow", 10),
timeout=3
)
except asyncio.TimeoutError:
print("操作超时!")
# 运行入口
if __name__ == '__main__':
asyncio.run(main_parallel())

4.2 aiohttp 异步 HTTP 客户端#

# pip install aiohttp
import aiohttp
import asyncio
import time
async def fetch_url(session, url):
"""异步获取单个 URL"""
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
return url, response.status, await response.text()
async def batch_fetch(urls):
"""批量异步获取"""
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
for url, status, body in results:
if isinstance(status, Exception):
print(f"{url}: 错误 - {status}")
else:
print(f"{url}: {status} ({len(body)} bytes)")
async def with_semaphore(urls, max_concurrent=5):
"""限制并发数"""
sem = asyncio.Semaphore(max_concurrent)
async def fetch_with_sem(session, url):
async with sem:
return await fetch_url(session, url)
async with aiohttp.ClientSession() as session:
tasks = [fetch_with_sem(session, url) for url in urls]
return await asyncio.gather(*tasks)
# 高并发示例
urls = [
f"https://httpbin.org/delay/{i % 5}" for i in range(20)
]
start = time.time()
asyncio.run(batch_fetch(urls))
print(f"20 个请求并发耗时: {time.time() - start:.2f}s")
# POST 请求
async def post_json(url, data):
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data) as resp:
return await resp.json()
# 文件上传
async def upload_file(url, filepath):
async with aiohttp.ClientSession() as session:
data = aiohttp.FormData()
data.add_field('file',
open(filepath, 'rb'),
filename='data.csv',
content_type='text/csv')
async with session.post(url, data=data) as resp:
return await resp.json()

4.3 异步 vs 同步性能对比#

import requests
import aiohttp
import asyncio
import time
URLS = [f"https://httpbin.org/delay/1" for _ in range(20)]
# 同步版本
def sync_version():
start = time.time()
results = []
for url in URLS:
resp = requests.get(url, timeout=30)
results.append(resp.status_code)
elapsed = time.time() - start
print(f"[同步] 20 请求: {elapsed:.2f}s")
return elapsed
# 异步版本
async def async_version():
start = time.time()
async with aiohttp.ClientSession() as session:
tasks = [session.get(url) for url in URLS]
responses = await asyncio.gather(*tasks)
results = [resp.status for resp in responses]
elapsed = time.time() - start
print(f"[异步] 20 请求: {elapsed:.2f}s")
return elapsed
# 实际测试结果
# [同步] 20 请求: 20.45s (每个请求约 1s,串行)
# [异步] 20 请求: 1.23s (所有请求并发,几乎是 1 个请求的时间)
# 性能提升: 约 16.6 倍

4.4 aiohttp 服务端#

from aiohttp import web
import asyncio
# 简单的异步 Web 服务
async def handle_index(request):
return web.json_response({
'message': 'Hello, aiohttp!',
'timestamp': asyncio.get_event_loop().time()
})
async def handle_echo(request):
name = request.match_info.get('name', 'World')
return web.json_response({'echo': f'Hello, {name}!'})
async def handle_post(request):
data = await request.json()
return web.json_response({
'received': data,
'status': 'ok'
})
# 中间件示例
async def auth_middleware(app, handler):
async def middleware(request):
token = request.headers.get('Authorization')
if not token and request.path.startswith('/api/'):
return web.json_response({'error': 'Unauthorized'}, status=401)
return await handler(request)
return middleware
def create_app():
app = web.Application(middlewares=[auth_middleware])
app.router.add_get('/', handle_index)
app.router.add_get('/echo/{name}', handle_echo)
app.router.add_post('/api/data', handle_post)
return app
if __name__ == '__main__':
app = create_app()
web.run_app(app, host='0.0.0.0', port=8080)

五、WebSocket 实时通信#

5.1 WebSocket 基础#

# WebSocket 协议:
# 1. 基于 HTTP 协议升级(Upgrade)
# 2. 全双工通信(客户端和服务端都可主动发送)
# 3. 持久连接(无需每次建立 HTTP 连接)
# 4. 低延迟、低开销(无 HTTP 头部重复传输)
# 应用场景:
# - 实时聊天应用
# - 股票行情推送
# - 在线协同编辑
# - 游戏实时通信
# - IoT 设备监控
# - 实时通知系统

5.2 websockets 库实现#

# pip install websockets
import asyncio
import websockets
# WebSocket 服务端
async def echo_server(websocket):
async for message in websocket:
print(f"[服务端] 收到: {message}")
response = f"服务器回复: {message}"
await websocket.send(response)
async def start_server():
async with websockets.serve(echo_server, "0.0.0.0", 8765):
print("[WebSocket 服务端] 启动于 ws://localhost:8765")
await asyncio.Future() # 永久运行
# WebSocket 客户端
async def echo_client():
uri = "ws://localhost:8765"
async with websockets.connect(uri) as websocket:
messages = ["你好", "测试消息", "WebSocket 真好用"]
for msg in messages:
await websocket.send(msg)
response = await websocket.recv()
print(f"[客户端] 发送: {msg} → 收到: {response}")
# 同时启动服务端和客户端测试
async def test():
server_task = asyncio.create_task(start_server())
await asyncio.sleep(0.5) # 等待服务启动
client_task = asyncio.create_task(echo_client())
await client_task
# 保持服务运行
await server_task
# asyncio.run(test())

5.3 聊天室实战#

import asyncio
import websockets
import json
from collections import defaultdict
class ChatServer:
def __init__(self):
self.rooms = defaultdict(set) # room_name -> set of websockets
self.user_names = {} # websocket -> username
async def handler(self, websocket):
async for message in websocket:
data = json.loads(message)
action = data.get('action')
if action == 'join':
await self.handle_join(websocket, data)
elif action == 'leave':
await self.handle_leave(websocket, data)
elif action == 'message':
await self.handle_message(websocket, data)
async def handle_join(self, ws, data):
room = data['room']
username = data['username']
self.rooms[room].add(ws)
self.user_names[ws] = username
# 通知房间内其他人
notify = json.dumps({
'type': 'system',
'message': f'{username} 加入房间 {room}'
})
await self.broadcast(room, notify, exclude=ws)
# 确认加入
await ws.send(json.dumps({
'type': 'joined',
'room': room,
'users': [self.user_names[w] for w in self.rooms[room]]
}))
async def handle_leave(self, ws, data):
room = data['room']
username = self.user_names.get(ws, '匿名')
self.rooms[room].discard(ws)
self.user_names.pop(ws, None)
notify = json.dumps({
'type': 'system',
'message': f'{username} 离开房间 {room}'
})
await self.broadcast(room, notify)
async def handle_message(self, ws, data):
room = data['room']
username = self.user_names.get(ws, '匿名')
message = data['message']
broadcast_msg = json.dumps({
'type': 'message',
'username': username,
'message': message,
'room': room
})
await self.broadcast(room, broadcast_msg)
async def broadcast(self, room, message, exclude=None):
for ws in self.rooms.get(room, set()):
if ws != exclude:
await ws.send(message)
# 启动服务器
if __name__ == '__main__':
server = ChatServer()
start = websockets.serve(server.handler, '0.0.0.0', 8765)
asyncio.get_event_loop().run_until_complete(start)
print("[聊天室] 启动于 ws://localhost:8765")
asyncio.get_event_loop().run_forever()
# 客户端 HTML/JS 示例:
# const ws = new WebSocket('ws://localhost:8765');
# ws.send(JSON.stringify({action: 'join', room: 'general', username: 'Alice'}));
# ws.onmessage = (e) => console.log(JSON.parse(e.data));

六、实战:高效网络请求工具#

6.1 批量 URL 健康检查工具#

import asyncio
import time
from typing import List, Dict, Optional
import aiohttp
class URLHealthChecker:
def __init__(
self,
timeout: float = 10.0,
max_concurrent: int = 10,
retry_count: int = 2
):
self.timeout = timeout
self.max_concurrent = max_concurrent
self.retry_count = retry_count
self.semaphore = asyncio.Semaphore(max_concurrent)
async def check_url(
self,
session: aiohttp.ClientSession,
url: str
) -> Dict:
"""检查单个 URL 健康状态"""
async with self.semaphore:
result = {
'url': url,
'status': None,
'response_time': None,
'error': None,
'ok': False
}
for attempt in range(self.retry_count + 1):
try:
start = time.time()
async with session.get(
url,
timeout=aiohttp.ClientTimeout(total=self.timeout),
allow_redirects=True
) as response:
elapsed = time.time() - start
result['status'] = response.status
result['response_time'] = round(elapsed, 3)
result['ok'] = 200 <= response.status < 400
result['final_url'] = str(response.url)
break
except asyncio.TimeoutError:
result['error'] = 'Timeout'
if attempt < self.retry_count:
await asyncio.sleep(1)
except aiohttp.ClientError as e:
result['error'] = str(e)
if attempt < self.retry_count:
await asyncio.sleep(1)
except Exception as e:
result['error'] = str(e)
break
return result
async def check_all(self, urls: List[str]) -> List[Dict]:
"""批量检查 URL"""
connector = aiohttp.TCPConnector(
limit=self.max_concurrent,
ssl=False # 生产环境建议开启
)
async with aiohttp.ClientSession(connector=connector) as session:
tasks = [self.check_url(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
# 过滤异常
clean_results = []
for r, url in zip(results, urls):
if isinstance(r, Exception):
clean_results.append({
'url': url,
'status': None,
'response_time': None,
'error': str(r),
'ok': False
})
else:
clean_results.append(r)
return clean_results
def run(self, urls: List[str]) -> List[Dict]:
"""同步入口"""
return asyncio.run(self.check_all(urls))
# 使用示例
if __name__ == '__main__':
urls = [
"https://www.google.com",
"https://www.github.com",
"https://www.python.org",
"https://www.cloudflare.com",
"https://example.com",
"https://httpbin.org/status/200",
"https://httpbin.org/status/404",
"https://invalid-domain-that-does-not-exist.com",
]
checker = URLHealthChecker(timeout=5, max_concurrent=5)
start = time.time()
results = checker.run(urls)
total_time = time.time() - start
# 输出报告
print(f"\n{'='*60}")
print(f"URL 健康检查报告 - 耗时 {total_time:.2f}s")
print(f"{'='*60}")
ok_count = sum(1 for r in results if r['ok'])
fail_count = len(results) - ok_count
for r in results:
status_icon = "✅" if r['ok'] else "❌"
rt = f"{r['response_time']}s" if r['response_time'] else "N/A"
err = f" ({r['error']})" if r['error'] else ""
print(f"{status_icon} [{r['status'] or 'ERR'}] {rt:>8} {r['url']}{err}")
print(f"\n统计: {ok_count} 通过, {fail_count} 失败, 共 {len(results)} 个 URL")

6.2 使用示例#

输出示例:
============================================================
URL 健康检查报告 - 耗时 3.45s
============================================================
✅ [200] 0.312s https://www.google.com
✅ [200] 0.523s https://www.github.com
✅ [200] 0.891s https://www.python.org
✅ [200] 0.234s https://www.cloudflare.com
✅ [200] 0.015s https://example.com
✅ [200] 0.256s https://httpbin.org/status/200
❌ [404] 0.198s https://httpbin.org/status/404
❌ [ERR] N/A https://invalid-domain-that-does-not-exist.com (Cannot resolve host)
统计: 6 通过, 2 失败, 共 8 个 URL

七、最佳实践与总结#

7.1 库选择决策树#

需要网络编程?
├── 底层自定义协议?
│ └── Python socket 模块
├── HTTP/REST API 调用?
│ ├── 简单脚本 → requests
│ ├── 需要异步/HTTP/2 → httpx
│ └── 高并发爬虫 → aiohttp
├── 实时双向通信?
│ └── websockets
├── Web 服务开发?
│ ├── 简单 API → aiohttp / Flask
│ ├── 高性能 → FastAPI
│ └── 微服务 → gRPC + Protobuf
└── 其他需求?
├── 邮件 → smtplib
├── FTP → ftplib
└── DNS → dnspython

7.2 性能优化要点#

# 1. 连接复用:使用 Session/ClientSession
session = requests.Session() # 复用 TCP 连接
aiohttp_session = aiohttp.ClientSession() # 异步复用
# 2. 连接池配置
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
adapter = HTTPAdapter(
pool_connections=100,
pool_maxsize=100,
max_retries=Retry(total=3, backoff_factor=1)
)
session.mount('http://', adapter)
session.mount('https://', adapter)
# 3. 批量并发:异步 + Semaphore 控制并发数
sem = asyncio.Semaphore(10) # 限制 10 个并发
# 4. 超时设置:永远不要省略
# connect timeout + read timeout
resp = requests.get(url, timeout=(5, 30))
# 5. 流式处理:大文件不要一次性读入内存
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
# 6. 合理的 User-Agent:避免被封禁
headers = {
'User-Agent': 'Mozilla/5.0 (compatible; MyBot/1.0)'
}
# 7. 错误处理:重试 + 降级
try:
resp = session.get(url, timeout=10)
resp.raise_for_status()
except requests.exceptions.RequestException:
# 降级:使用备份 URL 或缓存
pass

7.3 常见问题排查#

# 问题1: 连接超时/SSL 错误
# 解决方案:检查网络、配置代理、验证证书
import requests
# 跳过 SSL 验证(仅开发环境)
resp = requests.get(url, verify=False)
# 问题2: 连接过多导致端口耗尽
# 解决方案:使用 Session 复用、控制连接数
# 错误:每次请求创建新连接
for url in urls:
requests.get(url) # 新连接
# 正确:Session 复用
with requests.Session() as s:
for url in urls:
s.get(url) # 复用连接
# 问题3: aiohttp 与 requests 混用导致事件循环问题
# 解决方案:异步进程完全使用 aiohttp
# 错误:在 async 函数内使用 requests
async def bad():
requests.get(url) # 阻塞事件循环!
# 正确:完全使用异步库
async def good():
async with session.get(url) as resp:
return await resp.json()
# 问题4: WebSocket 频繁断连
# 解决方案:心跳机制 + 自动重连
async def websocket_with_heartbeat(uri):
while True:
try:
async with websockets.connect(
uri,
ping_interval=30,
ping_timeout=10
) as ws:
async for message in ws:
handle_message(message)
except websockets.ConnectionClosed:
print("连接断开,3 秒后重连...")
await asyncio.sleep(3)

7.4 学习资源推荐#

1. 官方文档
- Python socket: https://docs.python.org/3/library/socket.html
- requests: https://requests.readthedocs.io/
- httpx: https://www.python-httpx.org/
- aiohttp: https://docs.aiohttp.org/
- websockets: https://websockets.readthedocs.io/
2. 进阶书籍
- 《Python 网络编程》
- 《O'Reilly Python Cookbook》
3. 实战项目
- 构建个人代理工具
- 开发小型 Web 框架
- 编写网络爬虫/API 客户端
- 实现实时数据推送服务

总结#

Python 网络编程体系庞大,但可以按层次循序渐进地学习:

  1. 基础层:理解 OSI/TCP-IP 模型、TCP/UDP 协议、Socket 编程
  2. 应用层:掌握 requests/httpx 等 HTTP 客户端库
  3. 进阶层:学习 asyncio + aiohttp 异步编程应对高并发场景
  4. 高级层:WebSocket 实时通信、性能优化、错误处理

实践是最好的学习方式。建议从简单的 HTTP 请求开始,逐步深入到异步编程和实时通信,构建属于自己的网络工具集。

Python 网络编程完全指南:Socket / HTTP / aiohttp / WebSocket 从入门到实战
https://971918.xyz/posts/python-guide/python-networking-guide/
作者
九所长
发布于
2026-07-30
许可协议
CC BY-NC-SA 4.0