1. 网络编程基础概念
1.1 TCP/IP协议栈
Socket是应用层与传输层之间的抽象接口,基于TCP/IP协议栈实现。Python的socket模块提供了标准的BSD Socket接口实现,支持多种协议族:
- AF_INET:IPv4网络协议
- AF_INET6:IPv6网络协议
- AF_UNIX:Unix域套接字(本地进程通信)
1.2 Socket类型
Python中常用的Socket类型包括:
- SOCK_STREAM:面向连接的TCP套接字
- SOCK_DGRAM:无连接的UDP套接字
- SOCK_RAW:原始套接字(需要管理员权限)
2. TCP Socket编程详解
2.1 服务端实现
TCP服务端需要依次调用socket()、bind()、listen()和accept()方法:
python
import socket
# 创建TCP套接字
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置SO_REUSEADDR选项,避免端口占用问题
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址和端口
server.bind(('localhost', 8888))
# 开始监听,设置最大连接数为5
server.listen(5)
print("服务端启动,监听端口8888...")
while True:
# 接受客户端连接
client_socket, addr = server.accept()
print(f"连接来自 {addr}")
try:
# 接收数据(最多1024字节)
data = client_socket.recv(1024)
print(f"收到: {data.decode()}")
# 发送响应(模拟HTTP响应)
client_socket.send(b"HTTP/1.1 200 OK\r\n\r\nHello Client")
finally:
# 关闭客户端连接
client_socket.close()
2.2 客户端实现
TCP客户端需要调用socket()和connect()方法:
python
import socket
# 创建TCP套接字
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# 连接服务器
client.connect(('localhost', 8888))
# 发送数据
client.send(b"Hello Server")
# 接收响应
response = client.recv(1024)
print(f"服务端响应: {response.decode()}")
finally:
# 关闭连接
client.close()
2.3 多客户端处理(多线程)
单线程服务端一次只能处理一个客户端,使用多线程可以同时处理多个连接:
python
import socket
import threading
def handle_client(client_socket, addr):
try:
print(f"处理 {addr}")
data = client_socket.recv(1024)
print(f"收到: {data.decode()}")
client_socket.send(b"ACK")
finally:
client_socket.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8888))
server.listen(5)
while True:
client, addr = server.accept()
# 为每个客户端创建新线程
threading.Thread(target=handle_client, args=(client, addr)).start()
3. UDP Socket编程
UDP是无连接的协议,具有速度快但不可靠的特点,适用于实时性要求高但对可靠性要求不高的场景,如视频流、DNS查询等。
3.1 UDP服务端
python
import socket
# 创建UDP套接字
udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口
udp_server.bind(('localhost', 9999))
print("UDP服务端启动,监听端口9999...")
while True:
# 接收数据和客户端地址
data, addr = udp_server.recvfrom(1024)
print(f"来自{addr}: {data.decode()}")
# 发送响应到客户端地址
udp_server.sendto(b"OK", addr)
3.2 UDP客户端
python
import socket
# 创建UDP套接字
udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# 发送数据(不需要先建立连接)
udp_client.sendto(b"Hello UDP", ('localhost', 9999))
# 接收响应
response, _ = udp_client.recvfrom(1024)
print(response.decode())
finally:
udp_client.close()
4. 非阻塞Socket与select
使用非阻塞Socket配合select模块可以实现单线程处理多个连接:
python
import socket
import select
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False) # 设置为非阻塞模式
server.bind(('localhost', 8888))
server.listen(5)
inputs = [server] # 监控的可读socket列表
while True:
# 使用select监控socket列表
readable, _, _ = select.select(inputs, [], [])
for s in readable:
if s is server:
# 有新连接
client, addr = server.accept()
print(f"新连接: {addr}")
client.setblocking(False)
inputs.append(client)
else:
# 有数据可读
data = s.recv(1024)
if data:
print(f"收到: {data.decode()}")
s.send(b"ACK")
else:
# 连接关闭
print(f"关闭连接: {s.getpeername()}")
inputs.remove(s)
s.close()
5. HTTP协议与http.server
Python内置了简单的HTTP服务器实现:
python
from http.server import HTTPServer, BaseHTTPRequestHandler
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
# 设置响应状态码
self.send_response(200)
# 设置响应头
self.send_header('Content-type', 'text/html')
self.end_headers()
# 设置响应内容
self.wfile.write(b"<h1>Hello World</h1>")
self.wfile.write(f"<p>访问路径: {self.path}</p>".encode())
# 创建HTTP服务器
server = HTTPServer(('localhost', 8000), MyHandler)
print("HTTP服务器启动,访问 http://localhost:8000")
server.serve_forever()
6. 第三方HTTP库
6.1 requests库(同步)
python
import requests
# GET请求示例
resp = requests.get('https://api.github.com')
print(f"状态码: {resp.status_code}")
print(f"响应内容: {resp.json()}")
# POST请求示例
data = {'key': 'value'}
resp = requests.post('https://httpbin.org/post', data=data)
print(resp.json())
6.2 aiohttp库(异步)
python
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://example.com')
print(html[:100]) # 打印前100个字符
asyncio.run(main())
7. WebSocket编程
WebSocket实现了全双工通信,适用于实时应用如聊天室、在线游戏等。
7.1 服务端实现
python
import asyncio
import websockets
async def echo(websocket, path):
async for message in websocket:
print(f"收到消息: {message}")
await websocket.send(f"Echo: {message}")
start_server = websockets.serve(echo, "localhost", 8765)
print("WebSocket服务器启动,监听端口8765...")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
7.2 客户端实现(浏览器)
html
<script>
const ws = new WebSocket("ws://localhost:8765");
ws.onopen = () => {
console.log("连接已建立");
ws.send("Hello Server");
};
ws.onmessage = (event) => {
console.log(`收到消息: ${event.data}`);
};
ws.onclose = () => {
console.log("连接已关闭");
};
</script>
8. 异步IO网络编程实战
使用asyncio实现高并发TCP服务器:
python
import asyncio
async def handle_client(reader, writer):
addr = writer.get_extra_info('peername')
print(f"新连接: {addr}")
try:
while True:
data = await reader.read(100)
if not data:
break
print(f"收到: {data.decode()}")
writer.write(b"ACK: " + data)
await writer.drain() # 确保数据已发送
except Exception as e:
print(f"错误: {e}")
finally:
print(f"关闭连接: {addr}")
writer.close()
await writer.wait_closed()
async def main():
server = await asyncio.start_server(
handle_client, 'localhost', 8888)
addr = server.sockets[0].getsockname()
print(f"服务器启动,监听 {addr}")
async with server:
await server.serve_forever()
asyncio.run(main())
9. 实战项目:简易聊天室
结合asyncio和WebSocket实现多用户聊天室:
9.1 服务端代码
python
import asyncio
import websockets
# 存储所有连接的客户端
connected = set()
async def chat_handler(websocket, path):
# 新客户端连接
connected.add(websocket)
print(f"新用户加入,当前用户数: {len(connected)}")
try:
async for message in websocket:
# 广播消息给所有其他客户端
for conn in connected:
if conn != websocket:
await conn.send(f"用户说: {message}")
finally:
# 客户端断开连接
connected.remove(websocket)
print(f"用户离开,剩余用户数: {len(connected)}")
start_server = websockets.serve(chat_handler, "localhost", 6789)
print("聊天室服务器启动,监听端口6789...")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
9.2 客户端代码(HTML/JavaScript)
html
<!DOCTYPE html>
<html>
<head>
<title>简易聊天室</title>
<style>
#messages { height: 300px; overflow-y: scroll; border: 1px solid #ccc; }
</style>
</head>
<body>
<div id="messages"></div>
<input id="message" type="text" placeholder="输入消息">
<button onclick="sendMessage()">发送</button>
<script>
const ws = new WebSocket("ws://localhost:6789");
const messages = document.getElementById('messages');
const messageInput = document.getElementById('message');
ws.onmessage = (event) => {
const message = document.createElement('div');
message.textContent = event.data;
messages.appendChild(message);
messages.scrollTop = messages.scrollHeight;
};
function sendMessage() {
const message = messageInput.value;
if (message) {
ws.send(message);
messageInput.value = '';
}
}
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
</script>
</body>
</html>
10. 总结与最佳实践
10.1 TCP与UDP比较
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠 | 不可靠 |
| 顺序保证 | 保证 | 不保证 |
| 流量控制 | 有 | 无 |
| 传输速度 | 较慢 | 较快 |
| 适用场景 | 文件传输、网页浏览等 | 视频流、DNS查询等 |
10.2 网络编程最佳实践
- 错误处理:始终处理网络异常(连接超时、断开等)
- 资源管理:使用try/finally或with语句确保socket关闭
- 性能优化 :
- 对于IO密集型应用使用异步编程
- 合理设置缓冲区大小
- 考虑使用连接池
- 安全性 :
- 验证输入数据
- 使用TLS加密敏感数据
- 防范DDoS攻击
10.3 技术选型建议
- 简单REST API:requests + Flask/Django
- 高并发服务:aiohttp/asyncio
- 实时应用:WebSocket
- 底层网络操作:socket模块