徹底搞懂HTTP協(xié)議 - 天天造輪子
時(shí)間:2023-07-18 18:39:02 | 來源:網(wǎng)站運(yùn)營
時(shí)間:2023-07-18 18:39:02 來源:網(wǎng)站運(yùn)營
徹底搞懂HTTP協(xié)議 - 天天造輪子:你將Get的技能
文章轉(zhuǎn)載:樂字節(jié)
- 如何調(diào)戲百度服務(wù)器 - 用自己手寫的HTTP協(xié)議
- 如何調(diào)戲Chrome瀏覽器 - 用自己手寫的HTTP協(xié)議
- 了解HTTP協(xié)議與TCP協(xié)議的關(guān)系
- 了解HTTP協(xié)議的發(fā)明的動(dòng)機(jī)
- 一個(gè)字一個(gè)字編寫一個(gè)HTTP報(bào)文
Why
據(jù)說前端界有一個(gè)到非常有名的面試題叫做
輸入一個(gè)url發(fā)生了什么
如果簡單回答大概可以分為
- DNS解析
- TCP連接
- 發(fā)送HTTP請求
- 服務(wù)器 HTTP 應(yīng)答
- 瀏覽器解析渲染
- 連接結(jié)束
等幾個(gè)階段,當(dāng)然如果詳細(xì)拆分每個(gè)階段還會(huì)在再次被細(xì)分,所謂細(xì)節(jié)決定成敗,你談的細(xì)節(jié)越多就說明你的知識越系統(tǒng)。唬住唬不住就看你了,不過這確實(shí)是一道非常好的考題。
今天然叔只想談?wù)勂渲械囊画h(huán)。
就是如何在TCP協(xié)議之上發(fā)送HTTP請求,以及如何應(yīng)答HTTP請求。
游戲規(guī)則
實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)。
那么怎么才能驗(yàn)證我們確實(shí)可以實(shí)現(xiàn)了HTTP協(xié)議呢?
我們知道HTTP協(xié)議分為Request 和 Response兩部分
首先我們會(huì)使用 TCP 協(xié)議封裝 HTTP協(xié)議. 通過以下兩個(gè)標(biāo)準(zhǔn)驗(yàn)證實(shí)驗(yàn)是否成功。
- 【Request請求】的驗(yàn)證標(biāo)準(zhǔn): 可以收到百度的應(yīng)答
- 【Response響應(yīng)】有效的標(biāo)準(zhǔn): 可以讓Chrome瀏覽器訪問
Where - 網(wǎng)絡(luò)基礎(chǔ)與TCP/IP
第一步我們要先找到我們應(yīng)該在哪做這個(gè)游戲,要不然是不是就是神仙打架。
我們知道網(wǎng)絡(luò)其實(shí)就是使用最少兩根導(dǎo)線,將多個(gè)網(wǎng)絡(luò)節(jié)點(diǎn)連接起來交換數(shù)據(jù)。
可想而知,兩個(gè)兒子還要打架,那么成千上萬的計(jì)算機(jī)如果要保證他們不打架和平相處,就需要復(fù)雜的協(xié)議支撐。
在計(jì)算機(jī)世界中如果一個(gè)復(fù)雜問題通常的解決方式就是分層解決
其實(shí)這個(gè)就是OSI參考模型,而實(shí)際我們現(xiàn)在的互聯(lián)網(wǎng)世界是就是這個(gè)理論模型的落地叫做TCP/IP協(xié)議
What - TCP與HTTP是什么
什么是TCP通訊?
其實(shí)傳輸層有兩種通訊方式分別是TCP和UDP。
兩種協(xié)議都能夠傳輸數(shù)據(jù),區(qū)別主要是要不要提前建立連接 TCP就是需要建立連接的一個(gè),好處在于通訊方式比較可靠。所以我們說TCP不丟包。
但是UDP也不是沒有用武之地,就比如說玩游戲 ,一技能沒作用我再按一次就行了,所以延時(shí)小比可靠連接更重要,所以早期的游戲很多都看上了UDP協(xié)議。
對于一門高級編程語言來講無論是(C++ , Java, JS)一般都是可以基于叫做socket的東西完成數(shù)據(jù)傳輸?shù)摹?br> TCP通訊程序
下面我們來個(gè)Node小例子。
Client
var net = require("net"); var client = net.connect(3000, () => { console.log("連接到服務(wù)器!"); }); let n = 3; const interval = setInterval(() => { const msg = "Time " + new Date().getTime(); console.log("客戶端發(fā)送: " + msg); client.write(msg); if (n-- === 0) { client.end(); clearInterval(interval); } }, 500); client.on("end", function () { console.log("斷開與服務(wù)器的連接"); }); 復(fù)制代碼
Server
var net = require("net"); var server = net.createServer((connection) => { console.log("client connected"); connection.on("data", (data) => { console.log("Server接收: " + data.toString()); }); connection.on("end", function () { console.log("客戶端關(guān)閉連接"); }); connection.end("Hello I am /r/n"); }); server.listen(3000, function () { console.log("server is listening at 3000"); }); 復(fù)制代碼
為什么需要HTTP協(xié)議
既然上面我們已經(jīng)知道了通過TCP可以收發(fā)數(shù)據(jù),假設(shè)我們想做一個(gè)類似論壇BBS這樣的需求怎么做。
我們大體上可以把BBS服務(wù)器比作一個(gè)存儲文本、圖片、甚至聲音、視頻的圖書館。
用戶如果想借書或者是還書都應(yīng)該正確的填寫借書單。這樣才能保證存取有序。
顯然這種功能TCP協(xié)議并沒有規(guī)定,TCP只是提供了交換數(shù)據(jù)的可能,相當(dāng)于打開了借書小窗口。真正要完成借書還書還需要設(shè)計(jì)一個(gè)借書單。其實(shí)這個(gè)借書單就是HTTP協(xié)議。
超文本傳輸協(xié)議(英文:
Hyper
Text
Transfer
Protocol,縮寫:HTTP)是一種用于分布式、協(xié)作式和超媒體信息系統(tǒng)的應(yīng)用層協(xié)議。HTTP是萬維網(wǎng)的數(shù)據(jù)通信的基礎(chǔ)。
下面我們先簡單瀏覽一下HTTP協(xié)議你看看是否非常像一個(gè)借書單。
HTTP協(xié)議規(guī)則
下面我們來細(xì)致講解HTTP協(xié)議
要想看到HTTP報(bào)文長什么樣子,可以使用curl命令
其實(shí)HTTP報(bào)文就是一個(gè)文本,這里面使用分隔符比如空格、回車、換行符來區(qū)分他的不同部分。
解析HTTP報(bào)文
下面我們著手去用代碼解析一個(gè)HTTP報(bào)文。
第一步 拆分請求行、頭部、請求體
- 請求行: 就是第一行 - 第一個(gè)回車符和換行符前的字符都是請求行
- 頭部: 請求行之后一直到遇到一個(gè)空行 -- 其實(shí)就是遇到兩個(gè)連續(xù)的回車符和換行符
- 請求體: 剩下的部分
private parse(): void { this.httpMessage = {} as HttpMessage; const messages = this.message.split('/r/n'); const [head] = messages; const headers = messages.slice(1, -2); const [body] = messages.slice(-1); this.parseHead(head); this.parseHeaders(headers); this.parseBody(body); }
第二步 解析請求行
請求結(jié)構(gòu)就是 : 請求方法 + 【空格】+ URL +【空格】+ 版本號
private parseHead(headStr) { const [method, url, version] = headStr.split(' '); this.httpMessage.method = method; this.httpMessage.url = url; this.httpMessage.version = version; }
第三步 解析頭部 頭部的結(jié)構(gòu):
KEY_A : VALUE
KEY_A : VALUE
KEY_C : VALUE
function parseHeaders(headers) { this.httpMessage.headers = {}; for (let i = 0; i < headers.length; i++) { const header = headers[i]; const [key, value] = header.split(":"); key = key.toLocaleLowerCase(); value = value.trim(); this.httpMessage.headers[key] = value; } }
請求體請求體就是剩下的部分無需解析
拼裝HTTP響應(yīng)拼裝的過程其實(shí)就是將整個(gè)過程反過來進(jìn)行
function format() { const head = `${this.version} ${this.status} ${this.message}`; let headers = ''; for (let key in this.headers) { const value = this.headers[key]; headers += `${key.toLocaleLowerCase()}: ${value}/r/n`; } const combineData = [head, headers, this.body].join('/r/n'); return combineData;}
實(shí)現(xiàn)HTTP爬蟲訪問百度首頁下面利用剛才寫好的HTTP函數(shù)拼裝一個(gè)報(bào)文調(diào)戲一下【 百度 】
const net = require("net");const createFormater = require("./http/formater");const formater = createFormater("request");const req = { method: "GET", url: "/", version: "HTTP/1.1", headers: { "user-agent": "curl/7.71.1", accept: "*/*" }, body: "",};console.log(formater.format(req))const client = net.connect(80, "www.baidu.com", () => { console.log("連接到服務(wù)器!"); client.write(formater.format(req));});client.on("data", function (data) { console.log(data.toString()); client.end();});client.on("end", function () { console.log("斷開與服務(wù)器的連接");});
大家注意這段程序并沒有用http協(xié)議 ,而只是向百度發(fā)送了一個(gè)tcp請求,使用的報(bào)文也是剛才我自己實(shí)現(xiàn)的。結(jié)果百度服務(wù)器真的應(yīng)答了。調(diào)戲成功,說明我們的HTTP協(xié)議實(shí)現(xiàn)的不錯(cuò)。
實(shí)現(xiàn)能被Chrome訪問的HTTP服務(wù)器下面我們再來試試這個(gè)程序是否能夠經(jīng)受住chrome的考驗(yàn)
const net = require("net");const createFormater = require("./http/formater");const formater = createFormater("response");const res = { version: "HTTP/1.1", status: "200", message: "OK", headers: { date: "Sat, 04 Dec 2021 14", connection: "keep-alive", "keep-alive": "timeout=5", // "content-length": "19", }, body: "<h1> Hello HTTP<h1>",};const server = net.createServer(function (connection) { console.log("client connected"); connection.on("data", (data) => { console.log(data.toString()); }); connection.on("end", function () { console.log("客戶端關(guān)閉連接"); }); connection.end(formater.format(res));});server.listen(3000, function () { console.log("server is listening");});
服務(wù)器發(fā)送請求后向?yàn)g覽器發(fā)送了自己組裝的應(yīng)答,瀏覽器正確渲染的頁面。這個(gè)實(shí)驗(yàn)也可以認(rèn)為是成功的。
總結(jié)回顧目前雖然簡單的實(shí)現(xiàn)了HTTP協(xié)議但是還很初級,后續(xù)還會(huì)補(bǔ)充
- 圖片、視頻、音頻數(shù)據(jù)
- cookie-session鑒權(quán)
- 緩存實(shí)現(xiàn)
- 分包上傳
- 管線化
- Http2.0
- Https 與RSA證書
我們都會(huì)一一實(shí)現(xiàn)
文章轉(zhuǎn)載:樂字節(jié)