http 深入淺出

duffy發表於2018-02-27

前言

相信大家對http都不陌生,我們平時用的網路是在tcp/ip協議族的基礎上運作的,http是屬於內部的一個子集。tcp/ip協議分別有四層:應用層(http)、傳輸層(tcp)、網路層(ip)、資料鏈路層(網路等),簡要說一下大概的流程:

  • 客戶端發起一個http的請求(http請求報文)
  • 傳輸層收到http請求報文資料後進行分割,當然是為了傳輸方便,然後在上面打上序號和埠。
  • 網路層接收後新增上MAC地址然後轉發給下一層鏈路層
  • 對應的接收伺服器鏈路層收到資料後就會解析,然後就是網路層》傳輸層》應用層一步一步解析。 下面一張圖就非常直觀了。
    tcp.png
    ok,瞭解大概流程後,我們繼續,這節主要說的是應用層http,當然本人也是菜鳥,只是想分享交流一下,不足的請指出。

瞭解 HTTP

HTTP協議是Hyper Text Transfer Protocol(超文字傳輸協議)的縮寫,是用於從全球資訊網(WWW:World Wide Web )伺服器傳輸超文字到本地瀏覽器的傳送協議。 HTTP是一個基於TCP/IP通訊協議來傳遞資料(HTML 檔案, 圖片檔案, 查詢結果等)

  • 請求的一方叫客戶端,響應的一方叫伺服器端
  • 通過請求和響應達成通訊
  • HTTP是一種不儲存狀態的協議

HTTP的歷史(瞭解一下哈~):

HTTP/0.9 --1990年問世,那時的HTTP並沒有作為正式的標準被建立,只支援GET方法,不支援MIM型別,很快被HTTP/1.0取代
HTTP/1.0 --HTTP正式作為標準被公佈是在1996年的5月,版本被命名為HTTP/1.0,並記載於RFC1945。雖然說是初期標準,但該協議標準至今仍被使用在伺服器端。
HTTP/1.1 --1997年公佈的HTTP/1.1是目前主流的HTTP協議版本。之前的標準是RFC2068,之後又釋出了修訂版RFC2616。
HTTP/2 --2015年,HTTP/2 釋出。它不叫 HTTP/2.0,是因為標準委員會不打算再發布子版本了,下一個新版本將是 HTTP/3。

HTTP之請求報文&相應報文

請求報文

請求報文的組成部分由請求行、請求頭、請求體三部分組成。

http請求結構.png

請求行

請求行由請求方法、URL 、協議/版本組成。

  • 這裡說一下請求方法,http請求中我們最常用的就是POST和 GET了,下面列出方法:

(1)GET:獲取資源
(2)POST:傳輸實體
(3)PUT:傳輸檔案。PUT方法自身不帶驗證機制,任何人都可以上傳檔案,存在安全性問題,因此一般的Web網站不使用該方法。若配合Web應用程式的驗證機制或架構設計採用REST(表徵狀態轉移)標準的同類Web網站,就可能會開放使用PUT方法。 (4)HEAD:獲得報文首部
(5)DELETE:刪除檔案。特點同PUT方法。
(6)OPTIONS:詢問支援的方法。伺服器不一定支援所有HTTP協議的方法,所以可以通過OPTIONS請求檢視伺服器支援的HTTP方法有哪些。
(7)TRACE:追蹤路徑。用來確認連線過程中發生的一系列操作,但該方法易引起XST(跨站追蹤)攻擊,通常是不會用到的。
(8)CONNECT:要求用隧道協議連線代理。

請求頭

  • 通用首部(General Header)
  • 請求首部(Request Header)
  • 響應首部(Response Header)
  • 實體首部(Entity Header Fields)

請求實體

也就是我們平時的請求資料,通常get請求資料為空。

響應報文

一般情況下,伺服器接收並處理客戶端發過來的請求後會返回一個HTTP的響應訊息。

HTTP響應由三個部分組成,分別是:狀態行、訊息報頭、響應正文。 這裡我們主要說一下狀態碼:

200 請求已正常處理 204 請求資源成功,但沒有資源可返回 206 部分資源的請求

301 資源的URI已更新,你也更新下你的書籤引用吧 302 資源的URI已臨時定位到其他位置了,姑且算你已經知道這個情況了 303 資源的URI已更新,你是否能臨時按新的URI訪問 304 資源已找到,但未符合條件請求......304雖然被劃分在3XX類別中,但是和重定向沒有關係。 307 臨時重定向,和302有著相同的含義,307會遵照標準,不會從POST變成GET。

400 伺服器無法理解這個請求 401 需要認證的請求,且認證失敗時,返回該狀態碼 403 伺服器拒絕該請求,可能是客戶端未獲得檔案系統的訪問授權,也可能是訪問許可權出現了某些問題。 404 伺服器上沒有訪問的資源

500 伺服器端在執行請求時發生了錯誤。 503 伺服器暫時處於超負載或正在進行停機維修,現在無法處理請求

