【HTTP協議其實很簡單】02.我要找王者榮耀開發小組----理解HTTP協議的第一行

【HTTP協議其實很簡單.02】我要找王者榮耀開發小組

本篇主題:理解HTTP協議的第一行

上一篇,講了講怎麼去理解協議,其中的核心點就是用計算機的思維模式去思考。這一篇開始正式接觸HTTP協議的第一行。


古語講:預先善其事,必先利其器。所以在開始之前,我們需要了解一個“工具”(一個軟件的操作)。這個工具就是去看HTTP協議內容的工具,有的小夥伴可能已經想到了,是F12。下面講講工具的打開方法:

  1. 打開瀏覽器(chrome、360、qq隨便什麼都可以)
  2. 按下快捷鍵F12。你應該會看到下圖:
    image
  3. 下面的每一條代表的是一次HTTP請求。點擊可以查看這一次請求的具體內容。

工具準備好了,下面進入正題。


我們設想一個打電話的場景,目標是找騰訊公司/研發部/王者榮耀工作室/開發小組,而我們只知道騰訊公司前臺的電話(0755-83765566)。

撥通電話,問候之後,我們要告訴對方,我找研發部王者榮耀開發小組,順利的情況下前臺小姐姐會把電話轉給王者榮耀開發小組。

我們梳理一下整個過程中的重要信息:0755-83765566/研發部/王者榮耀工作室/開發小組

是不是有點像:pvp.qq.com/web201605/herodetail/116.shtml

電話號碼就像一個網址的域名(pvp.qq.com),研發部/王者榮耀工作室 就像web201605/herodetail,開發小組就像116.shtml

這些信息幫助前臺小姐姐很精準的找到我們想找的人,是這次電話場景中必不可少的內容。

同樣這也是HTTP協議中最重要的組成部分,我們來看看他們在HTTP協議中是如何體現的。

我們以這個網址爲例來看: https://sale.vmall.com/pseries.html?cid=10618

注意打開瀏覽器後先按下F12,然後再輸入網址,會在開發人員工具裏看到這樣的界面:
image

(注意,有很多網址看不到view source,找不到就換個別的網址)

依次點擊圖中紅框的位置,會看到下圖:

image

這就是http協議的第一行,這一行內容告訴服務器我需要什麼。就像打電話告訴前臺小姐姐我要找誰一樣。

我們可以寫一個程序代替瀏覽器發起這一次請求。來完整的看一看一個完整的HTTP協議請求和響應的內容分別是什麼。

這次實驗的地址是:
www.12371yun.com/news-info.php?cid=2&id=69

下面是模擬瀏覽器的代碼:


import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class MyChrome {
    public static void main(String[] args) throws IOException {
        String host = "www.12371yun.com"; //定義要訪問哪個服務器,就像是要撥打哪個電話
        int port = 80; //定義端口,一般HTTP請求的默認商品是80
        SocketAddress endpoint = new InetSocketAddress(host, port); //創建地址對象
        // 與服務端建立連接
        Socket socket = new Socket(); //Socket類是TCP/IP請求的核心類。要知道HTTP是建立的TCP/IP基礎上的。
        socket.connect(endpoint,2000); //開始連接服務器,就好比開始撥打電話,參數2000是超時時間,意思是2秒中沒有影響則停止。
        // 建立連接後獲得輸出流
        OutputStream os = socket.getOutputStream(); //獲得輸出流,用來向服務器發送報文。告訴服務器我想幹什麼

        //message字符串就是具體的請求報文
        String message = "GET /news-info.php?cid=2&id=69 HTTP/1.1\n" + //第一行告訴服務器我的請求方式是GET,需要的資源是/news-info.php,cid=2&id=69是包含了兩個參數
                //Host是廣告服務器我要訪問的是哪個域名,服務器上往往是運行着多個網站,每個網站可能有不同的域名。雖然在代碼的第一行裏有域名,
                // 但是在連接的時候域名會被解析爲ip地址。而作爲服務器並不知道你要訪問哪一個域名。
                "Host: www.12371yun.com\n" +
                "Connection: keep-alive\n" +
                "Pragma: no-cache\n" +
                "Cache-Control: no-cache\n" +
                "Upgrade-Insecure-Requests: 1\n" +
                "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\n" +
                "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\n" +
                "Accept-Encoding: gzip, deflate\n" +
                "Accept-Language: zh-CN,zh;q=0.9\n";
        System.out.println(message);
        os.write(message.getBytes("UTF-8"));//將報文數據通過輸出流發送給服務器
        //通過shutdownOutput高速服務器已經發送完數據,後續只能接受數據
        socket.shutdownOutput();//結束輸出流,就向通話完成後告訴對方:完畢

        InputStream inputStream = socket.getInputStream(); //定義輸出流,準備接收服務器返回的報文

        byte[] bytes = new byte[1024];  
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = inputStream.read(bytes)) != -1) {
            //注意指定編碼格式,發送方和接收方一定要統一,建議使用UTF-8
            sb.append(new String(bytes, 0, len,"UTF-8"));
        }
        System.out.println("Response:\r\n================");
        System.out.println(sb);

        inputStream.close();
        os.close();
        socket.close();
    }
}

以上代碼運行後會在服務器端輸出如下結果:

以下爲請求報文:
GET http://12371yun.com/news-info.php?cid=2&id=69 HTTP/1.1
Host: www.12371yun.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9


