下面我将分步进行,从最基础的版本开始,逐步完善,最后给出一个功能更完整的版本。

核心概念回顾
- HTTP (HyperText Transfer Protocol): 应用层协议,规定了客户端(浏览器)和服务器之间如何通信,一个简单的 HTTP 请求包含三部分:请求行、请求头、请求体(GET 请求通常没有请求体),一个简单的 HTTP 响应也包含三部分:状态行、响应头、响应体。
- TCP (Transmission Control Protocol): 传输层协议,提供可靠的、面向连接的数据传输,HTTP 通常运行在 TCP 之上。
- Socket: 是操作系统提供的一个 API,是网络编程的“终端”,它允许程序发送和接收数据,我们可以使用
socket模块来创建 TCP 套接字,监听端口,接受客户端连接,并进行数据收发。
创建最基础的 TCP 服务器
这个服务器能接收连接,但还不是一个 HTTP 服务器,因为它不解析 HTTP 协议。
# server_v0.py
import socket
# 1. 创建一个 TCP socket 对象
# AF_INET 表示使用 IPv4 地址
# SOCK_STREAM 表示使用 TCP 协议
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 设置 socket 选项,避免 "Address already in use" 错误
# SOL_SOCKET 是套接字级别
# SO_REUSEADDR 是重用地址选项
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定 IP 地址和端口号
# '' 表示监听所有可用的网络接口
# 7890 是我们选择的端口号
server_socket.bind(('', 7890))
# 4. 开始监听 incoming connections
# 5 是连接队列的最大长度
server_socket.listen(5)
print("Server is listening on port 7890...")
# 5. 进入主循环,等待并处理客户端连接
while True:
# accept() 会阻塞,直到有新的客户端连接
# 它返回一个新的 socket 对象 (client_socket) 和客户端的地址 (client_address)
client_socket, client_address = server_socket.accept()
print(f"Connection from {client_address} has been established!")
# 从客户端接收数据
# 1024 是缓冲区大小
request_data = client_socket.recv(1024)
print(f"Received:\n{request_data.decode('utf-8')}")
# 6. 关闭与客户端的连接
client_socket.close()
运行这个服务器,然后在浏览器中访问 http://localhost:7890,你会在服务器终端看到浏览器发送的原始 TCP 数据,这些数据就是 HTTP 请求。
实现一个能响应 HTTP 请求的简单服务器
我们让服务器能够解析收到的 HTTP 请求,并返回一个符合 HTTP 协格式的响应。
一个简单的 HTTP 响应结构如下:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 43
<html><body><h1>Hello, World!</h1></body></html>
- 状态行:
HTTP/1.1 200 OK(协议版本 状态码 状态描述) - 响应头:
Content-Type,Content-Length等,用空行\r\n\r\n与响应体分隔。 - 响应体: 实际返回的 HTML 内容。
# server_v1.py
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 7890))
server_socket.listen(5)
print("Server is listening on port 7890...")
while True:
client_socket, client_address = server_socket.accept()
# 接收请求数据
request = client_socket.recv(1024).decode('utf-8')
print(f"Received request:\n{request}")
# 准备响应
# 状态行
status_line = "HTTP/1.1 200 OK\r\n"
# 响应头
headers = "Content-Type: text/html\r\n"
# 响应体
body = "<html><body><h1>Hello from a custom HTTP server!</h1></body></html>"
# 计算响应体的长度,并添加到 Content-Length 头中
headers += f"Content-Length: {len(body)}\r\n"
# 空行,用于分隔响应头和响应体
blank_line = "\r\n"
# 组合成完整的响应
response = status_line + headers + blank_line + body
# 发送响应
client_socket.sendall(response.encode('utf-8'))
# 关闭连接
client_socket.close()
运行这个版本,访问 http://localhost:7890,浏览器会正确显示 "Hello from a custom HTTP server!"。
实现更完整的功能(解析路径、返回404)
一个真正的服务器需要能够根据请求的路径返回不同的内容,如果请求的路径不存在,应该返回 404 Not Found 错误。
# server_v2.py
import socket
# 定义一个简单的路由映射
ROUTES = {
"/": "<html><body><h1>Home Page</h1><p>Welcome to the server!</p></body></html>",
"/about": "<html><body><h1>About Us</h1><p>This is a simple HTTP server.</p></body></html>"
}
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 7890))
server_socket.listen(5)
print("Server is listening on port 7890...")
while True:
client_socket, client_address = server_socket.accept()
request_data = client_socket.recv(1024).decode('utf-8')
print(f"Received request from {client_address}:\n{request_data}")
# 解析请求行,获取路径
# 请求行格式: "GET /path HTTP/1.1"
request_line = request_data.split('\r\n')[0]
method, path, protocol = request_line.split(' ')
print(f"Requested path: {path}")
# 根据路径查找对应的响应体
if path in ROUTES:
body = ROUTES[path]
status_code = "200 OK"
else:
body = "<html><body><h1>404 Not Found</h1><p>The requested page was not found.</p></body></html>"
status_code = "404 Not Found"
# 构建响应
status_line = f"HTTP/1.1 {status_code}\r\n"
headers = "Content-Type: text/html\r\n"
headers += f"Content-Length: {len(body)}\r\n"
blank_line = "\r\n"
response = status_line + headers + blank_line + body
client_socket.sendall(response.encode('utf-8'))
client_socket.close()
运行这个版本:
- 访问
http://localhost:7890/,会看到 "Home Page"。 - 访问
http://localhost:7890/about,会看到 "About Us"。 - 访问任何其他路径(如
http://localhost:7890/contact),会看到 "404 Not Found"。
最终版本:多线程处理并发
之前的版本都是单线程的,一次只能处理一个客户端请求,当处理慢请求时,其他客户端必须等待,我们可以使用 threading 模块来为每个客户端连接创建一个新线程,实现简单的并发处理。

