Python网络编程
1. 网络编程基础
作为Java开发者,您可能已经使用过Java的网络编程API,如Socket和ServerSocket。Python同样提供了强大的网络编程功能,允许您创建客户端和服务器应用程序,处理各种网络协议。
网络模型
在深入Python网络编程之前,让我们回顾一下网络通信的基本模型:
- OSI七层模型:
- 物理层:处理比特流的传输
- 数据链路层:处理帧的传输
- 网络层:处理数据包的路由(IP协议)
- 传输层:处理端到端的连接(TCP/UDP协议)
- 会话层:管理会话
- 表示层:处理数据格式转换
- 应用层:为应用程序提供网络服务(HTTP、FTP等)
graph TD
A[应用层] -->|数据| B[表示层]
B -->|数据| C[会话层]
C -->|数据| D[传输层]
D -->|数据包| E[网络层]
E -->|数据包| F[数据链路层]
F -->|帧| G[物理层]
G -->|比特流| H[物理介质]
H -->|比特流| I[物理层]
I -->|帧| J[数据链路层]
J -->|数据包| K[网络层]
K -->|数据包| L[传输层]
L -->|数据| M[会话层]
M -->|数据| N[表示层]
N -->|数据| O[应用层]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#dfd,stroke:#333,stroke-width:2px
style D fill:#fdd,stroke:#333,stroke-width:2px
style E fill:#ddf,stroke:#333,stroke-width:2px
style F fill:#ffd,stroke:#333,stroke-width:2px
style G fill:#dff,stroke:#333,stroke-width:2px
style H fill:#ccc,stroke:#333,stroke-width:2px
style I fill:#dff,stroke:#333,stroke-width:2px
style J fill:#ffd,stroke:#333,stroke-width:2px
style K fill:#ddf,stroke:#333,stroke-width:2px
style L fill:#fdd,stroke:#333,stroke-width:2px
style M fill:#dfd,stroke:#333,stroke-width:2px
style N fill:#bbf,stroke:#333,stroke-width:2px
style O fill:#f9f,stroke:#333,stroke-width:2px
- TCP/IP四层模型:
- 网络接口层:对应OSI的物理层和数据链路层
- 网络层:对应OSI的网络层(IP协议)
- 传输层:对应OSI的传输层(TCP/UDP协议)
- 应用层:对应OSI的会话层、表示层和应用层
常见网络协议
-
TCP (传输控制协议):
- 面向连接的协议
- 提供可靠的数据传输
- 具有流量控制和拥塞控制
- 适用于需要可靠传输的应用(如HTTP、FTP、SMTP)
-
UDP (用户数据报协议):
- 无连接的协议
- 不保证数据传输的可靠性
- 低开销,高效率
- 适用于实时应用(如视频流、游戏、DNS)
-
HTTP (超文本传输协议):
- 应用层协议,基于TCP
- 用于Web浏览器和服务器之间的通信
- 无状态协议
- HTTP/1.1、HTTP/2、HTTP/3版本
-
WebSocket:
- 提供全双工通信通道
- 基于TCP的应用层协议
- 用于Web应用中的实时通信
-
SMTP (简单邮件传输协议):
- 用于发送电子邮件
- 基于TCP的应用层协议
-
FTP (文件传输协议):
- 用于在客户端和服务器之间传输文件
- 使用两个TCP连接:控制连接和数据连接
2. Python Socket编程
Socket是网络编程的基础,它提供了一种在网络上发送和接收数据的方法。Python的socket
模块提供了对底层Socket API的访问。
创建Socket
import socket
# 创建TCP Socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建UDP Socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
参数说明:
socket.AF_INET
:使用IPv4地址族socket.AF_INET6
:使用IPv6地址族socket.SOCK_STREAM
:使用TCP协议socket.SOCK_DGRAM
:使用UDP协议
TCP客户端
import socket
def tcp_client():
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# 连接服务器
server_address = ('localhost', 8000)
print(f"连接到 {server_address[0]}:{server_address[1]}")
client_socket.connect(server_address)
# 发送数据
message = "Hello, Server!"
print(f"发送: {message}")
client_socket.sendall(message.encode('utf-8'))
# 接收响应
data = client_socket.recv(1024)
print(f"接收: {data.decode('utf-8')}")
finally:
# 关闭连接
print("关闭连接")
client_socket.close()
if __name__ == "__main__":
tcp_client()
TCP服务器
import socket
def tcp_server():
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_address = ('localhost', 8000)
print(f"启动服务器 {server_address[0]}:{server_address[1]}")
server_socket.bind(server_address)
# 监听连接
server_socket.listen(5) # 最多5个排队连接
try:
while True:
# 等待连接
print("等待连接...")
client_socket, client_address = server_socket.accept()
print(f"接受来自 {client_address[0]}:{client_address[1]} 的连接")
try:
# 接收数据
data = client_socket.recv(1024)
print(f"接收: {data.decode('utf-8')}")
if data:
# 发送响应
response = "Hello, Client!"
print(f"发送: {response}")
client_socket.sendall(response.encode('utf-8'))
else:
print("没有数据")
break
finally:
# 关闭客户端连接
print("关闭客户端连接")
client_socket.close()
finally:
# 关闭服务器Socket
print("关闭服务器")
server_socket.close()
if __name__ == "__main__":
tcp_server()
UDP客户端
import socket
def udp_client():
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# 服务器地址
server_address = ('localhost', 8000)
# 发送数据
message = "Hello, UDP Server!"
print(f"发送: {message}")
client_socket.sendto(message.encode('utf-8'), server_address)
# 接收响应
data, server = client_socket.recvfrom(1024)
print(f"从 {server[0]}:{server[1]} 接收: {data.decode('utf-8')}")
finally:
# 关闭Socket
print("关闭Socket")
client_socket.close()
if __name__ == "__main__":
udp_client()
UDP服务器
import socket
def udp_server():
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口
server_address = ('localhost', 8000)
print(f"启动UDP服务器 {server_address[0]}:{server_address[1]}")
server_socket.bind(server_address)
try:
while True:
# 接收数据
print("等待消息...")
data, client_address = server_socket.recvfrom(1024)
print(f"从 {client_address[0]}:{client_address[1]} 接收: {data.decode('utf-8')}")
# 发送响应
response = "Hello, UDP Client!"
print(f"发送: {response}")
server_socket.sendto(response.encode('utf-8'), client_address)
finally:
# 关闭服务器Socket
print("关闭服务器")
server_socket.close()
if __name__ == "__main__":
udp_server()
Socket选项
可以使用setsockopt
方法设置Socket选项:
import socket
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置地址重用选项
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 设置超时
client_socket.settimeout(5.0) # 5秒超时
非阻塞Socket
默认情况下,Socket操作是阻塞的。可以设置为非阻塞模式:
import socket
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置为非阻塞模式
client_socket.setblocking(False)
try:
client_socket.connect(('localhost', 8000))
except BlockingIOError:
# 连接操作正在进行中
pass
# 使用select模块处理非阻塞I/O
import select
readable, writable, exceptional = select.select([client_socket], [client_socket], [client_socket], 5.0)
if writable:
print("连接就绪")
client_socket.send(b"Hello")
3. 高级Socket编程
使用socketserver模块
Python的socketserver
模块提供了更高级的服务器实现,简化了服务器编程:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
处理TCP请求的类
"""
def handle(self):
# self.request是TCP套接字
data = self.request.recv(1024).strip()
print(f"{self.client_address[0]} 发送: {data.decode('utf-8')}")
# 发送响应
response = "已收到您的消息"
self.request.sendall(response.encode('utf-8'))
if __name__ == "__main__":
HOST, PORT = "localhost", 8000
# 创建服务器
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
# 启动服务器
print(f"服务器启动在 {HOST}:{PORT}")
server.serve_forever()
多线程服务器
使用ThreadingMixIn
创建多线程服务器:
import socketserver
class ThreadedTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024).strip()
print(f"线程 {threading.current_thread().name} 处理来自 {self.client_address[0]} 的请求")
print(f"接收: {data.decode('utf-8')}")
response = f"线程 {threading.current_thread().name} 已处理您的请求"
self.request.sendall(response.encode('utf-8'))
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
import threading
HOST, PORT = "localhost", 8000
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPHandler)
# 启动线程处理请求
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
print(f"服务器启动在 {HOST}:{PORT}")
print(f"服务器线程名: {server_thread.name}")
# 保持主线程运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
server.shutdown()
异步I/O
使用asyncio
模块实现异步网络编程:
import asyncio
async def handle_client(reader, writer):
# 获取客户端地址
addr = writer.get_extra_info('peername')
print(f"连接来自 {addr}")
# 读取数据
data = await reader.read(100)
message = data.decode('utf-8')
print(f"接收: {message}")
# 发送响应
response = f"已收到: {message}"
print(f"发送: {response}")
writer.write(response.encode('utf-8'))
await writer.drain()
# 关闭连接
print("关闭连接")
writer.close()
await writer.wait_closed()
async def main():
# 启动服务器
server = await asyncio.start_server(
handle_client, '127.0.0.1', 8000)
addr = server.sockets[0].getsockname()
print(f'服务器启动在 {addr}')
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main())
4. HTTP客户端编程
Python提供了多种方式来发送HTTP请求。
使用urllib
urllib
是Python标准库中的HTTP客户端:
from urllib import request, parse
import json
def fetch_data():
# GET请求
with request.urlopen('https://api.example.com/data') as response:
data = response.read().decode('utf-8')
return json.loads(data)
def post_data():
# POST请求数据
data = {
'name': 'John Doe',
'email': 'john@example.com'
}
data = parse.urlencode(data).encode('utf-8')
# 创建请求
req = request.Request('https://api.example.com/submit', data=data, method='POST')
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
# 发送请求
with request.urlopen(req) as response:
result = response.read().decode('utf-8')
return json.loads(result)
使用requests库
requests
是一个更简单、更强大的HTTP客户端库:
import requests
def fetch_data():
# GET请求
response = requests.get('https://api.example.com/data')
response.raise_for_status() # 如果请求失败则抛出异常
return response.json()
def post_data():
# POST请求
data = {
'name': 'John Doe',
'email': 'john@example.com'
}
response = requests.post('https://api.example.com/submit', data=data)
response.raise_for_status()
return response.json()
def upload_file():
# 上传文件
files = {'file': open('document.pdf', 'rb')}
response = requests.post('https://api.example.com/upload', files=files)
response.raise_for_status()
return response.json()
def custom_headers():
# 自定义请求头
headers = {
'User-Agent': 'My Python Client',
'Authorization': 'Bearer token123'
}
response = requests.get('https://api.example.com/protected', headers=headers)
response.raise_for_status()
return response.json()
def with_cookies():
# 使用会话保持Cookie
session = requests.Session()
# 登录
login_data = {'username': 'user', 'password': 'pass'}
session.post('https://example.com/login', data=login_data)
# 访问需要认证的页面
response = session.get('https://example.com/dashboard')
return response.text
def with_timeout():
# 设置超时
try:
response = requests.get('https://api.example.com/data', timeout=5)
response.raise_for_status()
return response.json()
except requests.Timeout:
print("请求超时")
return None
异步HTTP请求
使用aiohttp
库进行异步HTTP请求:
import aiohttp
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def fetch_multiple(urls):
tasks = [fetch(url) for url in urls]
return await asyncio.gather(*tasks)
async def main():
urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3'
]
results = await fetch_multiple(urls)
for url, result in zip(urls, results):
print(f"URL: {url}, 长度: {len(result)}")
if __name__ == "__main__":
asyncio.run(main())
5. HTTP服务器编程
使用http.server模块
Python的http.server
模块提供了简单的HTTP服务器实现:
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def _set_headers(self, content_type='text/html'):
self.send_response(200)
self.send_header('Content-type', content_type)
self.end_headers()
def do_GET(self):
if self.path == '/':
self._set_headers()
self.wfile.write(b"<html><body><h1>Welcome to my server!</h1></body></html>")
elif self.path == '/api/data':
self._set_headers('application/json')
data = {'message': 'Hello, World!', 'status': 'success'}
self.wfile.write(json.dumps(data).encode('utf-8'))
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 Not Found")
def do_POST(self):
if self.path == '/api/submit':
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
# 解析JSON数据
try:
data = json.loads(post_data.decode('utf-8'))
# 处理数据
response = {'status': 'success', 'received': data}
# 发送响应
self._set_headers('application/json')
self.wfile.write(json.dumps(response).encode('utf-8'))
except json.JSONDecodeError:
self.send_response(400)
self.end_headers()
self.wfile.write(b"Invalid JSON")
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 Not Found")
def run_server(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler, port=8000):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print(f"Starting server on port {port}...")
httpd.serve_forever()
if __name__ == "__main__":
run_server()
使用Flask框架
Flask是一个轻量级的Web框架,适合构建API和Web应用:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return "<h1>Welcome to my API</h1>"
@app.route('/api/data')
def get_data():
data = {'message': 'Hello, World!', 'status': 'success'}
return jsonify(data)
@app.route('/api/submit', methods=['POST'])
def submit_data():
if request.is_json:
data = request.get_json()
# 处理数据
response = {'status': 'success', 'received': data}
return jsonify(response)
else:
return jsonify({'error': 'Invalid JSON'}), 400
if __name__ == '__main__':
app.run(debug=True, port=8000)
使用FastAPI框架
FastAPI是一个现代、高性能的Web框架,支持异步请求处理和自动API文档生成:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.post("/items/")
def create_item(item: Item):
return item
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
if item_id < 0:
raise HTTPException(status_code=400, detail="Item ID must be positive")
return {"item_id": item_id, **item.dict()}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
6. WebSocket编程
WebSocket提供了全双工通信通道,适用于需要实时通信的Web应用。
使用websockets库
import asyncio
import websockets
# WebSocket服务器
async def echo(websocket, path):
async for message in websocket:
print(f"接收: {message}")
await websocket.send(f"Echo: {message}")
async def main():
async with websockets.serve(echo, "localhost", 8765):
await asyncio.Future() # 运行直到被取消
if __name__ == "__main__":
asyncio.run(main())
WebSocket客户端
import asyncio
import websockets
async def hello():
uri = "ws://localhost:8765"
async with websockets.connect(uri) as websocket:
message = "Hello WebSocket!"
await websocket.send(message)
print(f"发送: {message}")
response = await websocket.recv()
print(f"接收: {response}")
if __name__ == "__main__":
asyncio.run(hello())
使用Flask-SocketIO
Flask-SocketIO是Flask的WebSocket扩展:
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
@app.route('/')
def index():
return render_template('index.html')
@socketio.on('connect')
def test_connect():
emit('response', {'data': 'Connected'})
@socketio.on('message')
def handle_message(data):
print('received message: ' + data)
emit('response', {'data': f'Server received: {data}'})
@socketio.on('disconnect')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app, debug=True)
HTML客户端:
<!DOCTYPE html>
<html>
<head>
<title>Flask-SocketIO Test</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const socket = io();
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('response', (data) => {
console.log('Server response:', data);
document.getElementById('messages').innerHTML += '<p>' + data.data + '</p>';
});
document.getElementById('send').addEventListener('click', () => {
const message = document.getElementById('message').value;
socket.emit('message', message);
document.getElementById('message').value = '';
});
});
</script>
</head>
<body>
<h1>Flask-SocketIO Test</h1>
<div id="messages"></div>
<input type="text" id="message" placeholder="Enter message">
<button id="send">Send</button>
</body>
</html>
7. 电子邮件编程
Python提供了发送和接收电子邮件的功能。
使用smtplib发送邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_email():
# 邮件服务器配置
smtp_server = "smtp.gmail.com"
port = 587
sender_email = "your_email@gmail.com"
password = "your_password"
receiver_email = "recipient@example.com"
# 创建多部分消息
message = MIMEMultipart()
message["Subject"] = "Python邮件测试"
message["From"] = sender_email
message["To"] = receiver_email
# 添加正文
body = "这是一封由Python发送的测试邮件。"
message.attach(MIMEText(body, "plain"))
try:
# 创建SMTP会话
server = smtplib.SMTP(smtp_server, port)
server.ehlo() # 向SMTP服务器标识自己
server.starttls() # 启用TLS加密
server.ehlo() # 重新标识
server.login(sender_email, password)
# 发送邮件
server.sendmail(sender_email, receiver_email, message.as_string())
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
if __name__ == "__main__":
send_email()
发送HTML邮件和附件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import os
def send_email_with_attachment():
# 邮件服务器配置
smtp_server = "smtp.gmail.com"
port = 587
sender_email = "your_email@gmail.com"
password = "your_password"
receiver_email = "recipient@example.com"
# 创建多部分消息
message = MIMEMultipart()
message["Subject"] = "Python邮件测试 - 带附件"
message["From"] = sender_email
message["To"] = receiver_email
# 添加HTML正文
html = """
<html>
<body>
<h1>Python邮件测试</h1>
<p>这是一封由<b>Python</b>发送的测试邮件,包含<i>HTML</i>格式和附件。</p>
</body>
</html>
"""
message.attach(MIMEText(html, "html"))
# 添加附件
filename = "document.pdf" # 替换为实际文件路径
with open(filename, "rb") as f:
attachment = MIMEApplication(f.read(), _subtype="pdf")
attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(filename))
message.attach(attachment)
try:
# 创建SMTP会话
server = smtplib.SMTP(smtp_server, port)
server.ehlo()
server.starttls()
server.ehlo()
server.login(sender_email, password)
# 发送邮件
server.sendmail(sender_email, receiver_email, message.as_string())
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
使用imaplib接收邮件
import imaplib
import email
from email.header import decode_header
def fetch_emails():
# 邮件服务器配置
imap_server = "imap.gmail.com"
email_address = "your_email@gmail.com"
password = "your_password"
try:
# 连接到IMAP服务器
mail = imaplib.IMAP4_SSL(imap_server)
mail.login(email_address, password)
# 选择收件箱
mail.select("INBOX")
# 搜索邮件
status, messages = mail.search(None, "UNSEEN") # 未读邮件
if status == "OK":
# 获取邮件ID列表
email_ids = messages[0].split()
for email_id in email_ids:
# 获取邮件内容
status, msg_data = mail.fetch(email_id, "(RFC822)")
if status == "OK":
# 解析邮件
msg = email.message_from_bytes(msg_data[0][1])
# 解码主题
subject, encoding = decode_header(msg["Subject"])[0]
if isinstance(subject, bytes):
subject = subject.decode(encoding or "utf-8")
# 获取发件人
from_addr = msg.get("From")
print(f"From: {from_addr}")
print(f"Subject: {subject}")
# 获取邮件内容
if msg.is_multipart():
# 处理多部分邮件
for part in msg.walk():
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
# 跳过附件
if "attachment" in content_disposition:
filename = part.get_filename()
if filename:
print(f"附件: {filename}")
# 获取文本内容
if content_type == "text/plain" and "attachment" not in content_disposition:
body = part.get_payload(decode=True).decode()
print(f"Body: {body}")
else:
# 处理纯文本邮件
body = msg.get_payload(decode=True).decode()
print(f"Body: {body}")
# 标记为已读
mail.store(email_id, "+FLAGS", "\\Seen")
# 关闭连接
mail.close()
mail.logout()
except Exception as e:
print(f"接收邮件失败: {e}")
if __name__ == "__main__":
fetch_emails()
8. FTP编程
Python的ftplib
模块提供了FTP客户端功能。
FTP客户端
from ftplib import FTP
def ftp_operations():
# 连接到FTP服务器
ftp = FTP('ftp.example.com')
ftp.login(user='username', passwd='password')
# 显示当前目录
print(f"当前目录: {ftp.pwd()}")
# 列出目录内容
files = ftp.nlst()
print("目录内容:")
for file in files:
print(f" {file}")
# 切换目录
ftp.cwd('/public')
print(f"切换到目录: {ftp.pwd()}")
# 下载文件
with open('downloaded_file.txt', 'wb') as local_file:
ftp.retrbinary('RETR example.txt', local_file.write)
# 上传文件
with open('local_file.txt', 'rb') as local_file:
ftp.storbinary('STOR uploaded_file.txt', local_file)
# 关闭连接
ftp.quit()
if __name__ == "__main__":
ftp_operations()
使用ftplib创建FTP服务器
Python标准库没有直接提供FTP服务器实现,但可以使用第三方库如pyftpdlib
:
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
def run_ftp_server():
# 创建用户认证器
authorizer = DummyAuthorizer()
# 添加用户,设置权限
# 参数: 用户名, 密码, 主目录, 权限
authorizer.add_user("user", "password", "./ftp_home", perm="elradfmwMT")
# 添加匿名用户
authorizer.add_anonymous("./ftp_home", perm="elr")
# 创建处理器
handler = FTPHandler
handler.authorizer = authorizer
# 设置欢迎消息
handler.banner = "欢迎使用Python FTP服务器"
# 创建服务器
address = ('', 2121) # 空字符串表示所有可用地址
server = FTPServer(address, handler)
# 设置最大连接数
server.max_cons = 256
server.max_cons_per_ip = 5
# 启动服务器
print(f"FTP服务器启动在端口 {address[1]}")
server.serve_forever()
if __name__ == "__main__":
run_ftp_server()
9. 网络安全编程
SSL/TLS加密通信
使用ssl
模块实现加密通信:
import socket
import ssl
def ssl_client():
# 创建普通Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 包装为SSL Socket
context = ssl.create_default_context()
ssl_socket = context.wrap_socket(client_socket, server_hostname='example.com')
try:
# 连接服务器
ssl_socket.connect(('example.com', 443))
# 获取证书信息
cert = ssl_socket.getpeercert()
print(f"服务器证书: {cert}")
# 发送HTTP请求
request = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
ssl_socket.send(request.encode('utf-8'))
# 接收响应
response = ssl_socket.recv(4096)
print(response.decode('utf-8'))
finally:
# 关闭连接
ssl_socket.close()
def ssl_server():
# 创建SSL上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
# 加载证书和私钥
context.load_cert_chain(certfile='server.crt', keyfile='server.key')
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8443))
server_socket.listen(5)
# 包装为SSL Socket
ssl_socket = context.wrap_socket(server_socket, server_side=True)
try:
print("等待连接...")
client_socket, addr = ssl_socket.accept()
print(f"连接来自: {addr}")
# 接收数据
data = client_socket.recv(1024)
print(f"接收: {data.decode('utf-8')}")
# 发送响应
response = "Hello, secure world!"
client_socket.send(response.encode('utf-8'))
# 关闭客户端连接
client_socket.close()
finally:
# 关闭服务器
ssl_socket.close()
使用cryptography库进行加密
from cryptography.fernet import Fernet
def encrypt_decrypt_data():
# 生成密钥
key = Fernet.generate_key()
cipher = Fernet(key)
# 加密数据
message = "敏感信息"
encrypted = cipher.encrypt(message.encode('utf-8'))
print(f"加密后: {encrypted}")
# 解密数据
decrypted = cipher.decrypt(encrypted).decode('utf-8')
print(f"解密后: {decrypted}")
def secure_network_communication():
# 在实际应用中,发送方和接收方需要安全地共享密钥
key = Fernet.generate_key()
cipher = Fernet(key)
# 发送方加密数据
message = "这是一条安全消息"
encrypted = cipher.encrypt(message.encode('utf-8'))
# 通过网络发送加密数据
# network.send(encrypted)
# 接收方解密数据
# received_data = network.receive()
received_data = encrypted # 模拟接收
decrypted = cipher.decrypt(received_data).decode('utf-8')
print(f"接收到的消息: {decrypted}")
哈希和数字签名
import hashlib
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
def calculate_hash():
# 使用hashlib计算哈希
data = "重要数据".encode('utf-8')
# MD5 (不推荐用于安全目的)
md5_hash = hashlib.md5(data).hexdigest()
print(f"MD5: {md5_hash}")
# SHA-256
sha256_hash = hashlib.sha256(data).hexdigest()
print(f"SHA-256: {sha256_hash}")
# SHA-512
sha512_hash = hashlib.sha512(data).hexdigest()
print(f"SHA-512: {sha512_hash}")
def digital_signature():
# 生成RSA密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
public_key = private_key.public_key()
# 要签名的消息
message = b"Sign this message"
# 创建签名
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print(f"签名: {signature.hex()}")
# 验证签名
try:
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("签名验证成功")
except Exception as e:
print(f"签名验证失败: {e}")
10. 网络编程最佳实践
错误处理
在网络编程中,错误处理至关重要:
import socket
import time
def robust_client():
max_retries = 3
retry_delay = 2
retries = 0
while retries < max_retries:
try:
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.settimeout(5) # 设置超时
# 连接服务器
client_socket.connect(('example.com', 80))
# 发送数据
client_socket.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
# 接收响应
response = b""
while True:
try:
data = client_socket.recv(4096)
if not data:
break
response += data
except socket.timeout:
print("接收超时")
break
print(f"接收到 {len(response)} 字节")
return response
except socket.timeout:
print(f"连接超时,重试 {retries + 1}/{max_retries}")
except ConnectionRefusedError:
print(f"连接被拒绝,重试 {retries + 1}/{max_retries}")
except socket.error as e:
print(f"Socket错误: {e}, 重试 {retries + 1}/{max_retries}")
finally:
client_socket.close()
retries += 1
time.sleep(retry_delay)
print("达到最大重试次数,放弃")
return None
资源管理
使用上下文管理器确保资源正确关闭:
import socket
from contextlib import contextmanager
@contextmanager
def tcp_connection(host, port):
"""Socket连接的上下文管理器"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
yield sock
finally:
sock.close()
# 使用上下文管理器
def safe_client():
try:
with tcp_connection('example.com', 80) as sock:
sock.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
response = sock.recv(4096)
print(response.decode('utf-8'))
except Exception as e:
print(f"错误: {e}")
并发处理
使用多线程或异步I/O处理多个连接:
import threading
import socket
def handle_client(client_socket, address):
"""处理客户端连接的线程函数"""
try:
print(f"处理来自 {address} 的连接")
# 接收数据
data = client_socket.recv(1024)
print(f"接收: {data.decode('utf-8')}")
# 发送响应
response = "Hello from server!"
client_socket.sendall(response.encode('utf-8'))
except Exception as e:
print(f"处理客户端时出错: {e}")
finally:
client_socket.close()
def threaded_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address = ('localhost', 8000)
server_socket.bind(server_address)
server_socket.listen(5)
print(f"服务器启动在 {server_address}")
try:
while True:
client_socket, client_address = server_socket.accept()
# 为每个客户端创建新线程
client_thread = threading.Thread(
target=handle_client,
args=(client_socket, client_address)
)
client_thread.daemon = True
client_thread.start()
except KeyboardInterrupt:
print("服务器关闭")
finally:
server_socket.close()
性能优化
提高网络应用性能的技巧:
- 使用连接池:重用连接而不是每次创建新连接
- 批量处理:合并多个小请求为一个大请求
- 压缩数据:减少传输的数据量
- 异步I/O:避免阻塞等待I/O操作
- 负载均衡:分散请求到多个服务器
# 使用连接池示例
import urllib3
def connection_pool_example():
# 创建连接池
http = urllib3.PoolManager(maxsize=10)
# 发送多个请求
urls = [
"http://example.com",
"http://example.org",
"http://example.net"
]
for url in urls:
response = http.request('GET', url)
print(f"URL: {url}, 状态: {response.status}")
安全最佳实践
- 始终使用TLS/SSL:加密敏感数据传输
- 验证证书:防止中间人攻击
- 输入验证:防止注入攻击
- 使用最新的加密算法:避免使用已知有漏洞的算法
- 定期更新依赖库:修复安全漏洞
import requests
def secure_request():
# 验证证书
response = requests.get('https://example.com', verify=True)
# 设置超时
response = requests.get('https://example.com', timeout=5)
# 使用会话保持连接
session = requests.Session()
session.verify = True # 验证证书
response = session.get('https://example.com')
11. 实际应用案例
简单的聊天服务器和客户端
服务器端:
import socket
import threading
import time
class ChatServer:
def __init__(self, host='localhost', port=9090):
self.host = host
self.port = port
self.server_socket = None
self.clients = []
self.nicknames = []
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, address = self.server_socket.accept()
print(f"连接来自 {address}")
# 请求昵称
client_socket.send("NICK".encode('utf-8'))
nickname = client_socket.recv(1024).decode('utf-8')
self.nicknames.append(nickname)
self.clients.append(client_socket)
# 广播新用户加入
self.broadcast(f"{nickname} 加入了聊天室!".encode('utf-8'))
client_socket.send("已连接到服务器!".encode('utf-8'))
# 为客户端创建处理线程
thread = threading.Thread(target=self.handle_client, args=(client_socket, nickname))
thread.daemon = True
thread.start()
except KeyboardInterrupt:
self.broadcast("服务器关闭".encode('utf-8'))
self.stop()
def handle_client(self, client_socket, nickname):
try:
while True:
message = client_socket.recv(1024)
if message:
self.broadcast(f"{nickname}: {message.decode('utf-8')}".encode('utf-8'))
else:
# 客户端断开连接
index = self.clients.index(client_socket)
self.clients.remove(client_socket)
client_socket.close()
nickname = self.nicknames[index]
self.nicknames.remove(nickname)
self.broadcast(f"{nickname} 离开了聊天室!".encode('utf-8'))
break
except Exception as e:
print(f"处理客户端时出错: {e}")
if client_socket in self.clients:
index = self.clients.index(client_socket)
self.clients.remove(client_socket)
client_socket.close()
nickname = self.nicknames[index]
self.nicknames.remove(nickname)
self.broadcast(f"{nickname} 离开了聊天室!".encode('utf-8'))
def broadcast(self, message):
for client in self.clients:
try:
client.send(message)
except:
# 如果发送失败,移除客户端
index = self.clients.index(client)
self.clients.remove(client)
client.close()
nickname = self.nicknames[index]
self.nicknames.remove(nickname)
self.broadcast(f"{nickname} 离开了聊天室!".encode('utf-8'))
def stop(self):
if self.server_socket:
for client in self.clients:
client.close()
self.server_socket.close()
print("服务器已关闭")
if __name__ == "__main__":
server = ChatServer()
server.start()
客户端:
import socket
import threading
import tkinter as tk
from tkinter import scrolledtext, Entry, Button, END
class ChatClient:
def __init__(self, host='localhost', port=9090):
self.host = host
self.port = port
self.client_socket = None
self.nickname = None
# 创建GUI
self.root = tk.Tk()
self.root.title("Python聊天客户端")
self.root.geometry("600x400")
# 聊天显示区域
self.chat_area = scrolledtext.ScrolledText(self.root, state='disabled')
self.chat_area.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
# 输入框和发送按钮
input_frame = tk.Frame(self.root)
input_frame.pack(padx=10, pady=5, fill=tk.X)
self.message_input = Entry(input_frame)
self.message_input.pack(side=tk.LEFT, fill=tk.X, expand=True)
self.message_input.bind("<Return>", self.send_message)
send_button = Button(input_frame, text="发送", command=self.send_message)
send_button.pack(side=tk.RIGHT, padx=5)
# 设置关闭事件
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
def connect(self, nickname):
self.nickname = nickname
try:
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client_socket.connect((self.host, self.port))
# 启动接收线程
receive_thread = threading.Thread(target=self.receive_messages)
receive_thread.daemon = True
receive_thread.start()
return True
except Exception as e:
self.update_chat(f"连接错误: {e}")
return False
def receive_messages(self):
while True:
try:
message = self.client_socket.recv(1024).decode('utf-8')
if message == "NICK":
self.client_socket.send(self.nickname.encode('utf-8'))
else:
self.update_chat(message)
except Exception as e:
self.update_chat(f"错误: {e}")
self.client_socket.close()
break
def send_message(self, event=None):
message = self.message_input.get()
if message:
try:
self.client_socket.send(message.encode('utf-8'))
self.message_input.delete(0, END)
except Exception as e:
self.update_chat(f"发送错误: {e}")
def update_chat(self, message):
self.chat_area.config(state='normal')
self.chat_area.insert(END, message + "\n")
self.chat_area.config(state='disabled')
self.chat_area.see(END)
def on_closing(self):
if self.client_socket:
try:
self.client_socket.close()
except:
pass
self.root.destroy()
def run(self):
# 先获取昵称
nickname_window = tk.Toplevel(self.root)
nickname_window.title("输入昵称")
nickname_window.geometry("300x100")
nickname_window.transient(self.root)
nickname_window.grab_set()
tk.Label(nickname_window, text="请输入您的昵称:").pack(pady=5)
nickname_entry = Entry(nickname_window)
nickname_entry.pack(pady=5, padx=10, fill=tk.X)
nickname_entry.focus()
def submit_nickname():
nickname = nickname_entry.get()
if nickname:
if self.connect(nickname):
nickname_window.destroy()
self.root.deiconify() # 显示主窗口
else:
tk.Label(nickname_window, text="连接失败,请重试", fg="red").pack()
Button(nickname_window, text="连接", command=submit_nickname).pack(pady=5)
nickname_entry.bind("<Return>", lambda e: submit_nickname())
self.root.withdraw() # 隐藏主窗口
self.root.mainloop()
if __name__ == "__main__":
client = ChatClient()
client.run()
简单的Web爬虫
import requests
from bs4 import BeautifulSoup
import time
import random
import csv
class WebCrawler:
def __init__(self, base_url):
self.base_url = base_url
self.visited_urls = set()
self.data = []
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
def crawl(self, url, max_pages=10):
"""爬取指定URL及其链接的页面"""
page_count = 0
urls_to_visit = [url]
while urls_to_visit and page_count < max_pages:
current_url = urls_to_visit.pop(0)
if current_url in self.visited_urls:
continue
print(f"爬取: {current_url}")
try:
response = requests.get(current_url, headers=self.headers)
response.raise_for_status()
self.visited_urls.add(current_url)
page_count += 1
# 解析页面
soup = BeautifulSoup(response.text, 'html.parser')
# 提取数据
self.extract_data(soup, current_url)
# 提取链接
links = soup.find_all('a', href=True)
for link in links:
href = link['href']
# 处理相对URL
if href.startswith('/'):
href = self.base_url + href
# 只处理同一域名下的URL
if href.startswith(self.base_url) and href not in self.visited_urls:
urls_to_visit.append(href)
# 礼貌爬取,避免请求过于频繁
time.sleep(random.uniform(1, 3))
except Exception as e:
print(f"爬取 {current_url} 时出错: {e}")
print(f"爬取完成,共访问 {len(self.visited_urls)} 个页面")
def extract_data(self, soup, url):
"""从页面提取数据,需要根据具体网站定制"""
# 示例:提取所有标题和段落
title = soup.title.string if soup.title else "无标题"
paragraphs = []
for p in soup.find_all('p'):
if p.text.strip():
paragraphs.append(p.text.strip())
self.data.append({
'url': url,
'title': title,
'paragraphs': paragraphs,
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
})
def save_to_csv(self, filename):
"""将爬取的数据保存到CSV文件"""
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['url', 'title', 'timestamp']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for item in self.data:
# 只保存URL、标题和时间戳
writer.writerow({
'url': item['url'],
'title': item['title'],
'timestamp': item['timestamp']
})
print(f"数据已保存到 {filename}")
if __name__ == "__main__":
crawler = WebCrawler('https://example.com')
crawler.crawl('https://example.com', max_pages=5)
crawler.save_to_csv('crawled_data.csv')
实时股票价格监控器
import requests
import time
import json
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
class StockMonitor:
def __init__(self, symbols):
self.symbols = symbols
self.data = {symbol: {'prices': [], 'times': []} for symbol in symbols}
self.api_key = "YOUR_API_KEY" # 替换为实际的API密钥
def fetch_stock_data(self):
"""获取股票数据"""
for symbol in self.symbols:
try:
# 使用Alpha Vantage API获取股票数据
url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={self.api_key}"
response = requests.get(url)
response.raise_for_status()
data = response.json()
if "Global Quote" in data:
quote = data["Global Quote"]
price = float(quote.get("05. price", 0))
current_time = time.strftime('%H:%M:%S')
self.data[symbol]['prices'].append(price)
self.data[symbol]['times'].append(current_time)
# 只保留最近30个数据点
if len(self.data[symbol]['prices']) > 30:
self.data[symbol]['prices'] = self.data[symbol]['prices'][-30:]
self.data[symbol]['times'] = self.data[symbol]['times'][-30:]
print(f"{symbol}: ${price} at {current_time}")
else:
print(f"无法获取 {symbol} 的数据")
except Exception as e:
print(f"获取 {symbol} 数据时出错: {e}")
def plot_data(self):
"""绘制股票价格图表"""
plt.figure(figsize=(12, 6))
for symbol in self.symbols:
plt.plot(self.data[symbol]['times'], self.data[symbol]['prices'], label=symbol)
plt.title('实时股票价格')
plt.xlabel('时间')
plt.ylabel('价格 ($)')
plt.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
def start_monitoring(self, interval=60):
"""开始实时监控股票价格"""
fig, ax = plt.subplots(figsize=(12, 6))
def update(frame):
self.fetch_stock_data()
ax.clear()
for symbol in self.symbols:
ax.plot(self.data[symbol]['times'], self.data[symbol]['prices'], label=symbol)
ax.set_title('实时股票价格')
ax.set_xlabel('时间')
ax.set_ylabel('价格 ($)')
ax.legend()
plt.xticks(rotation=45)
plt.tight_layout()
ani = FuncAnimation(fig, update, interval=interval*1000)
plt.show()
if __name__ == "__main__":
# 示例股票代码
symbols = ['AAPL', 'MSFT', 'GOOGL']
monitor = StockMonitor(symbols)
monitor.fetch_stock_data() # 获取初始数据
monitor.start_monitoring(interval=60) # 每60秒更新一次
12. 总结
在本章中,我们学习了Python网络编程的各个方面,从基础的Socket编程到高级的Web服务器和客户端开发。以下是主要内容的总结:
- 网络基础:了解了网络模型和常见协议(TCP、UDP、HTTP等)
- Socket编程:学习了如何使用Python的socket模块创建客户端和服务器
- 高级Socket编程:探索了socketserver模块和异步I/O
- HTTP客户端编程:使用urllib和requests库发送HTTP请求
- HTTP服务器编程:使用http.server模块和Flask框架创建Web服务器
- WebSocket编程:实现了实时双向通信
- 电子邮件编程:学习了如何发送和接收电子邮件
- FTP编程:使用ftplib模块进行文件传输
- 网络安全编程:了解了SSL/TLS加密和数字签名
- 网络编程最佳实践:错误处理、资源管理和性能优化
- 实际应用案例:开发了聊天应用、Web爬虫和股票监控器
Python丰富的网络编程库和工具使其成为开发网络应用的理想选择。无论是简单的客户端脚本还是复杂的分布式系统,Python都能提供所需的功能和灵活性。
随着互联网和网络技术的不断发展,网络编程技能变得越来越重要。掌握Python网络编程不仅可以帮助您开发各种网络应用,还能为您在云计算、物联网和微服务架构等领域打下坚实的基础。
Python网络编程
1. 网络编程基础
作为Java开发者,您可能已经使用过Java的网络编程API,如Socket和ServerSocket。Python同样提供了强大的网络编程功能,允许您创建客户端和服务器应用程序,处理各种网络协议。
网络模型
在深入Python网络编程之前,让我们回顾一下网络通信的基本模型:
-
OSI七层模型:
- 物理层:处理比特流的传输
- 数据链路层:处理帧的传输
- 网络层:处理数据包的路由(IP协议)
- 传输层:处理端到端的连接(TCP/UDP协议)
- 会话层:管理会话
- 表示层:处理数据格式转换
- 应用层:为应用程序提供网络服务(HTTP、FTP等)
-
TCP/IP四层模型:
- 网络接口层:对应OSI的物理层和数据链路层
- 网络层:对应OSI的网络层(IP协议)
- 传输层:对应OSI的传输层(TCP/UDP协议)
- 应用层:对应OSI的会话层、表示层和应用层
常见网络协议
-
TCP (传输控制协议):
- 面向连接的协议
- 提供可靠的数据传输
- 具有流量控制和拥塞控制
- 适用于需要可靠传输的应用(如HTTP、FTP、SMTP)
-
UDP (用户数据报协议):
- 无连接的协议
- 不保证数据传输的可靠性
- 低开销,高效率
- 适用于实时应用(如视频流、游戏、DNS)
-
HTTP (超文本传输协议):
- 应用层协议,基于TCP
- 用于Web浏览器和服务器之间的通信
- 无状态协议
- HTTP/1.1、HTTP/2、HTTP/3版本
-
WebSocket:
- 提供全双工通信通道
- 基于TCP的应用层协议
- 用于Web应用中的实时通信
-
SMTP (简单邮件传输协议):
- 用于发送电子邮件
- 基于TCP的应用层协议
-
FTP (文件传输协议):
- 用于在客户端和服务器之间传输文件
- 使用两个TCP连接:控制连接和数据连接
2. Python Socket编程
Socket是网络编程的基础,它提供了一种在网络上发送和接收数据的方法。Python的socket
模块提供了对底层Socket API的访问。
创建Socket
import socket
# 创建TCP Socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建UDP Socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
参数说明:
socket.AF_INET
:使用IPv4地址族socket.AF_INET6
:使用IPv6地址族socket.SOCK_STREAM
:使用TCP协议socket.SOCK_DGRAM
:使用UDP协议
TCP客户端
import socket
def tcp_client():
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# 连接服务器
server_address = ('localhost', 8000)
print(f"连接到 {server_address[0]}:{server_address[1]}")
client_socket.connect(server_address)
# 发送数据
message = "Hello, Server!"
print(f"发送: {message}")
client_socket.sendall(message.encode('utf-8'))
# 接收响应
data = client_socket.recv(1024)
print(f"接收: {data.decode('utf-8')}")
finally:
# 关闭连接
print("关闭连接")
client_socket.close()
if __name__ == "__main__":
tcp_client()
TCP服务器
import socket
def tcp_server():
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_address = ('localhost', 8000)
print(f"启动服务器 {server_address[0]}:{server_address[1]}")
server_socket.bind(server_address)
# 监听连接
server_socket.listen(5) # 最多5个排队连接
try:
while True:
# 等待连接
print("等待连接...")
client_socket, client_address = server_socket.accept()
print(f"接受来自 {client_address[0]}:{client_address[1]} 的连接")
try:
# 接收数据
data = client_socket.recv(1024)
print(f"接收: {data.decode('utf-8')}")
if data:
# 发送响应
response = "Hello, Client!"
print(f"发送: {response}")
client_socket.sendall(response.encode('utf-8'))
else:
print("没有数据")
break
finally:
# 关闭客户端连接
print("关闭客户端连接")
client_socket.close()
finally:
# 关闭服务器Socket
print("关闭服务器")
server_socket.close()
if __name__ == "__main__":
tcp_server()
UDP客户端
import socket
def udp_client():
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# 服务器地址
server_address = ('localhost', 8000)
# 发送数据
message = "Hello, UDP Server!"
print(f"发送: {message}")
client_socket.sendto(message.encode('utf-8'), server_address)
# 接收响应
data, server = client_socket.recvfrom(1024)
print(f"从 {server[0]}:{server[1]} 接收: {data.decode('utf-8')}")
finally:
# 关闭Socket
print("关闭Socket")
client_socket.close()
if __name__ == "__main__":
udp_client()
UDP服务器
import socket
def udp_server():
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口
server_address = ('localhost', 8000)
print(f"启动UDP服务器 {server_address[0]}:{server_address[1]}")
server_socket.bind(server_address)
try:
while True:
# 接收数据
print("等待消息...")
data, client_address = server_socket.recvfrom(1024)
print(f"从 {client_address[0]}:{client_address[1]} 接收: {data.decode('utf-8')}")
# 发送响应
response = "Hello, UDP Client!"
print(f"发送: {response}")
server_socket.sendto(response.encode('utf-8'), client_address)
finally:
# 关闭服务器Socket
print("关闭服务器")
server_socket.close()
if __name__ == "__main__":
udp_server()
Socket选项
可以使用setsockopt
方法设置Socket选项:
import socket
# 创建Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置地址重用选项
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 设置超时
client_socket.settimeout(5.0) # 5秒超时
非阻塞Socket
默认情况下,Socket操作是阻塞的。可以设置为非阻塞模式:
import socket
# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置为非阻塞模式
client_socket.setblocking(False)
try:
client_socket.connect(('localhost', 8000))
except BlockingIOError:
# 连接操作正在进行中
pass
# 使用select模块处理非阻塞I/O
import select
readable, writable, exceptional = select.select([client_socket], [client_socket], [client_socket], 5.0)
if writable:
print("连接就绪")
client_socket.send(b"Hello")
3. 高级Socket编程
使用socketserver模块
Python的socketserver
模块提供了更高级的服务器实现,简化了服务器编程:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
处理TCP请求的类
"""
def handle(self):
# self.request是TCP套接字
data = self.request.recv(1024).strip()
print(f"{self.client_address[0]} 发送: {data.decode('utf-8')}")
# 发送响应
response = "已收到您的消息"
self.request.sendall(response.encode('utf-8'))
if __name__ == "__main__":
HOST, PORT = "localhost", 8000
# 创建服务器
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
# 启动服务器
print(f"服务器启动在 {HOST}:{PORT}")
server.serve_forever()
多线程服务器
使用ThreadingMixIn
创建多线程服务器:
import socketserver
class ThreadedTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024).strip()
print(f"线程 {threading.current_thread().name} 处理来自 {self.client_address[0]} 的请求")
print(f"接收: {data.decode('utf-8')}")
response = f"线程 {threading.current_thread().name} 已处理您的请求"
self.request.sendall(response.encode('utf-8'))
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
import threading
HOST, PORT = "localhost", 8000
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPHandler)
# 启动线程处理请求
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
print(f"服务器启动在 {HOST}:{PORT}")
print(f"服务器线程名: {server_thread.name}")
# 保持主线程运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
server.shutdown()
异步I/O
使用asyncio
模块实现异步网络编程:
import asyncio
async def handle_client(reader, writer):
# 获取客户端地址
addr = writer.get_extra_info('peername')
print(f"连接来自 {addr}")
# 读取数据
data = await reader.read(100)
message = data.decode('utf-8')
print(f"接收: {message}")
# 发送响应
response = f"已收到: {message}"
print(f"发送: {response}")
writer.write(response.encode('utf-8'))
await writer.drain()
# 关闭连接
print("关闭连接")
writer.close()
await writer.wait_closed()
async def main():
# 启动服务器
server = await asyncio.start_server(
handle_client, '127.0.0.1', 8000)
addr = server.sockets[0].getsockname()
print(f'服务器启动在 {addr}')
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main())
4. HTTP客户端编程
Python提供了多种方式来发送HTTP请求。
使用urllib
urllib
是Python标准库中的HTTP客户端:
from urllib import request, parse
import json
def fetch_data():
# GET请求
with request.urlopen('https://api.example.com/data') as response:
data = response.read().decode('utf-8')
return json.loads(data)
def post_data():
# POST请求数据
data = {
'name': 'John Doe',
'email': 'john@example.com'
}
data = parse.urlencode(data).encode('utf-8')
# 创建请求
req = request.Request('https://api.example.com/submit', data=data, method='POST')
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
# 发送请求
with request.urlopen(req) as response:
result = response.read().decode('utf-8')
return json.loads(result)
使用requests库
requests
是一个更简单、更强大的HTTP客户端库:
import requests
def fetch_data():
# GET请求
response = requests.get('https://api.example.com/data')
response.raise_for_status() # 如果请求失败则抛出异常
return response.json()
def post_data():
# POST请求
data = {
'name': 'John Doe',
'email': 'john@example.com'
}
response = requests.post('https://api.example.com/submit', data=data)
response.raise_for_status()
return response.json()
def upload_file():
# 上传文件
files = {'file': open('document.pdf', 'rb')}
response = requests.post('https://api.example.com/upload', files=files)
response.raise_for_status()
return response.json()
def custom_headers():
# 自定义请求头
headers = {
'User-Agent': 'My Python Client',
'Authorization': 'Bearer token123'
}
response = requests.get('https://api.example.com/protected', headers=headers)
response.raise_for_status()
return response.json()
def with_cookies():
# 使用会话保持Cookie
session = requests.Session()
# 登录
login_data = {'username': 'user', 'password': 'pass'}
session.post('https://example.com/login', data=login_data)
# 访问需要认证的页面
response = session.get('https://example.com/dashboard')
return response.text
def with_timeout():
# 设置超时
try:
response = requests.get('https://api.example.com/data', timeout=5)
response.raise_for_status()
return response.json()
except requests.Timeout:
print("请求超时")
return None
异步HTTP请求
使用aiohttp
库进行异步HTTP请求:
import aiohttp
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def fetch_multiple(urls):
tasks = [fetch(url) for url in urls]
return await asyncio.gather(*tasks)
async def main():
urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3'
]
results = await fetch_multiple(urls)
for url, result in zip(urls, results):
print(f"URL: {url}, 长度: {len(result)}")
if __name__ == "__main__":
asyncio.run(main())
5. HTTP服务器编程
使用http.server模块
Python的http.server
模块提供了简单的HTTP服务器实现:
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def _set_headers(self, content_type='text/html'):
self.send_response(200)
self.send_header('Content-type', content_type)
self.end_headers()
def do_GET(self):
if self.path == '/':
self._set_headers()
self.wfile.write(b"<html><body><h1>Welcome to my server!</h1></body></html>")
elif self.path == '/api/data':
self._set_headers('application/json')
data = {'message': 'Hello, World!', 'status': 'success'}
self.wfile.write(json.dumps(data).encode('utf-8'))
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 Not Found")
def do_POST(self):
if self.path == '/api/submit':
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
# 解析JSON数据
try:
data = json.loads(post_data.decode('utf-8'))
# 处理数据
response = {'status': 'success', 'received': data}
# 发送响应
self._set_headers('application/json')
self.wfile.write(json.dumps(response).encode('utf-8'))
except json.JSONDecodeError:
self.send_response(400)
self.end_headers()
self.wfile.write(b"Invalid JSON")
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 Not Found")
def run_server(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler, port=8000):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print(f"Starting server on port {port}...")
httpd.serve_forever()
if __name__ == "__main__":
run_server()
使用Flask框架
Flask是一个轻量级的Web框架,适合构建API和Web应用:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return "<h1>Welcome to my API</h1>"
@app.route('/api/data')
def get_data():
data = {'message': 'Hello, World!', 'status': 'success'}
return jsonify(data)
@app.route('/api/submit', methods=['POST'])
def submit_data():
if request.is_json:
data = request.get_json()
# 处理数据
response = {'status': 'success', 'received': data}
return jsonify(response)
else:
return jsonify({'error': 'Invalid JSON'}), 400
if __name__ == '__main__':
app.run(debug=True, port=8000)
使用FastAPI框架
FastAPI是一个现代、高性能的Web框架,支持异步请求处理和自动API文档生成:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.post("/items/")
def create_item(item: Item):
return item
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
if item_id < 0:
raise HTTPException(status_code=400, detail="Item ID must be positive")
return {"item_id": item_id, **item.dict()}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
6. WebSocket编程
WebSocket提供了全双工通信通道,适用于需要实时通信的Web应用。
使用websockets库
import asyncio
import websockets
# WebSocket服务器
async def echo(websocket, path):
async for message in websocket:
print(f"接收: {message}")
await websocket.send(f"Echo: {message}")
async def main():
async with websockets.serve(echo, "localhost", 8765):
await asyncio.Future() # 运行直到被取消
if __name__ == "__main__":
asyncio.run(main())
WebSocket客户端
import asyncio
import websockets
async def hello():
uri = "ws://localhost:8765"
async with websockets.connect(uri) as websocket:
message = "Hello WebSocket!"
await websocket.send(message)
print(f"发送: {message}")
response = await websocket.recv()
print(f"接收: {response}")
if __name__ == "__main__":
asyncio.run(hello())
使用Flask-SocketIO
Flask-SocketIO是Flask的WebSocket扩展:
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
@app.route('/')
def index():
return render_template('index.html')
@socketio.on('connect')
def test_connect():
emit('response', {'data': 'Connected'})
@socketio.on('message')
def handle_message(data):
print('received message: ' + data)
emit('response', {'data': f'Server received: {data}'})
@socketio.on('disconnect')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app, debug=True)
HTML客户端:
<!DOCTYPE html>
<html>
<head>
<title>Flask-SocketIO Test</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const socket = io();
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('response', (data) => {
console.log('Server response:', data);
document.getElementById('messages').innerHTML += '<p>' + data.data + '</p>';
});
document.getElementById('send').addEventListener('click', () => {
const message = document.getElementById('message').value;
socket.emit('message', message);
document.getElementById('message').value = '';
});
});
</script>
</head>
<body>
<h1>Flask-SocketIO Test</h1>
<div id="messages"></div>
<input type="text" id="message" placeholder="Enter message">
<button id="send">Send</button>
</body>
</html>
7. 电子邮件编程
Python提供了发送和接收电子邮件的功能。
使用smtplib发送邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_email():
# 邮件服务器配置
smtp_server = "smtp.gmail.com"
port = 587
sender_email = "your_email@gmail.com"
password = "your_password"
receiver_email = "recipient@example.com"
# 创建多部分消息
message = MIMEMultipart()
message["Subject"] = "Python邮件测试"
message["From"] = sender_email
message["To"] = receiver_email
# 添加正文
body = "这是一封由Python发送的测试邮件。"
message.attach(MIMEText(body, "plain"))
try:
# 创建SMTP会话
server = smtplib.SMTP(smtp_server, port)
server.ehlo() # 向SMTP服务器标识自己
server.starttls() # 启用TLS加密
server.ehlo() # 重新标识
server.login(sender_email, password)
# 发送邮件
server.sendmail(sender_email, receiver_email, message.as_string())
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
if __name__ == "__main__":
send_email()
发送HTML邮件和附件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import os
def send_email_with_attachment():
# 邮件服务器配置
smtp_server = "smtp.gmail.com"
port = 587
sender_email = "your_email@gmail.com"
password = "your_password"
receiver_email = "recipient@example.com"
# 创建多部分消息
message = MIMEMultipart()
message["Subject"] = "Python邮件测试 - 带附件"
message["From"] = sender_email
message["To"] = receiver_email
# 添加HTML正文
html = """
<html>
<body>
<h1>Python邮件测试</h1>
<p>这是一封由<b>Python</b>发送的测试邮件,包含<i>HTML</i>格式和附件。</p>
</body>
</html>
"""
message.attach(MIMEText(html, "html"))
# 添加附件
filename = "document.pdf" # 替换为实际文件路径
with open(filename, "rb") as f:
attachment = MIMEApplication(f.read(), _subtype="pdf")
attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(filename))
message.attach(attachment)
try:
# 创建SMTP会话
server = smtplib.SMTP(smtp_server, port)
server.ehlo()
server.starttls()
server.ehlo()
server.login(sender_email, password)
# 发送邮件
server.sendmail(sender_email, receiver_email, message.as_string())
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
finally:
server.quit()
使用imaplib接收邮件
import imaplib
import email
from email.header import decode_header
def fetch_emails():
# 邮件服务器配置
imap_server = "imap.gmail.com"
email_address = "your_email@gmail.com"
password = "your_password"
try:
# 连接到IMAP服务器
mail = imaplib.IMAP4_SSL(imap_server)
mail.login(email_address, password)
# 选择收件箱
mail.select("INBOX")
# 搜索邮件
status, messages = mail.search(None, "UNSEEN") # 未读邮件
if status == "OK":
# 获取邮件ID列表
email_ids = messages[0].split()
for email_id in email_ids:
# 获取邮件内容
status, msg_data = mail.fetch(email_id, "(RFC822)")
if status == "OK":
# 解析邮件
msg = email.message_from_bytes(msg_data[0][1])
# 解码主题
subject, encoding = decode_header(msg["Subject"])[0]
if isinstance(subject, bytes):
subject = subject.decode(encoding or "utf-8")
# 获