================
以下爲響應報文:
HTTP/1.1 200 OK
Date: Wed, 12 Jun 2019 05:02:48 GMT
Server: Apache/2.2.21 (Win32) mod_ssl/2.2.21 OpenSSL/0.9.8t PHP/5.3.10
X-Powered-By: PHP/5.3.10
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html;charset=utf-8

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <meta name="renderer" content="webkit">
<title>話端陽 品傳統,跟習近平總書記學中華文化傳承之道 - 黨建動態 - 五心鴻卓,智慧黨建_專注智慧黨務,智慧黨建管理系統研發 - 鴻卓黨建雲平臺</title>
<meta name="generator" content="" />
<meta name="author" content="" />
<meta name="keywords" content="智慧黨建,黨建雲,智慧黨建系統,鴻卓黨建雲平臺" />
//以下HTML內容省略

我們可以看到請求的報文內容是

GET http://12371yun.com/news-info.php?cid=2&id=69   HTTP/1.1
Host: www.12371yun.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

這和瀏覽器檢測到的請求內容是一樣的:
image

而響應內容可以明顯看出是分成了兩部分,第一部分是:

HTTP/1.1 200 OK
Date: Wed, 12 Jun 2019 05:02:48 GMT
Server: Apache/2.2.21 (Win32) mod_ssl/2.2.21 OpenSSL/0.9.8t PHP/5.3.10
X-Powered-By: PHP/5.3.10
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html;charset=utf-8

這一部分和請求的格式有些相似,一般我們稱之爲報頭。

第二部分是:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <meta name="renderer" content="webkit">
<title>話端陽 品傳統,跟習近平總書記學中華文化傳承之道 - 黨建動態 - 五心鴻卓,智慧黨建_專注智慧黨務,智慧黨建管理系統研發 - 鴻卓黨建雲平臺</title>
<meta name="generator" content="" />
<meta name="author" content="" />
<meta name="keywords" content="智慧黨建,黨建雲,智慧黨建系統,鴻卓黨建雲平臺" />
//以下HTML內容省略

第二部分的內容和實際網頁的源碼則是一模一樣的。

這就是一次HTTP請求的完整過程,與瀏覽器不同的是我們的程序最後顯示的結果是源碼,而瀏覽器收到這些響應的內容則是呈現出一個好看的頁面。

另外,細心的同學可能會發現,F12工具中發現第一條請求之後會跟着發出了N多次請求,這就是瀏覽器在接收到第一個請求的響應之後,根據源碼的內容將所需要的各類資源(js,css,png,jpg等)依次下載,最終將這些內容呈現爲完整的網頁。


看到這裏我們再回到本篇文章的主題:第一行。
請求報文的第一行是:

GET http://12371yun.com/news-info.php?cid=2&id=69   HTTP/1.1

很明顯這裏麪包含了三部分:

第一部分

GET:表示請求的類型.

HTTP協議中共定義了八種方法或者叫“動作”來表明對Request-URI指定的資源的不同操作方式,具體介紹如下:

OPTIONS:返回服務器針對特定資源所支持的HTTP請求方法。也可以利用向Web服務器發送’*'的請求來測試服務器的功能性。

HEAD:向服務器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的元信息。

GET:向特定的資源發出請求。

POST:向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的創建和/或已有資源的修改。

PUT:向指定資源位置上傳其最新內容。

DELETE:請求服務器刪除Request-URI所標識的資源。

TRACE:回顯服務器收到的請求,主要用於測試或診斷。

CONNECT:HTTP/1.1協議中預留給能夠將連接改爲管道方式的代理服務器。

雖然HTTP的請求方式有8種,但是我們在實際應用中常用的也就是get和post,其他請求方式也都可以通過這兩種方式間接的來實現。

這些請求方式中GET請求的報文中是隻有報頭,沒有body的。
而POST請求則是有body的。POST請求一般是用在向服務器提交一些數據的時候使用,例如:登錄、註冊帳號、填寫一些數據等等。
我們來對比一下

POST http://127.0.0.1:80/auth/login.do HTTP/1.1
Host: 127.0.0.1:80
Connection: keep-alive
Content-Length: 66
Origin: http://127.0.0.1:80
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Content-Type: application/json;charset=UTF-8
Accept: */*


{"loginName":"test","loginPwd":"test","verifyCode":"aujp0e"}

這一段報文在最後還有一段json格式的數據。而GET請求是沒有這最後的內容的。

第二部分

http://12371yun.com/news-info.php?cid=2&id=69

這部分表示你要請求服務器上哪個資源,可以是一個html、php、jsp、css、png等等,或者是一個目錄。

那麼如果我們想要請求一個css的請需要怎麼修改這個報文呢?很簡單,只需要把這一部分換成下面這樣就可以了。

GET http://www.12371yun.com/css/index.css  HTTP/1.1

實際上,要完整的呈現出一個頁面,只有一次HTTP的請求過程是不夠的,因爲一個頁面需要有基本的HTML代碼,還有樣式、js、以及很多圖片,甚至還有從其它站點引入的js代碼庫等等,這些都需要瀏覽器在得到基本的HTML代碼之後進行解析,然後依次將所有的資源請求回來之後才能完整的呈現。

第三部分

HTTP/1.1
這一部分表示http協議的版本

總結

這一篇文章我們用一個控制檯程序去模擬瀏覽器進行了一次請求,通過這個程序我們可以完整的看到請求和響應的報文內容。

這個程序相當於是一個瀏覽端客戶端,下一篇,我將講解如何去寫一個服務器端,然後用瀏覽器去訪問我們的服務器端,去初步瞭解一下服務器端是如何處理HTTP請求的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章