# server_final.py
import socket
import threading
# 定义一个简单的路由映射
ROUTES = {
"/": "<html><body><h1>Home Page</h1><p>Welcome to the server!</p></body></html>",
"/about": "<html><body><h1>About Us</h1><p>This is a simple HTTP server.</p></body></html>"
}
# 创建一个函数来处理单个客户端请求
def handle_client_connection(client_socket, client_address):
print(f"[NEW CONNECTION] {client_address} connected.")
try:
# 接收请求数据
request_data = client_socket.recv(1024).decode('utf-8')
if not request_data:
return
print(f"Received request from {client_address}:\n{request_data}")
# 解析请求行,获取路径
request_line = request_data.split('\r\n')[0]
method, path, protocol = request_line.split(' ')
print(f"Requested path: {path}")
# 根据路径查找对应的响应体
if path in ROUTES:
body = ROUTES[path]
status_code = "200 OK"
else:
body = "<html><body><h1>404 Not Found</h1><p>The requested page was not found.</p></body></html>"
status_code = "404 Not Found"
# 构建响应
status_line = f"HTTP/1.1 {status_code}\r\n"
headers = "Content-Type: text/html\r\n"
headers += f"Content-Length: {len(body)}\r\n"
blank_line = "\r\n"
response = status_line + headers + blank_line + body
client_socket.sendall(response.encode('utf-8'))
except Exception as e:
print(f"[ERROR] handling client {client_address}: {e}")
finally:
# 确保连接被关闭
client_socket.close()
print(f"[CONNECTION CLOSED] {client_address} disconnected.")
# 主服务器逻辑
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 7890))
server_socket.listen(5)
print("Server is listening on port 7890...")
while True:
client_socket, client_address = server_socket.accept()
# 为每个新连接创建一个新线程
client_thread = threading.Thread(
target=handle_client_connection,
args=(client_socket, client_address)
)
client_thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")
if __name__ == "__main__":
start_server()
这个最终版本的特点:
- 并发处理:主线程负责监听和接受连接,每个客户端连接的处理逻辑被交给一个新的线程,这使得服务器可以同时为多个客户端服务。
- 代码结构化:将处理请求的逻辑封装在
handle_client_connection函数中,使主循环更清晰。 - 错误处理:增加了
try...except...finally块,确保即使发生错误,客户端连接也能被正确关闭。 - 状态追踪:打印出当前活跃的连接数。
总结与局限性
通过以上步骤,我们用 Python 的 socket 模块构建了一个功能相对完整的 HTTP 服务器,这个过程非常有价值,因为它让你理解了 HTTP 协议的底层细节。
这个服务器只是一个教学示例,它存在很多局限性,不适合用于生产环境:
- 性能:虽然使用了多线程,但在高并发下,创建和销毁线程的开销很大,性能远不如使用 I/O 多路复用(如
select,epoll)的成熟服务器框架(如 Nginx)。 - 功能缺失:它不支持静态文件服务、POST 请求、请求体解析、Cookie、会话管理、HTTPS 等现代 Web 服务器必备的功能。
- 安全性:没有进行任何输入验证,容易受到各种网络攻击。
- 可扩展性:代码结构简单,难以扩展和维护。
在实际开发中,我们通常会使用成熟的 Web 框架,
- Python: Flask, Django, FastAPI
- Node.js: Express, Koa
- Java: Spring Boot
- Go: Gin, Echo
这些框架已经帮我们处理了底层的 socket 通信、协议解析、并发控制等复杂问题,让我们可以专注于业务逻辑的开发。
