- HTTP 协议基础:理解我们正在构建什么。
- 从零开始:构建最简单的服务器:仅返回 "Hello, World"。
- 解析请求:读取客户端发来的 HTTP 请求头和体。
- 构建响应:生成符合 HTTP 规范的响应头和体。
- 处理不同路径和请求方法:实现路由功能。
- 处理静态文件:让服务器能返回网页、图片等文件。
- 使用 Web 框架:介绍更专业的工具(如 Flask)。
- 总结与进阶。
HTTP 协议基础:我们正在构建什么?
在写代码之前,我们必须理解 HTTP(HyperText Transfer Protocol,超文本传输协议)是 Web 通信的基石,它定义了客户端(如浏览器)和服务器之间如何请求和响应数据。

一个 HTTP 交互的基本流程如下:
-
客户端请求:浏览器向服务器发送一个请求,一个请求包含:
- 请求行:
METHOD /path HTTP/1.1METHOD:请求方法,如GET(获取资源)、POST(提交数据)、PUT(更新资源)、DELETE(删除资源)等。/path:请求的路径,如/index.html、/api/user。HTTP/1.1:使用的 HTTP 协议版本。
- 请求头:
Key: Value对,提供关于请求的附加信息,如Host: example.com,User-Agent: Chrome/90.0。 - 请求体:对于
POST或PUT等请求,可能会包含发送给服务器的数据(如表单数据、JSON)。
- 请求行:
-
服务器响应:服务器处理请求后,返回一个响应,一个响应包含:
- 状态行:
HTTP/1.1 200 OKHTTP/1.1:HTTP 协议版本。200:状态码,表示请求成功,常见的还有404(未找到)、500(服务器内部错误)。OK:状态码的描述性文本。
- 响应头:
Key: Value对,提供关于响应的附加信息,如Content-Type: text/html类型是 HTML)、Content-Length: 1234长度)。 - 响应体:实际返回给客户端的数据,如 HTML 文件、JSON 数据、图片等。
- 状态行:
我们的目标:编写一个程序,监听一个网络端口,接收客户端的 HTTP 请求,解析它,然后根据请求内容生成一个符合 HTTP 规范的 HTTP 响应,最后发回给客户端。

从零开始:构建最简单的服务器
我们将使用 Python 内置的 socket 库来实现。socket 是进行网络通信的基础。
代码:simple_server.py
import socket
# 1. 创建一个 socket 对象
# AF_INET 表示使用 IPv4 协议
# SOCK_STREAM 表示使用 TCP 协议
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定 IP 地址和端口号
# '' 表示监听本机所有可用的网络接口
# 7890 是我们选择的端口号(1024 以上的端口通常需要管理员权限)
server_address = ('', 7890)
server_socket.bind(server_address)
# 3. 开始监听,允许的最大连接数为 5
server_socket.listen(5)
print("服务器已启动,监听端口 7890...")
# 4. 进入主循环,等待客户端连接
while True:
# accept() 会阻塞程序,直到有客户端连接
# 它返回一个新的 socket 对象 (client_socket) 和客户端的地址 (client_address)
client_socket, client_address = server_socket.accept()
print(f"来自 {client_address} 的连接已建立。")
# 5. 接收客户端发送的数据
# recv(1024) 表示最多接收 1024 字节的数据
request_data = client_socket.recv(1024)
print("收到的请求数据:")
print(request_data.decode('utf-8')) # 将字节流解码为字符串
# 6. 构造 HTTP 响应
response_body = "Hello, World from my simple server!"
response = (
"HTTP/1.1 200 OK\r\n" # 状态行
"Content-Type: text/plain\r\n" # 响应头
"Content-Length: {}\r\n".format(len(response_body)) # 响应头
"\r\n" # 空行,分隔响应头和响应体
response_body # 响应体
)
# 7. 发送响应
client_socket.sendall(response.encode('utf-8')) # 将字符串编码为字节流
# 8. 关闭客户端连接
client_socket.close()
print(f"与 {client_address} 的连接已关闭,\n")
如何运行和测试
- 保存代码:将上面的代码保存为
simple_server.py。 - 运行服务器:在终端中执行
python simple_server.py,你会看到 "服务器已启动,监听端口 7890..."。 - 测试:打开你的网页浏览器(如 Chrome、Firefox),在地址栏输入
http://localhost:7890然后回车。
会发生什么?
- 浏览器会向你的服务器发送一个
GET请求。 - 你的服务器脚本会接收到这个请求,打印出原始的 HTTP 请求信息,然后构造一个 "Hello, World" 的响应并发送给浏览器。
- 浏览器接收到响应后,会在页面上显示 "Hello, World from my simple server!"。
解析请求
在上面的例子中,我们只是简单地将 request_data 打印出来,一个真实的请求格式如下:
GET /hello HTTP/1.1
Host: localhost:7890
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
我们需要解析这个字符串,提取出 请求方法、请求路径 和 请求头,我们可以手动用字符串分割,但对于更复杂的请求(如带 body 的 POST 请求),这会很麻烦,在实际项目中,我们通常会使用专门的库,但为了学习,我们手动解析一下。

