第11章 网络编程

网络编程是现代软件开发中不可或缺的技能,Python提供了丰富的网络编程库和工具,让开发者能够轻松构建各种网络应用。本章将从基础的Socket编程开始,逐步深入到HTTP客户端和服务器开发、异步网络编程以及网络安全等高级主题。

本系列文章所使用到的示例源码:Python从入门到精通示例代码

11.1 网络编程基础

网络协议概述

网络协议是计算机网络中进行数据交换而建立的规则、标准或约定。理解这些协议对于网络编程至关重要。

TCP/IP协议栈

TCP/IP协议栈是互联网的基础,它包含四个层次:

  1. 应用层:HTTP、HTTPS、FTP、SMTP、DNS等
  2. 传输层:TCP、UDP
  3. 网络层:IP、ICMP、ARP
  4. 链路层:以太网、WiFi等

常用网络协议

  • HTTP/HTTPS:超文本传输协议,Web应用的基础
  • FTP:文件传输协议
  • SMTP:简单邮件传输协议
  • DNS:域名系统
  • SSH:安全外壳协议

客户端-服务器模型

客户端-服务器(C/S)架构是网络应用的基本模式:

  • 客户端:发起请求的一方
  • 服务器:响应请求的一方
  • 请求-响应模式:客户端发送请求,服务器处理并返回响应

IP地址和端口

IP地址

  • IPv4:32位地址,如 192.168.1.1
  • IPv6:128位地址,如 2001:db8::1

端口号

端口号用于标识主机上的不同服务:

  • 知名端口(0-1023):HTTP(80)、HTTPS(443)、FTP(21)、SSH(22)
  • 注册端口(1024-49151):应用程序注册使用
  • 动态端口(49152-65535):临时分配使用

11.2 Socket编程

Socket(套接字)是网络编程的基础,它提供了进程间通信的接口。

Socket概念

Socket是网络通信的端点,可以理解为网络编程的”插座”。每个Socket都有一个地址,包括IP地址和端口号。

套接字类型

  • TCP Socket(SOCK_STREAM):面向连接,可靠传输
  • UDP Socket(SOCK_DGRAM):无连接,不可靠但高效

Python的socket模块

Python的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)

地址族

  • AF_INET:IPv4地址族
  • AF_INET6:IPv6地址族
  • AF_UNIX:Unix域套接字

TCP Socket编程实战

让我们通过实际的代码示例来学习TCP Socket编程。

TCP服务器端

首先创建一个TCP服务器,它能够处理多个客户端连接:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TCP服务器示例
"""

import socket
import threading
import time

def handle_client(client_socket, client_address):
    """处理客户端连接"""
    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_address} 已连接")

    try:
        while True:
            # 接收客户端数据
            data = client_socket.recv(1024)
            if not data:
                break

            message = data.decode('utf-8')
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 收到来自 {client_address} 的消息: {message}")

            # 回复客户端
            response = f"服务器收到: {message}"
            client_socket.send(response.encode('utf-8'))

    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 处理客户端 {client_address} 时发生错误: {e}")
    finally:
        client_socket.close()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_address} 已断开连接")

def start_server():
    """启动TCP服务器"""
    # 创建socket对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 设置socket选项,允许地址重用
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 绑定地址和端口
    host = 'localhost'
    port = 8888
    server_socket.bind((host, port))

    # 开始监听
    server_socket.listen(5)
    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] TCP服务器启动,监听 {host}:{port}")

    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(f"\n[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器正在关闭...")
    finally:
        server_socket.close()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器已关闭")

if __name__ == '__main__':
    start_server()

TCP客户端

接下来创建TCP客户端来连接服务器:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TCP客户端示例
"""

import socket
import time

def start_client():
    """启动TCP客户端"""
    # 创建socket对象
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # 连接服务器
        host = 'localhost'
        port = 8888
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 正在连接服务器 {host}:{port}...")
        client_socket.connect((host, port))
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 已连接到服务器")

        # 发送消息
        messages = [
            "Hello, Server!",
            "这是第二条消息",
            "Python网络编程测试",
            "再见!"
        ]

        for message in messages:
            # 发送数据
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息: {message}")
            client_socket.send(message.encode('utf-8'))

            # 接收服务器响应
            response = client_socket.recv(1024)
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器响应: {response.decode('utf-8')}")

            time.sleep(1)  # 等待1秒

    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端错误: {e}")
    finally:
        client_socket.close()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端已断开连接")

if __name__ == '__main__':
    start_client()

运行TCP示例

首先启动服务器:

python tcp_server.py

服务器输出:

[2025-08-15 21:47:55] TCP服务器启动,监听 localhost:8888

然后在另一个终端启动客户端:

python tcp_client.py

客户端输出:

