解剖http协议
解剖HTTP协议
什么是HTTP
相比于称呼HTTP 是一个应用层协议, 我个人跟倾向于 称呼http 是一种类似json文字语法。http 分为请求和响应,其格式如下
request
POST /path HTTP/1.1
Content-Type: application/x-www-form-urlencoded
key1=value1&key2=value2
response
HTTP/1.1 200 OK
Content-Type: application/json
{
"key":"value"
}
- 其中,请求报文中的第一行称为
请求行
, 第二行到空行为止称为请求头
, 从空行的下一行开始到结束称为请求体
- 响应报文中的第一行称为
状态行
, 第二行到空行为止称为响应头
, 从空行的下一行开始到结束称为响应体
手工实现一个HTTP 请求
try {
Socket socket = new Socket("www.baidu.com",80);
StringBuilder sb = new StringBuilder("GET / HTTP/1.1\r\n");
sb.append("Accept: text/html\r\n");
sb.append("Host: www.baidu.com\r\n");
sb.append("Connection: keep-alive\r\n");
sb.append("\r\n");
OutputStream outputStream = socket.getOutputStream();
outputStream.write(sb.toString().getBytes());
outputStream.flush();
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = -1;
while ((len = is.read(bytes)) != -1) {
baos.write(bytes, 0, len);
}
System.out.println(new String(baos.toByteArray()));
} catch (IOException e) {
e.printStackTrace();
}
- 由此可见,HTTP 协议本质上是一个阅读友好的文字协议,同理 HTTP 响应也是一个文字协议
- 所以,JDK 中的HttpURLConnection类,Apache 的 httpcomponents 亦或是OkHttp 本质上都是 按固定语法格式解析字符串的解析逻辑
被玩坏的HTTP
以下代码,模拟了一个HTTP 响应,并使用浏览器访问 http://localhost:8080,
try {
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = null;
while (true) {
socket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null && !line.equals("")) {
stringBuilder.append(line).append("<br/>");
}
System.out.println(stringBuilder.toString());
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
printWriter.println("HTTP/1.1 1000 摸摸哒");
printWriter.println("Content-Type: application/json; charset=utf-8");
printWriter.println("");
printWriter.println("{\n" +
" \"code\":\"1000\",\n" +
" \"status\":\"successful\"\n" +
"}");
printWriter.flush();
printWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
- 可以发现,浏览器并没有出现崩溃和其他异常,是因为,虽然返回了一个非常规的1000 状态码, 但这是合乎http 的语法的
深入思考
曾经有前端同事错误的认为, HTTP 状态码 200 是表示网络通畅
但是从上面的代码看来,状态码的本质其实就是一个字符串,它本身并不能表示网络状态
而且 HTTP 似乎是被封装在一个Socket 中传输的。
所以万一网络出了问题,HTTP 是无法感知的,并且,即使服务器返回了200 状态, 客户端也无法收到的
那么Socket 是一个什么东西呢? Socket 本质上是一个 TCP 连接,它是由java 封装抽象出来,并由上层应用使用的对象。
总结
-
打个比方,HTTP 是写信的固定格式,比如 开头必须是”亲爱的”开头,结尾必须写祝语和署名一般
-
TCP 则是 邮递系统,根据邮编和地址送信
-
网卡、网线则是邮递车和马路,是邮递系统必须依赖的硬件基础设施