代码:parsing_requests.py
# ... (前面的 socket 代码与 simple_server.py 相同) ...
while True:
client_socket, client_address = server_socket.accept()
request_data = client_socket.recv(1024).decode('utf-8')
print("--- 收到新请求 ---")
print(request_data)
# 解析请求
request_lines = request_data.split('\r\n')
request_line = request_lines[0]
method, path, version = request_line.split(' ')
headers = {}
for line in request_lines[1:]:
if line.strip() == '': # 空行表示头部结束
break
key, value = line.split(': ', 1)
headers[key] = value
print(f"解析结果: Method={method}, Path={path}, Version={version}")
print(f"Headers: {headers}")
# ... (构造和发送响应的代码) ...
构建响应
响应的格式比请求更简单,我们已经在上面的例子中见过,关键是:
- 状态行:必须包含协议版本、状态码和原因短语。
- 响应头:
Key: Value格式,以\r\n分隔。 - 空行:
\r\n\r\n,非常重要,它标志着响应头的结束。 - 响应体:实际的数据。
状态码的意义:
200 OK: 成功。404 Not Found: 请求的资源不存在。500 Internal Server Error: 服务器内部错误。
代码:building_responses.py
# ... (解析请求的代码) ...
# 根据路径返回不同的响应
if path == '/':
response_body = "<h1>欢迎访问首页!</h1>"
elif path == '/about':
response_body = "<h1>关于我们</h1><p>这是一个简单的 HTTP 服务器教程。</p>"
else:
response_body = "<h1>404 Not Found</h1>"
status_line = "HTTP/1.1 404 Not Found"
# 构造响应
status_line = "HTTP/1.1 200 OK" # 默认是 200
if response_body.startswith("<h1>404"):
status_line = "HTTP/1.1 404 Not Found"
response = (
status_line + "\r\n"
"Content-Type: text/html\r\n"
"Content-Length: {}\r\n".format(len(response_body))
"\r\n"
response_body
)
client_socket.sendall(response.encode('utf-8'))
client_socket.close()
处理不同路径和请求方法:实现路由
我们的服务器可以根据不同的 URL 路径返回不同的内容了,这就是 路由 的雏形,我们还可以根据不同的 HTTP 方法(GET vs POST)执行不同的操作。
代码:routing_server.py
# ... (socket 初始化代码) ...
def handle_request(method, path, headers):
"""处理请求并返回响应体"""
print(f"处理请求: {method} {path}")
if method == 'GET':
if path == '/':
return "<h1>首页 - GET 请求</h1>"
elif path == '/api/info':
# 返回 JSON 数据
import json
data = {"name": "My Server", "version": "1.0"}
return json.dumps(data)
else:
return None # 404
elif method == 'POST':
if path == '/api/data':
# 假设请求体里有数据
# 注意:这里我们没有实际读取请求体,仅作示例
return "<h1>数据已接收 - POST 请求</h1>"
else:
return None # 404
return None # 404
# ... (主循环代码) ...
while True:
client_socket, client_address = server_socket.accept()
request_data = client_socket.recv(1024).decode('utf-8')
if not request_data:
continue
# 解析请求
request_lines = request_data.split('\r\n')
request_line = request_lines[0]
method, path, version = request_line.split(' ')
response_body = handle_request(method, path, {})
# 构造响应
if response_body is None:
status_line = "HTTP/1.1 404 Not Found"
response_body = "<h1>404 Not Found</h1>"
else:
status_line = "HTTP/1.1 200 OK"
# 检查是否是 JSON
if path == '/api/info':
headers = "Content-Type: application/json\r\n"
else:
headers = "Content-Type: text/html\r\n"
response = (
status_line + "\r\n"
headers
"Content-Length: {}\r\n".format(len(response_body))
"\r\n"
response_body
)
client_socket.sendall(response.encode('utf-8'))
client_socket.close()
处理静态文件
一个 Web 服务器最重要的功能之一就是提供静态文件(如 HTML, CSS, JS, 图片),我们需要:
- 将文件路径映射到服务器文件系统路径。
- 读取文件内容。
- 根据文件扩展名设置正确的
Content-Type响应头。 - 处理文件不存在的情况(
404)。
代码:static_file_server.py
import socket
import os
# ... (socket 初始化代码) ...
# 文件扩展名到 Content-Type 的映射
CONTENT_TYPES = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.gif': 'image/gif',
}
def get_content_type(filepath):
"""根据文件路径获取 Content-Type"""
_, ext = os.path.splitext(filepath)
return CONTENT_TYPES.get(ext, 'application/octet-stream') # 默认为二进制流
def serve_static_file(path):
"""尝试提供静态文件"""
# 安全检查:防止路径遍历攻击 (e.g., /../../../../etc/passwd)
if '..' in path or path.startswith('/'):
return None
# 将请求路径映射到文件系统路径
# 假设所有静态文件都在 'static' 目录下
file_path = os.path.join('static', path.lstrip('/'))
if not os.path.exists(file_path) or not os.path.isfile(file_path):
return None # 404
try:
with open(file_path, 'rb') as f:
content = f.read()
return content
except IOError:
return None # 500
# ... (主循环代码) ...
while True:
client_socket, client_address = server_socket.accept()
request_data = client_socket.recv(1024).decode('utf-8')
if not request_data:
client_socket.close()
continue
# 解析请求
request_lines = request_data.split('\r\n')
request_line = request_lines[0]
method, path, version = request_line.split(' ')
# 尝试提供静态文件
response_body = serve_static_file(path)
if response_body is not None:
status_line = "HTTP/1.1 200 OK"
content_type = get_content_type(path)
headers = f"Content-Type: {content_type}\r\n"
# 注意:文件内容是字节,所以长度要用 len() 直接获取
response = (
status_line + "\r\n"
headers
"Content-Length: {}\r\n".format(len(response_body))
"\r\n"
).encode('utf-8') + response_body
else:
# 如果文件不存在,返回 404
status_line = "HTTP/1.1 404 Not Found"
response_body = "<h1>404 Not Found</h1>".encode('utf-8')
response = (
status_line + "\r\n"
"Content-Type: text/html\r\n"
"Content-Length: {}\r\n".format(len(response_body))
"\r\n"
).encode('utf-8') + response_body
client_socket.sendall(response)
client_socket.close()
如何测试这个服务器:
- 创建一个名为
static的文件夹。 - 在
static文件夹里放一个index.html文件和一些图片。 - 运行
python static_file_server.py。 - 在浏览器中访问
http://localhost:7890/index.html,你应该能看到你的网页。
使用 Web 框架
虽然我们手动构建服务器非常有助于理解底层原理,但在实际开发中,我们几乎不会从零开始写,因为:
- 效率低:代码量大,容易出错。
- 功能不完善:缺少路由、模板引擎、数据库连接、安全性等现成的解决方案。
- 社区支持少:没有丰富的第三方库。
这时,Web 框架就派上用场了,它们提供了构建 Web 应用所需的一切基础设施。
最流行的 Python Web 框架是 Flask 和 Django。
使用 Flask 重写我们的路由服务器
Flask 的代码极其简洁。
安装 Flask:
pip install Flask
代码:app.py
from flask import Flask, jsonify
# 创建一个 Flask 应用实例
app = Flask(__name__)
# @app.route 装饰器用于定义路由
# 它将 URL 路径映射到一个 Python 函数
@app.route('/')
def home():
"""处理首页的 GET 请求"""
return "<h1>首页 - Flask 服务器</h1>"
@app.route('/api/info')
def api_info():
"""处理 /api/info 的 GET 请求,返回 JSON"""
data = {"name": "My Flask Server", "version": "1.0"}
# Flask 的 jsonify 函数会自动将字典转换为 JSON 并设置正确的 Content-Type
return jsonify(data)
@app.route('/api/data', methods=['POST'])
def api_data():
"""处理 /api/data 的 POST 请求"""
# 在 Flask 中,你可以通过 request 对象访问请求数据
from flask import request
# 获取 JSON 数据: data = request.get_json()
# 或者获取表单数据: name = request.form.get('name')
return "<h1>数据已接收 - Flask POST 请求</h1>"
# 运行服务器
# debug=True 开启调试模式,代码修改后会自动重启
if __name__ == '__main__':
app.run(port=7890, debug=True)
运行 python app.py,然后访问 http://localhost:7890 或 http://localhost:7890/api/info,你会发现功能和我们之前手动实现的完全一样,但代码量却大大减少,而且更清晰、更易于维护。
总结与进阶
本教程带你从最底层的 socket 开始,一步步构建了一个功能从简到繁的 HTTP 服务器,你学到了:
- HTTP 请求和响应的基本结构。
- 如何使用 Python 的
socket库进行网络通信。 - 如何解析请求和构造响应。
- 实现了基本的路由和静态文件服务。
- 理解了为什么在实际项目中要使用 Web 框架(如 Flask)。
进阶方向
如果你对构建自己的 Web 服务器感兴趣,可以继续探索:
- 多线程/异步 I/O:我们上面的服务器是同步的,一次只能处理一个客户端请求,当有大量请求时,它会变得非常慢,你可以研究
threading模块为每个请求创建一个线程,或者使用asyncio和aiohttp实现更高效的异步服务器。 - WSGI:Web 服务器网关接口,这是 Python Web 应用和 Web 服务器之间的标准接口,像 Gunicorn、uWSGI 这样的服务器就是 WSGI 服务器,它们可以运行你的 Flask/Django 应用。
- 安全性:学习如何防止常见 Web 攻击,如 SQL 注入、跨站脚本、跨站请求伪造 等。
- 性能优化:学习缓存、负载均衡等技术,让你的服务器能应对高并发场景。
希望这份教程能帮助你打下坚实的基础!