[2025-08-15 21:48:07] 正在连接服务器 localhost:8888...
[2025-08-15 21:48:07] 已连接到服务器
[2025-08-15 21:48:07] 发送消息: Hello, Server!
[2025-08-15 21:48:07] 服务器响应: 服务器收到: Hello, Server!
[2025-08-15 21:48:08] 发送消息: 这是第二条消息
[2025-08-15 21:48:08] 服务器响应: 服务器收到: 这是第二条消息
[2025-08-15 21:48:09] 发送消息: Python网络编程测试
[2025-08-15 21:48:09] 服务器响应: 服务器收到: Python网络编程测试
[2025-08-15 21:48:10] 发送消息: 再见!
[2025-08-15 21:48:10] 服务器响应: 服务器收到: 再见!
[2025-08-15 21:48:11] 客户端已断开连接

服务器端同时显示:

[2025-08-15 21:48:07] 客户端 ('127.0.0.1', 50531) 已连接
[2025-08-15 21:48:07] 收到来自 ('127.0.0.1', 50531) 的消息: Hello, Server!
[2025-08-15 21:48:08] 收到来自 ('127.0.0.1', 50531) 的消息: 这是第二条消息
[2025-08-15 21:48:09] 收到来自 ('127.0.0.1', 50531) 的消息: Python网络编程测试
[2025-08-15 21:48:10] 收到来自 ('127.0.0.1', 50531) 的消息: 再见!
[2025-08-15 21:48:11] 客户端 ('127.0.0.1', 50531) 已断开连接

UDP Socket编程实战

UDP是无连接的协议,不需要建立连接就可以发送数据,适合对实时性要求高的应用。

UDP服务器端

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
UDP服务器示例
"""

import socket
import time

def start_udp_server():
    """启动UDP服务器"""
    # 创建UDP socket对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 绑定地址和端口
    host = 'localhost'
    port = 9999
    server_socket.bind((host, port))

    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] UDP服务器启动,监听 {host}:{port}")

    try:
        while True:
            # 接收数据和客户端地址
            data, client_address = server_socket.recvfrom(1024)
            message = data.decode('utf-8')

            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 收到来自 {client_address} 的消息: {message}")

            # 回复客户端
            response = f"UDP服务器收到: {message}"
            server_socket.sendto(response.encode('utf-8'), client_address)
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 已回复客户端 {client_address}")

    except KeyboardInterrupt:
        print(f"\n[{time.strftime('%Y-%m-%d %H:%M:%S')}] UDP服务器正在关闭...")
    finally:
        server_socket.close()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] UDP服务器已关闭")

if __name__ == '__main__':
    start_udp_server()

UDP客户端

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
UDP客户端示例
"""

import socket
import time

def start_udp_client():
    """启动UDP客户端"""
    # 创建UDP socket对象
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    try:
        # 服务器地址
        server_host = 'localhost'
        server_port = 9999
        server_address = (server_host, server_port)

        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] UDP客户端启动,目标服务器 {server_host}:{server_port}")

        # 发送消息列表
        messages = [
            "Hello UDP Server!",
            "这是UDP通信测试",
            "无连接协议演示",
            "UDP消息结束"
        ]

        for message in messages:
            # 发送数据到服务器
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息: {message}")
            client_socket.sendto(message.encode('utf-8'), server_address)

            # 接收服务器响应
            response, server_addr = client_socket.recvfrom(1024)
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器 {server_addr} 响应: {response.decode('utf-8')}")

            time.sleep(1)  # 等待1秒

    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] UDP客户端错误: {e}")
    finally:
        client_socket.close()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] UDP客户端已关闭")

if __name__ == '__main__':
    start_udp_client()

运行UDP示例

启动UDP服务器:

python udp_server.py

服务器输出:

[2025-08-15 21:48:29] UDP服务器启动,监听 localhost:9999

启动UDP客户端:

python udp_client.py

客户端输出:

[2025-08-15 21:48:36] UDP客户端启动,目标服务器 localhost:9999
[2025-08-15 21:48:36] 发送消息: Hello UDP Server!
[2025-08-15 21:48:36] 服务器 ('127.0.0.1', 9999) 响应: UDP服务器收到: Hello UDP Server!
[2025-08-15 21:48:37] 发送消息: 这是UDP通信测试
[2025-08-15 21:48:37] 服务器 ('127.0.0.1', 9999) 响应: UDP服务器收到: 这是UDP通信测试
[2025-08-15 21:48:38] 发送消息: 无连接协议演示
[2025-08-15 21:48:38] 服务器 ('127.0.0.1', 9999) 响应: UDP服务器收到: 无连接协议演示
[2025-08-15 21:48:39] 发送消息: UDP消息结束
[2025-08-15 21:48:39] 服务器 ('127.0.0.1', 9999) 响应: UDP服务器收到: UDP消息结束
[2025-08-15 21:48:40] UDP客户端已关闭

服务器端显示:

