基于Java Socket网络编程实现的多线程HTTP服务器,支持GET、POST和HEAD请求方法。
本项目实现了一个简易但功能完整的HTTP服务器,采用面向对象设计,使用Java Socket网络编程和多线程技术,能够响应浏览器的HTTP请求并返回静态网页文件。
- 多线程处理:使用线程池管理客户端连接,支持多个客户端同时访问
- HTTP方法支持:支持GET、POST、HEAD三种HTTP请求方法
- 静态文件服务:支持HTML、CSS、JavaScript、图片等多种文件类型
- 错误处理:支持400、403、404、405、500等HTTP状态码
- 安全防护:防止目录遍历攻击(检测
..和~) - 日志记录:记录服务器运行日志和访问日志
- URL解码:支持URL编码(如中文文件名)
| 技术 | 说明 |
|---|---|
| Java Socket | 网络编程,监听端口、接受连接 |
| 多线程 | ExecutorService线程池,支持并发处理 |
| 面向对象 | 类封装、职责分离、工厂模式 |
| HTTP协议 | HTTP/1.1协议的基本实现 |
┌─────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ 浏览器 │────▶│ HTTP服务器 │────▶│ 静态文件 │
│ (客户端) │◀────│ (服务器端) │◀────│ (webroot/) │
└─────────────┘ └─────────────────┘ └──────────────────┘
| 类名 | 职责 | 行数 |
|---|---|---|
HttpServer |
服务器主类,负责启动监听、接受连接、线程池管理 | 78行 |
ClientHandler |
客户端处理器,实现Runnable接口,处理单个客户端请求 | 203行 |
HttpRequest |
HTTP请求解析器,解析请求行、请求头、请求体 | 204行 |
HttpResponse |
HTTP响应构建器,构建状态行、响应头、响应体 | 160行 |
Logger |
日志工具类,记录服务器运行日志和访问日志 | 47行 |
总代码行数:692行
客户端发起请求
│
▼
ServerSocket.accept() 接受连接
│
▼
提交到线程池(ExecutorService)
│
▼
ClientHandler.run() 处理请求
│
├── HttpRequest.parse() 解析请求
│ ├── 解析请求行(方法、URL、协议版本)
│ ├── 解析请求头(Content-Length、Host等)
│ └── 解析请求体(POST数据)
│
├── 根据请求方法分发处理
│ ├── GET → handleGet() → 返回文件内容
│ ├── POST → handlePost() → 返回POST数据
│ └── HEAD → handleHead() → 返回响应头
│
├── HttpResponse.send() 发送响应
│ ├── 构建状态行(HTTP/1.1 200 OK)
│ ├── 构建响应头(Content-Type、Content-Length)
│ └── 发送响应体(HTML内容)
│
└── 关闭连接
根据客户端请求的URL,从webroot/目录下查找并返回对应的静态文件。
支持的文件类型:
| 文件类型 | Content-Type |
|---|---|
| .html / .htm | text/html |
| .css | text/css |
| .js | application/javascript |
| .jpg / .jpeg | image/jpeg |
| .png | image/png |
| .gif | image/gif |
| 其他 | application/octet-stream |
示例:
# 访问首页
curl http://localhost:8080/
# 访问指定页面
curl http://localhost:8080/index.html接收客户端发送的POST数据,并返回收到的数据。
示例:
# 发送POST请求
curl -X POST -d "name=张三&message=Hello" http://localhost:8080/submit
# 返回结果
<html><body><h1>POST数据已收到</h1><p>name=张三&message=Hello</p></body></html>与GET请求类似,但只返回响应头,不返回响应体。常用于检查资源是否存在。
示例:
curl -I http://localhost:8080/返回结果:
HTTP/1.1 200 OK
Server: SimpleHTTPServer/1.0
Connection: close
Content-Length: 8948
Date: Thu Jun 26 02:49:11 CST 2026
Content-Type: text/html
| 状态码 | 说明 | 触发条件 |
|---|---|---|
| 200 OK | 请求成功 | 文件存在或POST处理成功 |
| 400 Bad Request | 请求格式错误 | 无法解析HTTP请求 |
| 403 Forbidden | 拒绝访问 | 请求包含..或~(目录遍历) |
| 404 Not Found | 资源不存在 | 请求的文件不存在 |
| 405 Method Not Allowed | 方法不允许 | 使用了不支持的HTTP方法 |
| 500 Internal Server Error | 服务器内部错误 | 服务器处理异常 |
- 目录遍历防护:检测URL中的
..和~,防止访问webroot目录之外的文件 - 路径规范化:使用
getCanonicalPath()获取文件真实路径,确保文件在webroot目录内
服务器会记录以下日志:
[2026-06-26 02:25:14] [INFO] ========================================
[2026-06-26 02:25:14] [INFO] 简易HTTP服务器启动成功
[2026-06-26 02:25:14] [INFO] 监听端口: 8080
[2026-06-26 02:25:14] [INFO] 访问地址: http://localhost:8080/
[2026-06-26 02:25:14] [INFO] ========================================
[2026-06-26 02:48:46] [INFO] 新连接: /127.0.0.1:54312
[2026-06-26 02:48:46] /127.0.0.1:54312 POST /submit 200
- Java JDK:8 或更高版本(已测试 JDK 17)
- 操作系统:Windows / Linux / macOS
- 磁盘空间:约 10MB
# 方式一:使用编译脚本
chmod +x build.sh
./build.sh
# 方式二:手动编译
mkdir -p out
javac -d out src/main/java/com/httpserver/*.java# 使用默认端口8080
java -cp out com.httpserver.HttpServer
# 指定端口
java -cp out com.httpserver.HttpServer 9090在浏览器中访问:
# 方式一:Ctrl+C(前台运行时)
# 方式二:kill进程
pkill -f 'com.httpserver.HttpServer'# 测试首页访问
curl -s http://localhost:8080/
# 测试响应头
curl -sI http://localhost:8080/
# 测试指定文件
curl -s http://localhost:8080/index.html# 发送POST数据
curl -s -X POST -d "name=张三&age=20" http://localhost:8080/submit
# 发送JSON数据
curl -s -X POST -H "Content-Type: application/json" -d '{"key":"value"}' http://localhost:8080/submit# 测试404错误(访问不存在的文件)
curl -s http://localhost:8080/nonexistent.html
# 测试目录遍历防护
curl -s http://localhost:8080/../etc/passwdimport urllib.request
import urllib.parse
# 测试GET请求
response = urllib.request.urlopen('http://localhost:8080/')
print('GET Status:', response.status)
# 测试POST请求
data = urllib.parse.urlencode({'name': '张三', 'message': 'Hello'}).encode()
req = urllib.request.Request('http://localhost:8080/submit', data=data, method='POST')
with urllib.request.urlopen(req) as response:
print('POST Status:', response.status)
print('Body:', response.read().decode())=== 测试GET请求 ===
状态码: 200
响应大小: 8948 字节
=== 测试POST请求 ===
<html><body><h1>POST数据已收到</h1><p>name=张三&message=Hello</p></body></html>
=== 测试HEAD请求 ===
HTTP/1.1 200 OK
Server: SimpleHTTPServer/1.0
Connection: close
Content-Length: 0
=== 测试404错误 ===
状态码: 404
java/
├── src/
│ └── main/
│ └── java/
│ └── com/
│ └── httpserver/
│ ├── HttpServer.java # 服务器主类(78行)
│ ├── ClientHandler.java # 客户端处理器(203行)
│ ├── HttpRequest.java # HTTP请求解析器(204行)
│ ├── HttpResponse.java # HTTP响应构建器(160行)
│ └── Logger.java # 日志工具类(47行)
├── webroot/ # 静态文件目录
│ └── index.html # 默认首页
├── images/ # 图表目录
│ ├── class_diagram.mmd # 类图(mermaid格式)
│ ├── request_flow.mmd # 请求流程图(mermaid格式)
│ └── system_architecture.mmd # 系统架构图(mermaid格式)
├── out/ # 编译输出目录
├── build.sh # 编译脚本
├── package.sh # 打包脚本
├── test.sh # 测试脚本
├── MANIFEST.MF # JAR打包配置
├── 刘龙灿-报告.md # 刘龙灿的课程设计报告
├── 周宇聪-报告.md # 周宇聪的课程设计报告
└── README.md # 本文件
| 成员 | 负责模块 | 主要工作 |
|---|---|---|
| 刘龙灿 | 协议处理模块 | HTTP请求解析、HTTP响应构建、服务器主框架、协议处理逻辑 |
| 周宇聪 | 业务处理模块 | 客户端请求处理、静态文件服务、错误处理、测试页面设计 |
- HTTP协议规范:https://tools.ietf.org/html/rfc2616
- Java Socket编程:https://docs.oracle.com/javase/tutorial/networking/sockets/
- Java多线程编程:https://docs.oracle.com/javase/tutorial/essential/concurrency/
- Java实现HTTP服务器参考:http://blog.csdn.net/earbao/article/details/16354881
- HTTP协议简介:http://zsxxsz.iteye.com/blog/568250
- HTTP协议详解:http://www.cnblogs.com/zhijianliutang/archive/2012/09/23/2698860.html
# 查看端口占用
lsof -i:8080
# 使用其他端口
java -cp out com.httpserver.HttpServer 9090确保HTML文件使用UTF-8编码保存,并在HTML头部指定:
<meta charset="UTF-8">在ClientHandler.java的getContentType()方法中添加:
} else if (url.endsWith(".pdf")) {
return "application/pdf";
}在HttpServer.java中修改:
this.threadPool = Executors.newFixedThreadPool(20); // 改为20本项目仅用于课程设计学习,不用于商业用途。