我們常用的狀態有:

  • 200 OK //客戶端請求成功
  • 400 Bad Request //客戶端請求有語法錯誤,不能被伺服器所理解
  • 403 Forbidden //伺服器收到請求,但是拒絕提供服務
  • 404 Not Found //請求資源不存在,eg:輸入了錯誤的URL
  • 500 Internal Server Error //伺服器發生不可預期的錯誤
  • 503 Server Unavailable //伺服器當前不能處理客戶端的請求,一段時間後可能恢復正常

舉栗子說明一下

我們可以敲命令驗證一下:

 curl -v www.baidu.com
複製程式碼

test.png
紅色區域為請求報文

  • 紅色區域第一行 GET 為請求行
  • 其他行 各種首部欄位
  • 因為是get方法所以沒有 請求主體

藍色區域為相應報文

  • 藍色區域第一行 狀態行
  • 藍色區域第一行到第10行為各種首部欄位
  • 空白行 html部分為 響應正文

下面是請求報文和響應報文的結構:

結構圖.png

node建立HTTP伺服器

首先要說明一下,http伺服器是繼承自tcp伺服器 http協議是應用層協議,是基於TCP的。

let http = require('http');
//req 流物件 是可讀流
//res 是一個可寫流 write

let server = http.createServer();
let url = require('url');
//當客戶端連線上伺服器之後執行回撥
server.on('connection', function (socket) {
    console.log('客戶端連線 ');
});

/**
> POST / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.53.0
> Accept: *
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
} [9 bytes data]
 */

server.on('close', function (req, res) {
    console.log('伺服器關閉 ');
});
server.on('error', function (err) {
    console.log('伺服器錯誤 ');
});
server.listen(8080, function () {
    console.log('server started at http://localhost:8080');
});
複製程式碼

當客戶端連線上來之後先觸發connection事件,然後可以多次傳送請求,每次請求都會觸發request事件。流程就是伺服器監聽客戶端的請求,當有請求到來的時候執行回撥

HTTP請求

//req代表客戶端的連線,server伺服器把客戶端的請求資訊進行解析,然後放在req上面
//res代表響應,如果希望向客戶端迴應訊息,需要通過 res
server.on('request', function (req, res) {
    console.log(req.method);//獲取請求方法名
    let { pathname, query } = url.parse(req.url, true);
    console.log(pathname);
    console.log(query);
    console.log(req.url);//獲取請求路徑 
    console.log(req.headers);//請求頭物件
    let result = [];
    req.on('data', function (data) {
        result.push(data);
    });
    req.on('end', function () {
        let r = Buffer.concat(result);//請求體
        console.log(r.toString());
        //如果進行響應

        res.end(r);
    })
});
複製程式碼

HTTP響應

let http = require('http');
//如何向客戶端寫入響應資訊 */
/**
HTTP/1.1 200 OK  響應行
Date: Fri, 02 Feb 2018 13:57:46 GMT   響應頭
Connection: keep-alive
Content-Length: 9

name=dafei 響應體
Transfer-Encoding: chunked 分塊傳輸 
*/
let server = http.createServer(function (req, res) {
    console.log('request');
    //在同一個方法裡設定狀態碼,原因短語,響應頭
    res.setHeader('Content-Type', 'text/html');
    console.log('headersSent1', res.headersSent);//響應頭是否已經傳送過了
   
    res.writeHead(200, {
        "Content-Type": "text/html;charset=utf8"
    })
    
    console.log('headersSent2', res.headersSent);//響應頭是否已經傳送過了
    res.end('byebye');
    // res.statusCode = 404;//設定響應碼 
    // res.sendDate = false;//Date響應頭預設會設定,如果真的不想要,可以設定為false 
    // res.setHeader('Content-Type', 'text/html;charset=utf8');//設定響應頭
    // console.log('getHeader1', res.getHeader('Content-Type'));//獲取響應頭
    // res.removeHeader('Content-Type');//刪除響應頭
    // console.log('getHeader2', res.getHeader('Content-Type'));//獲取響應頭
    // res.write('hello');
    // res.write('world');
    // res.end();
    //res.write('byebye');//write after end 在可寫流結束之後再次寫入
});
server.on('connection', function (socket) {
    console.log('connection');
    socket.on('end', function () {
        console.log('連線end');
    });
    socket.on('close', function () {
        console.log('連線close');
    });
});
server.listen(8080);
複製程式碼

writeHead一旦呼叫會立刻向客戶端傳送,setHeader. 當呼叫writeHead 或者呼叫write方法的時候才會向客戶端發響應頭,200是HTTP狀態碼,表示成功處理了此次請求, 當然也可以設定不同的響應碼,用statusCode, response.end()表示的就是響應事件傳輸資料結束。

下一節有時間我將淺談TCP/IP,關於TCP資料包封裝、TCP的三次握手、TCP的四次揮手、TCP和UDP區別等,感謝你的關注,喜歡記得點贊哈