[2025-08-15 21:48:36] 收到来自 ('127.0.0.1', 57540) 的消息: Hello UDP Server!
[2025-08-15 21:48:36] 已回复客户端 ('127.0.0.1', 57540)
[2025-08-15 21:48:37] 收到来自 ('127.0.0.1', 57540) 的消息: 这是UDP通信测试
[2025-08-15 21:48:37] 已回复客户端 ('127.0.0.1', 57540)
[2025-08-15 21:48:38] 收到来自 ('127.0.0.1', 57540) 的消息: 无连接协议演示
[2025-08-15 21:48:38] 已回复客户端 ('127.0.0.1', 57540)
[2025-08-15 21:48:39] 收到来自 ('127.0.0.1', 57540) 的消息: UDP消息结束
[2025-08-15 21:48:39] 已回复客户端 ('127.0.0.1', 57540)

TCP vs UDP 对比

特性 TCP UDP
连接性 面向连接 无连接
可靠性 可靠传输 不可靠传输
速度 较慢 较快
数据完整性 保证 不保证
适用场景 文件传输、网页浏览 视频直播、游戏

Socket编程最佳实践

1. 异常处理

try:
    # socket操作
    pass
except socket.error as e:
    print(f"Socket错误: {e}")
except Exception as e:
    print(f"其他错误: {e}")
finally:
    # 确保关闭socket
    if 'sock' in locals():
        sock.close()

2. 超时设置

# 设置超时时间
sock.settimeout(10.0)  # 10秒超时

3. 地址重用

# 允许地址重用,避免"Address already in use"错误
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

4. 缓冲区管理

# 设置接收缓冲区大小
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)

# 设置发送缓冲区大小
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)

11.3 HTTP客户端编程

HTTP(超文本传输协议)是Web应用的基础协议。Python提供了多种方式来进行HTTP客户端编程。

HTTP协议基础

HTTP请求方法

  • GET:获取资源
  • POST:提交数据
  • PUT:更新资源
  • DELETE:删除资源
  • HEAD:获取响应头
  • OPTIONS:获取支持的方法

HTTP状态码

  • 2xx:成功(200 OK、201 Created、204 No Content)
  • 3xx:重定向(301 Moved Permanently、302 Found、304 Not Modified)
  • 4xx:客户端错误(400 Bad Request、401 Unauthorized、404 Not Found)
  • 5xx:服务器错误(500 Internal Server Error、502 Bad Gateway、503 Service Unavailable)

urllib模块

urllib是Python标准库中的HTTP客户端模块,无需安装第三方库。

urllib.request基本使用

import urllib.request
import urllib.parse
import json

# GET请求
response = urllib.request.urlopen('https://httpbin.org/get')
print(f"状态码: {response.getcode()}")
print(f"响应头: {dict(response.headers)}")
data = response.read().decode('utf-8')
print(f"响应内容: {data}")

# POST请求
post_data = {'key': 'value', 'name': 'Python'}
data = urllib.parse.urlencode(post_data).encode('utf-8')
req = urllib.request.Request('https://httpbin.org/post', data=data)
response = urllib.request.urlopen(req)
print(f"POST响应: {response.read().decode('utf-8')}")

requests库

requests库是Python中最流行的HTTP客户端库,提供了更简洁的API。

安装requests

pip install requests

requests基本使用

