网络编程
网络编程是Python的重要应用之一,本章介绍网络基础、TCP/UDP编程和socket套接字的使用。
网络基础概念
在深入socket编程之前,我们需要了解一些基本的网络概念。
OSI七层模型 vs TCP/IP四层模型
┌─────────────────────────────────────────────────────────────────┐ │ OSI七层模型 │ ├─────────┬───────────────────────────────────────────────────────┤ │ 应用层 │ 应用层 (HTTP, FTP, SMTP) │ │ 表示层 │ (数据格式转换、加解密) │ │ 会话层 │ (会话管理、建立/维护连接) │ │ 传输层 │ (TCP/UDP、端口) │ │ 网络层 │ (IP、路由) │ │ 链路层 │ (MAC地址、数据帧传输) │ │ 物理层 │ (物理介质、比特流) │ └─────────┴───────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ TCP/IP四层模型 │ ├─────────┬───────────────────────────────────────────────────────┤ │ 应用层 │ HTTP, FTP, SMTP, DNS │ │ 传输层 │ TCP, UDP │ │ 网络层 │ IP │ │ 网络接口层 │ Ethernet, Wi-Fi, PPP │ └─────────┴───────────────────────────────────────────────────────┘
IP地址与端口
IP地址用于唯一标识网络中的设备,端口号用于标识设备上的具体应用程序。
┌──────────────────────────────────────────────────────┐ │ 一台计算机上的多个网络应用 │ ├──────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 浏览器 │ │ QQ │ │ 邮件 │ │ │ │ :8080 │ │ :45678 │ │ :110 │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ ┌────┴───────────────┴───────────────┴─────┐ │ │ │ IP: 192.168.1.100 │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ 网络接口 (网卡) │ │ │ │ │ └─────────────────────┘ │ │ │ └───────────────────────────────────────────┘ │ │ │ │ 端口范围: 0 ~ 65535 │ │ 0-1023: 特权端口 (需要管理员权限) │ │ 1024+: 普通端口 (任意用户可用) │ └──────────────────────────────────────────────────────┘
TCP vs UDP对比
| 特性 | TCP | UDP |
|---|---|---|
| 连接类型 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输 | 不可靠传输 |
| 数据传输 | 字节流 | 数据报文 |
| 速度 | 较慢 | 较快 |
| 资源消耗 | 较多 | 较少 |
| 适用场景 | 文件传输、网页、邮件 | 视频通话、DNS、在线游戏 |
socket简介
Socket(套接字)是网络通信的基础,Python标准库的socket模块提供了丰富的网络编程接口。
┌─────────────────────────────────────────────────────────┐ │ Socket通信原理 │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ 网络 ┌─────────────┐│ │ │ 客户端程序 │◀═══════════════════════▶│ 服务器程序 ││ │ │ │ socket() │ ││ │ │ socket() │ │ │ socket() ││ │ │ │ │ │ │ │ ││ │ │ connect()───┼─────────┼──────────────┼───bind() ││ │ │ │ │ │ │ │ ││ │ │ │ │ TCP三次握手 │ listen() ││ │ │ │ │ │ │ │ ││ │ │ │ │ ▼ │ accept() ││ │ │ send()/recv()◀═══════════════════════▶send()/recv()││ │ │ │ │ │ │ ││ │ │ close()─────┼─────────────────────────┼───close() ││ │ └─────────────┘ └─────────────┘│ │ │ │ 地址: 192.168.1.100:8080 ◀═════════════▶ :8080 │ │ │ └─────────────────────────────────────────────────────────┘
Python
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方法
# socket() 创建socket对象
# bind((host, port))绑定地址和端口
# listen(backlog) 监听连接 (TCP)
# accept() 接受连接 (TCP)
# connect() 连接服务器 (TCP)
# connect_ex() 连接服务器,返回错误码
# send() 发送数据 (TCP)
# sendto() 发送数据 (UDP)
# recv(bufsize) 接收数据
# recvfrom() 接收数据和发送方地址 (UDP)
# close() 关闭socket
# shutdown(how) 关闭读写半关闭
# setblocking(True/False) 设置阻塞模式
# settimeout(timeout) 设置超时时间
TCP服务器编程
TCP服务器需要绑定端口、监听连接、接受连接、处理请求、关闭连接。
┌─────────────────────────────────────────────────────────┐ │ TCP服务器工作流程 │ ├─────────────────────────────────────────────────────────┤ │ │ │ 1. 创建socket │ │ │ │ │ ▼ │ │ 2. bind() ──绑定IP和端口──▶ (192.168.1.100:8080) │ │ │ │ │ ▼ │ │ 3. listen() ──开始监听──▶ 等待客户端连接... │ │ │ │ │ ▼ │ │ 4. accept() ◀──阻塞等待──▶ 客户端连接请求 │ │ │ │ │ ▼ │ │ 5. recv/send ◀──双向通信──▶ 处理业务逻辑 │ │ │ │ │ ▼ │ │ 6. close() ◀──关闭连接──▶ 通信完成 │ │ │ └─────────────────────────────────────────────────────────┘
Python
import socket
import threading
def handle_client(client_socket, client_addr):
"""处理单个客户端请求"""
print(f"[+] 客户端连接: {client_addr}")
try:
while True:
data = client_socket.recv(1024)
if not data:
break
print(f"[来自 {client_addr}]: {data.decode('utf-8')}")
# 回显消息
response = f"服务器收到: {data.decode('utf-8')}"
client_socket.send(response.encode("utf-8"))
except Exception as e:
print(f"[!] 错误: {e}")
finally:
client_socket.close()
print(f"[-] 客户端断开: {client_addr}")
def tcp_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("0.0.0.0", 8080))
server_socket.listen(5)
print("[*] TCP服务器监听中 :8080")
try:
while True:
client_socket, client_addr = server_socket.accept()
# 为每个客户端创建新线程
client_thread = threading.Thread(target=handle_client,
args=(client_socket, client_addr))
client_thread.daemon = True
client_thread.start()
except KeyboardInterrupt:
print("\n[*] 服务器关闭")
finally:
server_socket.close()
# tcp_server()
💡 提示:使用`setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)`可以避免服务器重启时出现"Address already in use"错误。
TCP客户端编程
TCP客户端需要连接服务器、发送数据、接收响应、关闭连接。
Python
import socket
def tcp_client():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client_socket.connect(("127.0.0.1", 8080))
print("[*] 已连接到服务器")
while True:
msg = input("我: ")
if msg.lower() == "quit":
break
client_socket.send(msg.encode("utf-8"))
response = client_socket.recv(1024)
print(f"服务器: {response.decode('utf-8')}")
except ConnectionRefusedError:
print("[!] 无法连接到服务器")
except Exception as e:
print(f"[!] 错误: {e}")
finally:
client_socket.close()
print("[*] 连接已关闭")
# tcp_client()
UDP编程
UDP是无连接协议,通信更简单,但不如TCP可靠。
┌─────────────────────────────────────────────────────────┐ │ UDP通信模型 │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ 网络 ┌─────────────┐│ │ │ 发送方 │◀═══════════════════════▶│ 接收方 ││ │ │ │ 无需连接 │ ││ │ │ socket() │ │ │ socket() ││ │ │ │ │ │ │ │ ││ │ │ │ │ sendto() │ │ ││ │ │ │ │ │ │ │ ││ │ │ │ │ ├──────────────┼──recvfrom()││ │ │ │ │ │ │ │ ││ │ │ recvfrom()◀─┼─────────┼──────────────┼──sendto() ││ │ │ │ │ │ │ ││ │ │ close() │ │ close() ││ │ └─────────────┘ └─────────────┘│ │ │ │ 特点: │ │ - 无需建立连接 │ │ - 每个数据包独立发送 │ │ - 不保证顺序和可靠性 │ │ - 更适合实时性要求高的应用 │ │ │ └─────────────────────────────────────────────────────────┘
Python
import socket
# ===== UDP服务器 =====
def udp_server():
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(("127.0.0.1", 8081))
print("[*] UDP服务器监听中 :8081")
try:
while True:
data, addr = udp_socket.recvfrom(1024)
print(f"[来自 {addr}]: {data.decode('utf-8')}")
response = f"服务器收到: {data.decode('utf-8')}"
udp_socket.sendto(response.encode("utf-8"), addr)
except KeyboardInterrupt:
print("\n[*] 服务器关闭")
finally:
udp_socket.close()
# ===== UDP客户端 =====
def udp_client():
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_addr = ("127.0.0.1", 8081)
try:
while True:
msg = input("我: ")
if msg.lower() == "quit":
break
udp_socket.sendto(msg.encode("utf-8"), server_addr)
data, _ = udp_socket.recvfrom(1024)
print(f"服务器: {data.decode('utf-8')}")
except Exception as e:
print(f"[!] 错误: {e}")
finally:
udp_socket.close()
# udp_server() # 或 udp_client()
Python访问HTTP服务
虽然可以使用socket发送HTTP请求,但使用urllib或requests更方便。
Python
import urllib.request
import urllib.parse
import json
# ===== GET请求 =====
with urllib.request.urlopen("https://httpbin.org/get") as response:
data = response.read().decode("utf-8")
print(data)
# ===== 带参数的GET请求 =====
params = urllib.parse.urlencode({"name": "张三", "age": 25})
url = f"https://httpbin.org/get?{params}"
with urllib.request.urlopen(url) as response:
print(response.read().decode("utf-8"))
# ===== POST请求 =====
data = urllib.parse.urlencode({"username": "admin", "password": "123456"})
data = data.encode("utf-8")
req = urllib.request.Request("https://httpbin.org/post", data=data)
with urllib.request.urlopen(req) as response:
print(response.read().decode("utf-8"))
# ===== 设置请求头 =====
req = urllib.request.Request(
"https://httpbin.org/headers",
headers={"User-Agent": "Mozilla/5.0", "Accept": "application/json"}
)
with urllib.request.urlopen(req) as response:
print(response.read().decode("utf-8"))
# ===== 处理超时 =====
try:
with urllib.request.urlopen("https://httpbin.org/delay/5", timeout=3) as response:
print(response.read().decode("utf-8"))
except urllib.error.URLError as e:
print(f"请求超时: {e}")
章节总结
关键知识点:
- OSI七层模型:应用层、表示层、会话层、传输层、网络层、链路层、物理层
- TCP特点:面向连接、可靠传输、字节流、速度较慢
- UDP特点:无连接、不可靠传输、数据报文、速度较快
- Socket:网络编程的基础,封装了TCP/IP协议栈的接口
- IP地址+端口:唯一标识网络中的应用程序
- TCP服务器流程:socket() → bind() → listen() → accept() → recv/send → close()
- TCP客户端流程:socket() → connect() → send/recv → close()
- UDP通信:socket() → bind() → recvfrom/sendto → close()
socket常用参数速查:
| 参数 | 说明 |
|---|---|
| socket.AF_INET | IPv4协议 |
| socket.AF_INET6 | IPv6协议 |
| socket.SOCK_STREAM | TCP流式socket |
| socket.SOCK_DGRAM | UDP数据报socket |
课后测验
问题1:TCP和UDP的主要区别是什么?
问题2:TCP服务器的工作流程是什么?
问题3:socket.SOCK_DGRAM表示什么类型的socket?
课后练习
练习1:TCP聊天服务器
编写一个TCP服务器,能够同时服务多个客户端,服务器广播消息给所有连接的客户端。
提示
- 使用线程为每个客户端服务
- 维护一个客户端列表
- 当收到消息时,向所有客户端广播
- 注意线程安全问题
练习2:UDP心跳检测
编写一个UDP客户端,每隔5秒向服务器发送心跳包,服务器检测超过15秒未收到心跳则判定客户端离线。
提示
- 使用time模块记录发送时间
- 维护一个字典存储客户端的最后心跳时间
- 使用线程定期检查超时客户端
- 超时时间可设为15秒
🐍 Python 在线代码编辑器
📝 运行结果