真正“搞”懂HTTP協議04之搞起來

  前兩篇文章,我們從空間和時間的角度都對HTTP有了一定的學習和理解,那麼基於上一篇的HTTP發展的時間順序,我會在後面的文章由淺入深,按照HTTP版本內容的更迭,一邊介紹相關字段的使用方法,一邊講解其特性和目的,並和大家一起手寫測試代碼,學以致用。

  當然在真正進入時間線之前,我們還還需要一些前置內容,本篇呢,會先帶大家去手寫一下HTTP的一個小栗子及相關配置修改方式。然後我還會根據測試的HTTP請求帶大家先熟悉一下HTTP的基本內容。

  這就是本篇的所有內容啦~本來還想把HTTP的特點和方法也寫進來,但是覺得篇幅可能會太長,所以還是另起一篇吧。

一、一個小的不能再小的小栗子

  其實這個小栗子的代碼十分簡單,我們主要使用Node來作爲服務器端。然後還需要修改一下下本地的Hosts文件。廢話不多說,想必大家的大刀都飢渴難耐了吧,啊哈哈哈哈。

  我們先來把測試代碼寫好,簡單的一批,我是直接從Node官網的文檔的首頁直接複製下來的:

const http = require("http");

const hostname = "127.0.0.1";
const port = 8080;

const server = http.createServer((req, res) => {
  res.end("Hello Zaking World!This is Node");
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

  這段代碼十分簡單哈,就是啓動個服務,然後服務器返回一段字符串就完事了。然後我們直接在命令行中執行這段代碼的文件:

node ./server.js

  嗯,你可以看到這個文件的文件名叫做server。啓動成功後,我們直接打開瀏覽器在地址欄輸入http://127.0.0.1:8080/,就可以直接看到瀏覽器中顯示出了結果:

   很順利,很完美~到了這一步,其實你已經完成並體驗了HTTP/0.9的所有內容。Get請求,返回字符串。但是現在還只是通過本地的ip來訪問我們啓動的服務,我們要在本地模擬一下域名,直接通過域名來訪問我們啓動的Node服務,實驗一下Get請求。

  首先,我們找一下本機的hosts文件。Windows的hosts文件在這裏:C:\WINDOWS\system32\drivers\etc\hosts。你可以直接複製下來在windows裏搜索,注意保存的時候需要管理員權限,而mac的地址則是在這裏:/etc/hosts。可以通過命令行:open /etc/hosts打開後修改,也可以直接找到文件後修改。你可以在hosts中加一行,修改成這樣:

127.0.0.1       www.zaking.com

  然後,別忘了用管理員權限保存一下,我們直接打開瀏覽器,輸入www.zaking.com:8080。當然,這個域名你可以隨便起,就是本地自己玩嘛。我們可以看到瀏覽器同樣獲取到了我們想要的內容:

 

   爲什麼我們加了hosts的配置之後,就可以在本地通過域名來訪問我們啓動的服務了呢?其實簡單來說,就是域名解析的過程。當瀏覽器訪問www.zaking.com的時候,發現這不是一個ip那就肯定是域名了,於是會嘗試去訪問一系列的域名服務器,把這個域名轉換成ip,但是由於整個域名解析的體系實在是很複雜,如果每次都去訪問服務器確定域名對應的ip,那實在是慢死了。於是爲了解決慢的問題,就出現了緩存,當瀏覽器嘗試去獲取對應域名的ip的時候,會先去瀏覽器自己的緩存裏看看有沒有這個域名的IP,沒有就去操作系統裏找,再沒有,就會去檢查本機的域名解析文件,也就是hosts,於是,找到了就直接轉換成這個對應的ip了。

  那要是本機沒找到呢?那沒辦法,就得一級一級的去域名服務器找對應的ip啦。

  OK,我們的基本例子代碼和最最最簡單的實驗都做完了。接下來的篇章我們都基於這個最簡單的例子,去試驗各個HTTP的核心重點。

二、HTTP報文的組成

  還記得我們在空間穿梭的時候說過,當發送請求的時候,會在HTTP報文的基礎上每一層都會加上上一層的頭信息,然後當服務器獲取,路過各層的時候,會拆下來各層需要的頭信息。那HTTP其實也類似,也需要在傳輸的數據前附加一些頭數據。唯一不太一樣的是,HTTP的這些頭數據,是純文本,也就是ASCII碼,所以這些數據用肉眼就能分辨,不需要藉助解析工具。

  HTTP協議是基於請求—響應的模型,所以頭數據也分爲請求報文和響應報文。這兩種報文的結構也是基本相同的,都由三大部分組成:

  • 起始行(start line):描述請求或響應的基本信息
  • 頭部字段集合(header):使用key-value的形式更詳細的說明報文
  • 消息正文(entity):實際傳輸的數據,不一定只是文本,可能是圖片、視頻等二進制文件。

  看起來好像有點複雜,在通常情況下,我們會把起始行和頭字段統一叫做請求頭或者響應頭,也就是header,而傳輸的數據就叫做body。這樣看起來是不是簡單了些。

  我還記得我說要按照HTTP的歷史時間順序來講解HTTP,所以,現在應該講的是HTTP/0.9,但是HTTP/0.9又太少了,所以我只能把它包含在這裏了。

  後面,我會按照總分的方式來講後續的內容,總的就是總體的介紹、比如HTTP的方法、狀態碼、特點等等,分的部分呢,則是按照時間順序來講解頭字段。這樣,體系就清晰了。

  不多囉嗦啦,我們繼續~

一)起始行

  起始行這個東西其實有兩種叫法,發送請求的起始行就叫做請求行,發送響應的起始行就叫做狀態行。我們分別來看下他們的區別。

  我們來看張圖:

  這就是我們上面的基礎例子的HTTP請求的詳細headers。誒?你這糊弄我呢?你這也沒有啥請求行啊,不都是頭字段麼?糊弄我不懂呢?嗯……你看我後面慢慢給你編,上面這個圖先看下就好,咱們每天都接觸,不看也行啦。

1、請求行

  請求行由三部分構成,分別是:

  • 請求方法:通常是一個動詞,表示對服務器資源的操作,比如GET、POST等。
  • 請求目標:通常是一個URI,標記了目標資源的地址。
  • 版本號:表示報文使用的協議版本。

  你還記得HTTP的一個描述是:HTTP是使用純文本傳輸的,那麼在文本中,如何分割各個字段呢?嗯……就是空格,用空格來分割請求方法、請求目標以及版本號,然後,再用一個換行符來作爲請求行結束的標記。

  我們還是回到之前的例子中:

   看到跟之前的圖有什麼區別了嗎?這就是請求行原始的樣子,而我們上圖中看到的是parsed之後的,也就是經過了格式化,讓我們看起來更舒服。而source則可以讓我們看到報文的原始樣子。

  而在請求行中,實際上我們要重點關注的就是請求方法,這個我們下一章再說,我們先知道請求行都有哪些內容就好啦。

2、狀態行

   響應報文中的起始行不叫響應行,而是叫狀態行,狀態行也有三部分:

  • 版本號:嗯,就是跟請求行中一樣。
  • 狀態碼:一個三位數字,用來表示請求處理的結果,比如200、300、400啥的。
  • 原因:作爲狀態碼的補充,就像一個說明。

  狀態行就像這樣:

   那你知道爲啥響應報文中的起始行不叫響應行了不?重點就在這個狀態碼,告訴你對於當前請求的處理結果的狀態。而狀態碼和原因則往往是配對出現的。

  那麼請求中有“方法”,響應中有“狀態”。剛好,來回都描述的很完整。

二)頭字段

  請求行或者狀態行再加上頭字段,就是完整的請求頭或者響應頭。請求頭和響應頭的頭字段基本上是一致的,都是一種key-value形式的鍵值對。但是頭字段有以下幾點需要注意一下:

  • 字段名不區分大小寫,Host還是host還是HOST都是一個意思,但是通常我們會首字母大寫,便於閱讀與理解。
  • 字段名裏不能出現空格,可以有連字符“-”,但是不能有下劃線“_”。
  • 字段名後面必須緊跟着“:”,不能有空格,而“:”後面可以隨意空格。
  • 順序是無意義的,隨便。
  • 原則上字段名不能重複,除非本身的語義允許。

  HTTP協議規定了好多好多的頭字段,甚至我們還可以自定義頭字段,但是通常頭字段可以分爲以下幾類:

  • 通用字段,請求頭和響應頭中都可以使用
  • 請求字段,當然只能在請求頭中用。
  • 響應字段,當然只能出現在響應頭中。
  • 實體字段,其實算是通用字段,往往是爲了描述傳輸的數據的額外的信息。

  所以你看,原則上字段的類型就三種,要麼只有我能用,要麼只有你能用,要麼就都能用。

  當然,還有一種情況是往往有些字段不是獨立出現的,而是有一定的配合~我們後面會詳細聊噠。