让我们通过实际的代码示例来学习HTTP客户端编程:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
HTTP客户端编程示例
"""

import urllib.request
import urllib.parse
import urllib.error
import requests
import json
import time

def test_urllib():
    """测试urllib模块"""
    print("=== urllib模块测试 ===")

    try:
        # GET请求
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送urllib GET请求...")
        response = urllib.request.urlopen('https://httpbin.org/get', timeout=5)
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] GET请求状态码: {response.getcode()}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 响应头Content-Type: {response.headers.get('Content-Type')}")

        data = json.loads(response.read().decode('utf-8'))
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端IP: {data.get('origin')}")

    except urllib.error.URLError as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] urllib GET请求失败: {e}")
    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] urllib GET请求异常: {e}")

    try:
        # POST请求
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送urllib POST请求...")
        post_data = {
            'name': 'Python网络编程',
            'version': '3.9',
            'framework': 'urllib'
        }

        data = urllib.parse.urlencode(post_data).encode('utf-8')
        req = urllib.request.Request('https://httpbin.org/post', data=data)
        req.add_header('Content-Type', 'application/x-www-form-urlencoded')

        response = urllib.request.urlopen(req, timeout=5)
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] POST请求状态码: {response.getcode()}")

        result = json.loads(response.read().decode('utf-8'))
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 提交的表单数据: {result.get('form')}")

    except urllib.error.URLError as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] urllib POST请求失败: {e}")
    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] urllib POST请求异常: {e}")

def test_requests():
    """测试requests库"""
    print("\n=== requests库测试 ===")

    try:
        # GET请求
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送requests GET请求...")
        response = requests.get('https://httpbin.org/get', timeout=5)
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] GET请求状态码: {response.status_code}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 响应头Content-Type: {response.headers.get('Content-Type')}")

        data = response.json()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端IP: {data.get('origin')}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] User-Agent: {data.get('headers', {}).get('User-Agent')}")

    except requests.RequestException as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] requests GET请求失败: {e}")
    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] requests GET请求异常: {e}")

    try:
        # POST请求
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送requests POST请求...")
        post_data = {
            'name': 'Python网络编程',
            'version': '3.9',
            'framework': 'requests',
            'features': ['简单易用', '功能强大', '社区活跃']
        }

        headers = {
            'User-Agent': 'Python-HTTP-Client/1.0',
            'Content-Type': 'application/json'
        }

        response = requests.post(
            'https://httpbin.org/post',
            json=post_data,
            headers=headers,
            timeout=5
        )

        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] POST请求状态码: {response.status_code}")

        result = response.json()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 提交的JSON数据: {result.get('json')}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 请求头User-Agent: {result.get('headers', {}).get('User-Agent')}")

    except requests.RequestException as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] requests POST请求失败: {e}")
    except Exception as e:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] requests POST请求异常: {e}")

if __name__ == '__main__':
    test_urllib()
    test_requests()

运行HTTP客户端示例

python http_client_demo.py

实际运行输出:

=== urllib模块测试 ===
[2025-08-15 21:48:48] 发送urllib GET请求...
[2025-08-15 21:48:48] urllib GET请求失败: <urlopen error timed out>
[2025-08-15 21:48:53] 发送urllib POST请求...
[2025-08-15 21:48:53] urllib POST请求失败: <urlopen error timed out>

=== requests库测试 ===
[2025-08-15 21:48:58] 发送requests GET请求...
[2025-08-15 21:48:59] GET请求状态码: 200
[2025-08-15 21:48:59] 响应头Content-Type: application/json
[2025-08-15 21:48:59] 客户端IP: 116.21.38.173
[2025-08-15 21:48:59] User-Agent: python-requests/2.32.3
[2025-08-15 21:48:59] 发送requests POST请求...
[2025-08-15 21:49:00] POST请求状态码: 200
[2025-08-15 21:49:00] 提交的JSON数据: {'name': 'Python网络编程', 'version': '3.9', 'framework': 'requests', 'features': ['简单易用', '功能强大', '社区活跃']}
[2025-08-15 21:49:00] 请求头User-Agent: Python-HTTP-Client/1.0

从输出可以看到,requests库相比urllib更加稳定和易用,成功完成了HTTP请求。

requests高级功能

会话管理

import requests

# 创建会话对象
session = requests.Session()
session.headers.update({'User-Agent': 'My-App/1.0'})

# 使用会话发送请求
response = session.get('https://httpbin.org/get')
print(response.json())

# 会话会自动处理cookies
session.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
response = session.get('https://httpbin.org/cookies')
print(response.json())

文件上传

import requests

# 上传文件
files = {'file': open('example.txt', 'rb')}
response = requests.post('https://httpbin.org/post', files=files)
print(response.json())

超时和重试

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

# 配置重试策略
retry_strategy = Retry(
    total=3,
    status_forcelist=[429, 500, 502, 503, 504],
    method_whitelist=["HEAD", "GET", "OPTIONS"]
)

adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)

# 发送请求(带超时和重试)
response = session.get('https://httpbin.org/get', timeout=5)

11.4 Web服务器开发

Python提供了多种方式来开发Web服务器,从简单的静态文件服务器到复杂的Web应用框架。

HTTP服务器基础

HTTP服务器的基本工作流程:

  1. 监听端口:绑定到指定的IP地址和端口
  2. 接受连接:等待客户端连接
  3. 解析请求:解析HTTP请求报文
  4. 处理请求:根据请求执行相应的业务逻辑
  5. 生成响应:构造HTTP响应报文
  6. 发送响应:将响应发送给客户端

http.server模块

Python标准库提供了http.server模块来快速创建HTTP服务器。

简单HTTP服务器

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简单HTTP服务器示例
"""

import http.server
import socketserver
import json
import urllib.parse
import time
from datetime import datetime

class CustomHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
    """自定义HTTP请求处理器"""

    def do_GET(self):
        """处理GET请求"""
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] GET请求: {self.path} 来自 {self.client_address[0]}")

        if self.path == '/':
            self.send_html_response(self.get_home_page())
        elif self.path == '/api/info':
            self.send_json_response(self.get_server_info())
        elif self.path.startswith('/api/time'):
            self.send_json_response(self.get_current_time())
        else:
            self.send_error(404, "页面未找到")

    def do_POST(self):
        """处理POST请求"""
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] POST请求: {self.path} 来自 {self.client_address[0]}")

        if self.path == '/api/echo':
            # 读取请求体
            content_length = int(self.headers.get('Content-Length', 0))
            post_data = self.rfile.read(content_length)

            try:
                # 尝试解析JSON
                data = json.loads(post_data.decode('utf-8'))
                response = {
                    'status': 'success',
                    'message': '数据接收成功',
                    'received_data': data,
                    'timestamp': datetime.now().isoformat()
                }
                self.send_json_response(response)
            except json.JSONDecodeError:
                # 处理表单数据
                form_data = urllib.parse.parse_qs(post_data.decode('utf-8'))
                response = {
                    'status': 'success',
                    'message': '表单数据接收成功',
                    'form_data': form_data,
                    'timestamp': datetime.now().isoformat()
                }
                self.send_json_response(response)
        else:
            self.send_error(404, "API端点未找到")

    def send_html_response(self, html_content):
        """发送HTML响应"""
        self.send_response(200)
        self.send_header('Content-Type', 'text/html; charset=utf-8')
        self.send_header('Server', 'Python-HTTP-Server/1.0')
        self.end_headers()
        self.wfile.write(html_content.encode('utf-8'))

    def send_json_response(self, data):
        """发送JSON响应"""
        json_data = json.dumps(data, ensure_ascii=False, indent=2)
        self.send_response(200)
        self.send_header('Content-Type', 'application/json; charset=utf-8')
        self.send_header('Server', 'Python-HTTP-Server/1.0')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
        self.wfile.write(json_data.encode('utf-8'))

    def get_home_page(self):
        """获取首页HTML"""
        return """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Python HTTP服务器</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; }
        .container { max-width: 800px; margin: 0 auto; }
        .api-list { background: #f5f5f5; padding: 20px; border-radius: 5px; }
        .api-item { margin: 10px 0; }
        .method { color: #007bff; font-weight: bold; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Python HTTP服务器演示</h1>
        <p>这是一个使用Python http.server模块创建的简单Web服务器。</p>

        <h2>可用的API端点:</h2>
        <div class="api-list">
            <div class="api-item">
                <span class="method">GET</span> /api/info - 获取服务器信息
            </div>
            <div class="api-item">
                <span class="method">GET</span> /api/time - 获取当前时间
            </div>
            <div class="api-item">
                <span class="method">POST</span> /api/echo - 回显提交的数据
            </div>
        </div>

        <h2>测试示例:</h2>
        <pre>
# 获取服务器信息
curl http://localhost:8000/api/info

# 提交JSON数据
curl -X POST -H "Content-Type: application/json" \
     -d '{"name":"测试","value":123}' \
     http://localhost:8000/api/echo
        </pre>
    </div>
</body>
</html>
        """

    def get_server_info(self):
        """获取服务器信息"""
        return {
            'server': 'Python HTTP Server',
            'version': '1.0',
            'timestamp': datetime.now().isoformat(),
            'client_ip': self.client_address[0],
            'user_agent': self.headers.get('User-Agent', 'Unknown')
        }

    def get_current_time(self):
        """获取当前时间"""
        now = datetime.now()
        return {
            'current_time': now.isoformat(),
            'timestamp': now.timestamp(),
            'formatted_time': now.strftime('%Y年%m月%d日 %H:%M:%S'),
            'timezone': str(now.astimezone().tzinfo)
        }

    def log_message(self, format, *args):
        """自定义日志格式"""
        pass  # 我们已经在do_GET和do_POST中记录了日志

def start_server():
    """启动HTTP服务器"""
    host = 'localhost'
    port = 8000

    with socketserver.TCPServer((host, port), CustomHTTPRequestHandler) as httpd:
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] HTTP服务器启动,监听 http://{host}:{port}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 按 Ctrl+C 停止服务器")

        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            print(f"\n[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器正在关闭...")
        finally:
            httpd.server_close()
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器已关闭")

if __name__ == '__main__':
    start_server()

运行HTTP服务器示例

启动服务器:

python simple_http_server.py

服务器输出:

[2025-08-15 21:49:08] HTTP服务器启动,监听 http://localhost:8000
[2025-08-15 21:49:08] 按 Ctrl+C 停止服务器

测试API端点:

curl http://localhost:8000/api/info

响应结果:

{
  "server": "Python HTTP Server",
  "version": "1.0",
  "timestamp": "2025-08-15T21:49:15.123456",
  "client_ip": "127.0.0.1",
  "user_agent": "curl/8.7.1"
}

服务器日志:

[2025-08-15 21:49:15] GET请求: /api/info 来自 127.0.0.1

WSGI协议

WSGI(Web Server Gateway Interface)是Python Web应用和Web服务器之间的标准接口。

WSGI应用示例

