HTTP 快速解析
一、HTTP请求结构
HTTP请求和响应报文由以下部分组成(以请求报文为例):
-
请求报文结构:
- 请求行:包含HTTP方法(如GET/POST)、请求URL和协议版本(如HTTP/1.1,HTTP/2.0,HTTP/3.0),格式为
方法 URL 协议版本
。 - 请求头(Headers):包含元数据如
Host
(目标域名)、Content-Length
(报文主体长度)、Accept
(客户端支持的数据格式)等。 - 空行:用于分隔请求头和报文主体。
- 报文主体:通常为请求参数(GET方法无主体)或JSON/表单数据(POST/PUT方法)。
- 请求行:包含HTTP方法(如GET/POST)、请求URL和协议版本(如HTTP/1.1,HTTP/2.0,HTTP/3.0),格式为
-
响应报文结构:状态行(含状态码)、响应头、空行、响应主体。
二、常用HTTP状态码分类
1. 1xx(信息性状态码)
- 100 Continue:服务器已接收请求头,客户端可继续发送请求体。
- 101 Switching Protocols:协议切换(如WebSocket)。
2. 2xx(成功状态码)
- 200 OK:请求成功,返回完整数据。
- 204 No Content:请求成功但无返回数据(如DELETE操作)。
- 206 Partial Content:返回部分资源(如断点续传)。
3. 3xx(重定向状态码)
- 301 Moved Permanently:资源永久迁移,搜索引擎需更新URL。
- 304 Not Modified:客户端缓存有效,服务器不返回新内容。
- 307 Temporary Redirect:临时重定向,保留原始请求方法(如POST)。
4. 4xx(客户端错误)
- 400 Bad Request:请求语法错误。
- 403 Forbidden:服务器拒绝请求(权限不足)。
- 404 Not Found:资源不存在。
5. 5xx(服务器错误)
- 500 Internal Server Error:服务器内部错误。
- 503 Service Unavailable:服务器过载或维护。
三、状态码使用场景
1. 206 Partial Content
- 场景:客户端通过
Range
头请求资源的某部分(如大文件分块下载或视频流播放)。 - 示例:
服务器返回GET /large-file.mp4 HTTP/1.1 Range: bytes=0-999
206 Partial Content
和对应数据块。
2. 304 Not Modified
- 场景:客户端携带
If-Modified-Since
(最后修改时间)或If-None-Match
(ETag)请求资源,服务器验证后确认未修改。 - 流程:
- 客户端首次请求,服务器返回资源及
Last-Modified
或ETag
。 - 客户端再次请求时携带上述字段,服务器返回
304
,客户端使用本地缓存。
- 客户端首次请求,服务器返回资源及
小结
206 用于分块传输,304 用于缓存优化,两者均减少网络传输量。
四、HTTP缓存机制
1. 强制缓存
- 原理:客户端直接使用本地缓存,无需请求服务器。
- 控制字段:
Cache-Control: max-age=3600
(缓存3600秒)。Expire: Wed, 20 Jan 2026 12:00:00 GMT
(过期时间)。
- Cache-Control 的优先级高于 Expires
2. 协商缓存
- 原理:客户端与服务器协商是否使用缓存。
- 实现方式:
- Last-Modified:客户端发送
If-Modified-Since
,服务器比对时间。 - ETag:客户端发送
If-None-Match
,服务器校验资源哈希值。
- Last-Modified:客户端发送
- 优先级:ETag > Last-Modified(更精确)。
Last-Modified
基于时间实现,ETag
是基于唯一标识实现,故而ETag
可以避免由于时间篡改导致的不可信问题。 - 304 Not Modified:客户端缓存有效,服务器不返回新内容。
- 使用时与强制缓存配合使用,只有强制缓存失效才会使用协商缓存。
3. 缓存失效
- 主动失效:服务器通过
Cache-Control: no-cache
或Cache-Control: no-store
强制客户端重新请求。 - 被动失效:资源更新后,客户端下次请求会触发重新校验。
小结
HTTP缓存通过强制和协商机制提升性能,开发者需根据场景选择合适策略(如静态资源用强制缓存,动态内容用协商缓存)。
五、应用
基于 Java 原生 HttpURLConnection
的文件下载:
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;public class FileDownloadExample {public static void main(String[] args) {URL url = new URL("https://blog.csdn.net/xiaolingting?type=blog");HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();urlConnection.setRequestMethod("GET");urlConnection.setConnectTimeout(5000);urlConnection.setReadTimeout(10000);int responseCode = urlConnection.getResponseCode();if (responseCode != HttpURLConnection.HTTP_OK) {System.out.println("download error");return;}
// 1.0 传统方式 try (InputStream inputStream = urlConnection.getInputStream();FileOutputStream download = new FileOutputStream("download");) {BufferedInputStream bis = new BufferedInputStream(inputStream);BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(download);int bytesRead = 0;byte[] buffer = new byte[4 * 1024];while ((bytesRead = bis.read(buffer)) != -1) {bufferedOutputStream.write(buffer, 0, bytesRead);}} catch (IOException e) {throw new RuntimeException(e);}// 2.0 NIO 优化
// try(InputStream inputStream = urlConnection.getInputStream();
// FileChannel download = FileChannel.open(Paths.get("download"),
// StandardOpenOption.CREATE, StandardOpenOption.WRITE,StandardOpenOption.READ)) {
//
// MappedByteBuffer mapBuffer = download.map(FileChannel.MapMode.READ_WRITE, 0,
// urlConnection.getContentLength());
//
// int bytesRead = 0;
// byte[] buffer = new byte[4 * 1024];
// while ((bytesRead = inputStream.read(buffer)) != -1) {
// mapBuffer.put(buffer, 0, bytesRead);
// }
//
//
// } catch (IOException e) {
// throw new RuntimeException(e);
// } urlConnection.disconnect();}
}
代码说明
-
HTTP 连接建立
- 使用
HttpURLConnection
建立与目标 URL 的连接,设置超时时间避免阻塞。
- 使用
-
响应码检查
- 通过
connection.getResponseCode()
获取响应码,仅当状态码为200 OK
时继续下载。
- 通过
-
流处理
- 输入流:通过
connection.getInputStream()
获取服务器返回的字节流。 - 缓冲流:使用
BufferedInputStream
和BufferedOutputStream
提升读写效率。 - 分块读取:通过
4KB
缓冲区循环读取数据,避免大文件内存溢出。
- 输入流:通过
-
异常处理
- 使用
try-with-resources
自动关闭流,防止资源泄漏。 - 捕获
IOException
处理网络或IO错误。
- 使用
扩展功能
-
断点续传
添加Range
请求头指定下载范围:connection.setRequestProperty("Range", "bytes=0-");
处理
206 Partial Content
状态码。if (responseCode == HttpURLConnection.HTTP_PARTIAL) { // 206 Partial Content// 获取输入流并计算总文件大小try (InputStream inputStream = connection.getInputStream;RandomAccessFile file = new RandomAccessFile(savePath, "rw")) {// 跳过已下载的字节位置file.seek(file.length);byte[] buffer = new byte[4 * 1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {file.write(buffer, 0, bytesRead);downloadedBytes += bytesRead;updateCheckpoint(checkpointFile, downloadedBytes); // 更新断点记录}} }
-
进度监控
在while
循环中累加已读字节数,结合总文件大小(从Content-Length
头获取)计算下载进度。 -
MD5 校验
添加ETag
或Last-Modified
头验证文件一致性。
注意事项
- HTTPS 支持:需处理证书问题(默认信任所有证书可能不安全),参考HTTPS实现安全的关键方法及技术细节。
- 大文件下载:建议使用
NIO
或异步流提升性能。 - 生产环境:推荐使用 Apache HttpClient 或 OkHttp 等成熟库。
六、其它细节
1、HTTP版本差异
- HTTP/1.1使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
- HTTP/2.0 协议是基于 HTTPS 的,即 HTTP/2.0 的安全性是有保障的。
- HTTP/1.1 和 HTTP/2.0 传输协议使用的是
TCP
协议,而到了 HTTP/3.0 传输协议改用了UDP
协议。
关于HTTPS细节可以参考HTTPS实现安全的关键方法及技术细节