三)實體

   實體,其實就是我們在使用HTTP的時候傳輸的數據內容,我們通常把它叫做body,當然,我們也可以通過GET方法來傳遞一定數據量的信息,0.9就是這樣做的,但是畢竟這算不上是“正途”,如果我想要傳音頻、視頻、圖片咋整?總不能也用GET請求放在URL的query裏吧?

  這就需要我們的body了,但是body要怎麼傳呢?我傳給你服務器一個視頻文件,但是服務器怎麼知道要按照什麼樣的方式來解析這個文件呢?嗯……可以根據文件的後綴吖,沒錯,那我要是改了後綴名呢?我就故意改,你能咋整,所以這種方法也不那麼靠譜。

  然後,其實某一個類型的文件的二進制編碼的前面一小部分其實是固定的,我們也可以根據這些固定的數據來判斷,但是這樣也好像不是那麼靠譜,而且還有點低效,還有很大概率查不出來。

  那咋整,那就是MIME type,MIME是一個很大的標準規範,HTTP拿取了其中的一部分用來表示body的數據類型。那啥是MIME呢?嗯,它的大名叫做“多用途互聯網郵件擴展”(Multipurpose Internet Mail Extensions),本來是給電子郵件用的,目的是爲了讓電子郵件可以發送出了ASCII碼以外的任意數據。

  MIME把數據分爲了八大類,每個大類下面還有子類,大概是這樣的:type/subtype。就很符合HTTP的標準要求,於是就順手牽羊拿過來了。

  在HTTP中常用的大概有text、image、audio/video、application等。最常見的大家也最熟悉的想必就是application/json啦。

  至於具體每一個字段的詳細解釋,我會在後面講解到對應字段的時候再加以詳述。

三、小小結

  我們稍稍回顧一下本篇我們都學習了哪些內容。

  首先,我帶大家完成了一個小小小栗子,看了一下GET請求。

  然後,依賴於這個小小小栗子,我們知道了HTTP的組成有哪三部分或者哪兩部分。

  最後我問大家一個問題:你知道起始行中的重點內容有哪些麼?

  我們下一篇再見~嘿嘿

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