def simple_wsgi_app(environ, start_response):
    """简单的WSGI应用"""
    status = '200 OK'
    headers = [('Content-Type', 'text/html; charset=utf-8')]
    start_response(status, headers)

    html = f"""
    <html>
    <body>
        <h1>WSGI应用示例</h1>
        <p>请求方法: {environ['REQUEST_METHOD']}</p>
        <p>请求路径: {environ['PATH_INFO']}</p>
        <p>查询字符串: {environ['QUERY_STRING']}</p>
    </body>
    </html>
    """

    return [html.encode('utf-8')]

# 使用wsgiref运行WSGI应用
from wsgiref.simple_server import make_server

if __name__ == '__main__':
    server = make_server('localhost', 8080, simple_wsgi_app)
    print("WSGI服务器启动在 http://localhost:8080")
    server.serve_forever()

11.5 网络数据处理

在网络编程中,数据的序列化、压缩和加密是常见的需求。

数据序列化

JSON序列化

import json

# Python对象转JSON
data = {
    'name': '张三',
    'age': 25,
    'skills': ['Python', 'JavaScript', 'SQL'],
    'is_active': True
}

# 序列化
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(f"JSON字符串: {json_str}")

# 反序列化
parsed_data = json.loads(json_str)
print(f"解析后的数据: {parsed_data}")

pickle序列化

import pickle

# 序列化Python对象
data = {'name': '测试', 'numbers': [1, 2, 3, 4, 5]}
serialized = pickle.dumps(data)
print(f"序列化后的字节数据长度: {len(serialized)}")

# 反序列化
deserialized = pickle.loads(serialized)
print(f"反序列化后的数据: {deserialized}")

数据压缩

gzip压缩

import gzip
import json

# 原始数据
data = {'message': '这是一个测试消息' * 100}
json_data = json.dumps(data, ensure_ascii=False)

print(f"原始数据大小: {len(json_data.encode('utf-8'))} 字节")

# 压缩数据
compressed = gzip.compress(json_data.encode('utf-8'))
print(f"压缩后大小: {len(compressed)} 字节")
print(f"压缩比: {len(compressed) / len(json_data.encode('utf-8')):.2%}")

# 解压数据
decompressed = gzip.decompress(compressed).decode('utf-8')
restored_data = json.loads(decompressed)
print(f"解压后数据正确: {restored_data == data}")

数据加密

hashlib哈希

import hashlib

# 计算哈希值
data = "Python网络编程"

# MD5哈希
md5_hash = hashlib.md5(data.encode('utf-8')).hexdigest()
print(f"MD5: {md5_hash}")

# SHA256哈希
sha256_hash = hashlib.sha256(data.encode('utf-8')).hexdigest()
print(f"SHA256: {sha256_hash}")

11.6 异步网络编程

异步编程可以显著提高网络应用的性能,特别是在处理大量并发连接时。

异步编程概念

同步vs异步

  • 同步编程:代码按顺序执行,每个操作完成后才执行下一个
  • 异步编程:可以在等待I/O操作时执行其他任务

事件循环

事件循环是异步编程的核心,它负责调度和执行异步任务。

asyncio模块

Python 3.4+引入了asyncio模块,提供了异步编程的基础设施。

基本概念

import asyncio

# 协程函数
async def hello_world():
    print("Hello")
    await asyncio.sleep(1)  # 异步等待
    print("World")

# 运行协程
asyncio.run(hello_world())

异步网络编程实战

让我们创建一个完整的异步网络编程示例:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
异步网络编程示例
"""

import asyncio
import json
import time
from datetime import datetime

class AsyncTCPServer:
    """异步TCP服务器"""

    def __init__(self, host='localhost', port=9000):
        self.host = host
        self.port = port
        self.clients = set()
        self.client_count = 0

    async def handle_client(self, reader, writer):
        """处理客户端连接"""
        client_address = writer.get_extra_info('peername')
        self.client_count += 1
        client_id = self.client_count
        self.clients.add(writer)

        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_id} ({client_address}) 已连接")

        try:
            # 发送欢迎消息
            welcome_msg = f"欢迎连接到异步服务器!您是第 {client_id} 个客户端\n"
            writer.write(welcome_msg.encode('utf-8'))
            await writer.drain()

            while True:
                # 读取客户端数据
                data = await reader.read(1024)
                if not data:
                    break

                message = data.decode('utf-8').strip()
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_id} 发送: {message}")

                # 处理特殊命令
                if message.lower() == 'time':
                    response = f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
                elif message.lower() == 'clients':
                    response = f"当前连接客户端数: {len(self.clients)}\n"
                elif message.lower().startswith('broadcast:'):
                    broadcast_msg = message[10:].strip()
                    await self.broadcast_message(f"[广播] 客户端 {client_id}: {broadcast_msg}\n", exclude=writer)
                    response = "广播消息已发送\n"
                elif message.lower() == 'quit':
                    response = "再见!\n"
                    writer.write(response.encode('utf-8'))
                    await writer.drain()
                    break
                else:
                    response = f"服务器收到: {message}\n"

                # 发送响应
                writer.write(response.encode('utf-8'))
                await writer.drain()

        except asyncio.CancelledError:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_id} 连接被取消")
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 处理客户端 {client_id} 时发生错误: {e}")
        finally:
            self.clients.discard(writer)
            writer.close()
            await writer.wait_closed()
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_id} ({client_address}) 已断开连接")

    async def broadcast_message(self, message, exclude=None):
        """向所有客户端广播消息"""
        if not self.clients:
            return

        tasks = []
        for client in self.clients.copy():
            if client != exclude and not client.is_closing():
                tasks.append(self.send_to_client(client, message))

        if tasks:
            await asyncio.gather(*tasks, return_exceptions=True)

    async def send_to_client(self, writer, message):
        """向单个客户端发送消息"""
        try:
            writer.write(message.encode('utf-8'))
            await writer.drain()
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息到客户端失败: {e}")
            self.clients.discard(writer)

    async def start_server(self):
        """启动异步服务器"""
        server = await asyncio.start_server(
            self.handle_client,
            self.host,
            self.port
        )

        addr = server.sockets[0].getsockname()
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 异步TCP服务器启动,监听 {addr[0]}:{addr[1]}")
        print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 支持的命令: time, clients, broadcast:<消息>, quit")

        async with server:
            await server.serve_forever()

class AsyncTCPClient:
    """异步TCP客户端"""

    def __init__(self, host='localhost', port=9000):
        self.host = host
        self.port = port
        self.reader = None
        self.writer = None

    async def connect(self):
        """连接到服务器"""
        try:
            self.reader, self.writer = await asyncio.open_connection(self.host, self.port)
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 已连接到服务器 {self.host}:{self.port}")
            return True
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 连接服务器失败: {e}")
            return False

    async def send_message(self, message):
        """发送消息"""
        if not self.writer:
            return None

        try:
            self.writer.write(message.encode('utf-8'))
            await self.writer.drain()

            # 读取响应
            response = await self.reader.read(1024)
            return response.decode('utf-8').strip()
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息失败: {e}")
            return None

    async def listen_for_messages(self):
        """监听服务器消息"""
        try:
            while True:
                data = await self.reader.read(1024)
                if not data:
                    break
                message = data.decode('utf-8').strip()
                if message:
                    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器消息: {message}")
        except Exception as e:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 监听消息时发生错误: {e}")

    async def disconnect(self):
        """断开连接"""
        if self.writer:
            self.writer.close()
            await self.writer.wait_closed()
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 已断开连接")

async def run_server():
    """运行服务器"""
    server = AsyncTCPServer()
    try:
        await server.start_server()
    except KeyboardInterrupt:
        print(f"\n[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器正在关闭...")

async def run_single_client():
    """运行单个客户端"""
    client = AsyncTCPClient()

    if not await client.connect():
        return

    try:
        # 启动消息监听任务
        listen_task = asyncio.create_task(client.listen_for_messages())

        # 发送测试消息
        messages = [
            "Hello, Async Server!",
            "time",
            "clients",
            "broadcast:大家好,我是异步客户端!",
            "Python异步网络编程测试",
            "quit"
        ]

        for message in messages:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 发送消息: {message}")
            response = await client.send_message(message)
            if response:
                print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务器响应: {response}")
            await asyncio.sleep(1)

        # 取消监听任务
        listen_task.cancel()
        try:
            await listen_task
        except asyncio.CancelledError:
            pass

    finally:
        await client.disconnect()

async def run_multiple_clients(num_clients=3):
    """运行多个并发客户端"""
    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 启动 {num_clients} 个并发客户端")

    async def client_task(client_id):
        client = AsyncTCPClient()
        if not await client.connect():
            return

        try:
            # 发送一些测试消息
            for i in range(3):
                message = f"客户端 {client_id} 的消息 {i+1}"
                response = await client.send_message(message)
                if response:
                    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 客户端 {client_id} 收到响应: {response}")
                await asyncio.sleep(0.5)

            # 发送广播消息
            broadcast_msg = f"broadcast:来自客户端 {client_id} 的广播"
            await client.send_message(broadcast_msg)
            await asyncio.sleep(1)

            # 断开连接
            await client.send_message("quit")
        finally:
            await client.disconnect()

    # 并发运行多个客户端
    tasks = [client_task(i+1) for i in range(num_clients)]
    await asyncio.gather(*tasks)

if __name__ == '__main__':
    import sys

    if len(sys.argv) > 1:
        if sys.argv[1] == 'server':
            asyncio.run(run_server())
        elif sys.argv[1] == 'client':
            asyncio.run(run_single_client())
        elif sys.argv[1] == 'multi':
            asyncio.run(run_multiple_clients())
        else:
            print("用法: python async_server_demo.py [server|client|multi]")
    else:
        print("用法: python async_server_demo.py [server|client|multi]")
        print("  server - 启动异步服务器")
        print("  client - 启动单个客户端")
        print("  multi  - 启动多个并发客户端")

运行异步网络编程示例

启动异步服务器:

python async_server_demo.py server

在另一个终端启动多个并发客户端:

python async_server_demo.py multi

异步HTTP客户端

使用aiohttp库进行异步HTTP请求:

import aiohttp
import asyncio

async def fetch_url(session, url):
    """异步获取URL内容"""
    try:
        async with session.get(url) as response:
            return await response.text()
    except Exception as e:
        return f"错误: {e}"

async def main():
    urls = [
        'https://httpbin.org/delay/1',
        'https://httpbin.org/delay/2',
        'https://httpbin.org/delay/3'
    ]

    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)

        for i, result in enumerate(results):
            print(f"URL {i+1} 结果长度: {len(result)}")

# 运行异步HTTP客户端
asyncio.run(main())

11.7 网络安全

网络安全是网络编程中的重要主题,需要考虑各种安全威胁和防护措施。

常见网络安全威胁

1. 注入攻击

  • SQL注入:恶意SQL代码注入
  • 命令注入:系统命令注入
  • 代码注入:恶意代码注入

2. 跨站攻击

  • XSS(跨站脚本):注入恶意脚本
  • CSRF(跨站请求伪造):伪造用户请求

3. 拒绝服务攻击

  • DoS攻击:使服务不可用
  • DDoS攻击:分布式拒绝服务

安全编程实践

1. 输入验证

import re

def validate_email(email):
    """验证邮箱格式"""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None

def sanitize_input(user_input):
    """清理用户输入"""
    # 移除危险字符
    dangerous_chars = ['<', '>', '"', "'", '&', ';']
    for char in dangerous_chars:
        user_input = user_input.replace(char, '')
    return user_input.strip()

2. 密码安全

import hashlib
import secrets

def generate_salt():
    """生成随机盐值"""
    return secrets.token_hex(16)

def hash_password(password, salt):
    """哈希密码"""
    return hashlib.pbkdf2_hmac('sha256', 
                              password.encode('utf-8'), 
                              salt.encode('utf-8'), 
                              100000)

def verify_password(password, salt, hashed):
    """验证密码"""
    return hash_password(password, salt) == hashed

SSL/TLS编程

Python的ssl模块提供了SSL/TLS支持:

import ssl
import socket

# 创建SSL上下文
context = ssl.create_default_context()

# 创建安全连接
with socket.create_connection(('www.python.org', 443)) as sock:
    with context.wrap_socket(sock, server_hostname='www.python.org') as ssock:
        print(f"SSL版本: {ssock.version()}")
        print(f"加密套件: {ssock.cipher()}")

        # 发送HTTPS请求
        ssock.send(b"GET / HTTP/1.1\r\nHost: www.python.org\r\n\r\n")
        response = ssock.recv(4096)
        print(response.decode('utf-8')[:200])

认证和授权

JWT令牌示例

import jwt
import datetime

# 生成JWT令牌
def generate_token(user_id, secret_key):
    payload = {
        'user_id': user_id,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=24),
        'iat': datetime.datetime.utcnow()
    }
    return jwt.encode(payload, secret_key, algorithm='HS256')

# 验证JWT令牌
def verify_token(token, secret_key):
    try:
        payload = jwt.decode(token, secret_key, algorithms=['HS256'])
        return payload['user_id']
    except jwt.ExpiredSignatureError:
        return None  # 令牌已过期
    except jwt.InvalidTokenError:
        return None  # 无效令牌

总结

本章详细介绍了Python网络编程的各个方面:

  1. 网络编程基础:了解了TCP/IP协议栈、客户端-服务器模型和IP地址端口的概念

  2. Socket编程:掌握了TCP和UDP Socket编程的实现方法,包括服务器端和客户端的开发

  3. HTTP客户端编程:学习了使用urllib和requests库进行HTTP请求的方法

  4. Web服务器开发:了解了如何使用http.server模块创建简单的Web服务器

  5. 网络数据处理:掌握了数据序列化、压缩和加密的基本技术

  6. 异步网络编程:学习了使用asyncio进行异步网络编程,提高应用性能

  7. 网络安全:了解了常见的网络安全威胁和防护措施

通过本章的学习,你应该能够:
- 理解网络编程的基本概念和原理
- 使用Socket进行底层网络通信
- 开发HTTP客户端和服务器应用
- 处理网络数据的序列化和安全问题
- 使用异步编程提高网络应用性能
- 实施基本的网络安全措施

网络编程是一个广阔的领域,本章提供了坚实的基础。在实际项目中,你可能还需要学习特定的Web框架(如Django、Flask)、消息队列、微服务架构等更高级的主题。记住,网络编程的关键是理解协议、处理并发和确保安全性。

小夜