網(wǎng)絡(luò)與IP
HTTP協(xié)議的底層是由TCP和IP協(xié)議(TCP/IP)構(gòu)建的
什么是TCP
TCP傳輸控制協(xié)議(Transmission Control Protocol):按層次分,TCP位于傳輸層,提供可靠的字節(jié)流服務(wù)。
換句話說蝙云,為了更容易的傳輸大數(shù)據(jù)硫嘶,TCP協(xié)議會把數(shù)據(jù)分割成以報文段為單位的數(shù)據(jù)包進行管理岔激,并且TCP協(xié)議能夠確認數(shù)據(jù)最終是否到達對方(一般指所謂的三次握手)刀闷。
- TCP與UDP的區(qū)別:
簡答:TCP 可靠、面向連接瘦赫、相對 UDP 較慢;UDP 不可靠蛤迎,不面向連接确虱、相對 TCP 較快。搞定忘苛。 - TCP的三次握手:
- 簡答:每次建立連接前蝉娜,客戶端和服務(wù)端之前都要先進行三次對話才開始正式傳輸內(nèi)容,三次對話大概是這樣的:
- 客戶端:我要連接你了扎唾,可以嗎
- 服務(wù)端:嗯召川,我準備好了,連接我吧
- 客戶端:那我連接你咯胸遇。
- 開始后面步驟
- 詳細解答:握手過程中使用了TCP的標(biāo)志(flag)——SYG(synchronize)和ACG(acknowledge)
- 發(fā)送端:發(fā)送一個帶SYG標(biāo)志的數(shù)據(jù)包給對方
- 接收端:收到后荧呐,回傳一個帶SYG/ACK標(biāo)志的數(shù)據(jù)包以示傳達確認信息
- 發(fā)送端:收到后,再回傳一個帶ACK標(biāo)志的數(shù)據(jù)包纸镊,代表握手結(jié)束倍阐。
- 簡答:每次建立連接前蝉娜,客戶端和服務(wù)端之前都要先進行三次對話才開始正式傳輸內(nèi)容,三次對話大概是這樣的:
IP網(wǎng)絡(luò)協(xié)議(Internet Protocol)
從層次上分,IP網(wǎng)絡(luò)協(xié)議位于網(wǎng)絡(luò)層逗威,其作用是把各種數(shù)據(jù)包傳送給對方峰搪,為了確保數(shù)據(jù)傳送到正確的對象,則需要滿足一些條件凯旭,其中最重要的兩個便是:IP地址與MAC地址
- IP地址指明了節(jié)點被分配到的地址
- MAC地址是指的網(wǎng)卡所屬的固定地址
注意:
IP是一種網(wǎng)絡(luò)協(xié)議概耻,而IP地址是類似127.0.0.1的地址使套。
IP地址可以與MAC地址進行匹配。IP地址可換鞠柄,而MAC地址基本不會變侦高。
在互聯(lián)網(wǎng)中,一般一個設(shè)備對應(yīng)一個IP地址厌杜。通俗理解奉呛,IP地址分為內(nèi)網(wǎng)IP與外網(wǎng)IP
- 電信服務(wù)商提供DNS服務(wù),你從網(wǎng)頁中輸入的網(wǎng)址首先會去電信查找對應(yīng)的IP地址夯尽。
- 路由器有一個「內(nèi)網(wǎng)IP」與一個「外網(wǎng)IP」瞧壮。
- 內(nèi)網(wǎng)中的設(shè)備可以互相訪問(比如你可以用電腦或手機進入 http://192.168.1.1 來查看你的路由器),但是不能直接訪問外網(wǎng)呐萌,內(nèi)網(wǎng)設(shè)備想要訪問外網(wǎng)馁痴,就必須經(jīng)過路由器中轉(zhuǎn)。
- 外網(wǎng)中的設(shè)備可以互相訪問(比如 qq.com 可以把首頁發(fā)送給你的路由器肺孤,你的路由器有外網(wǎng) IP)罗晕,但是外網(wǎng)中的設(shè)備無法訪問你的內(nèi)網(wǎng)設(shè)備。
- 外網(wǎng)的資源通過路由器的中轉(zhuǎn)傳達至內(nèi)網(wǎng)的各個設(shè)備中赠堵,路由器在其中充當(dāng)指路人的角色小渊。
- 路由器沒有固定的「外網(wǎng)IP」,都是臨時分配的茫叭,類似14.17.32.21酬屉,每次路由器重啟都會被重新分配一個IP地址。
- 路由器與其wifi組成整個內(nèi)網(wǎng)揍愁,在內(nèi)網(wǎng)中的每個設(shè)備都有一個對應(yīng)的「外網(wǎng)IP」呐萨,類似192.168.1.1,這個內(nèi)網(wǎng)IP也不是固定的莽囤,一般設(shè)備斷開wifi重連都會改變內(nèi)網(wǎng)IP地址谬擦。
- 兩個特殊的IP,本地IP 127.0.0.1 代表設(shè)備自身朽缎,特別的IP 0.0.0.0 不表示任何設(shè)備惨远。
端口
端口其實就是一個編號,并不是一種硬件话肖。
一個服務(wù)器(硬件)不一定只提供一種服務(wù)北秽,比如一個服務(wù)器既提供 HTTP 服務(wù),又提供 FTP 服務(wù)最筒,還提供 SMTP 服務(wù)(郵件服務(wù))贺氓,那么只用一個 IP 是無法告訴服務(wù)器你想要使用哪種服務(wù)。
所以這里有一個重要的原則:一個端口對應(yīng)一個服務(wù)床蜘。
- HTTP服務(wù) 80端口
- HTTPS服務(wù) 443端口
- FTP服務(wù) 21端口
每個機器一共有65535(2的16次方減一)個端口(協(xié)議規(guī)定)
- 0到1023(2的10次方減一)號端口留給系統(tǒng)使用掠归。
- 其他端口用戶用
- 如果端口被占用缅叠,需要停掉該端口的服務(wù)才能再次使用。
寫一個簡易的HTTP Server
創(chuàng)建一個后臺服務(wù)
任何網(wǎng)絡(luò)服務(wù)應(yīng)用程序總是要先創(chuàng)建一個服務(wù)對象虏冻。這在 Node.js 中通常通過 createServer 方法。
var http = require('http');
var server = http.createServer(function(request, response) {
// magic happens here!
});
每當(dāng)有 HTTP 請求到達服務(wù)器時弹囚,createServer
中傳入的函數(shù)就被自動執(zhí)行厨相。所以這個函數(shù)也被稱為是請求處理函數(shù)。
當(dāng)一個 HTTP 到達服務(wù)端鸥鹉,node 調(diào)用 request 處理程序蛮穿,并產(chǎn)生一些唾手可得的對象用以處理傳輸,這些對象就是 request 和 response毁渗。践磅。
監(jiān)聽端口
實際上,為了處理請求灸异,listen 方法需要在 server 對象上被顯式調(diào)用府适。在大多數(shù)情況下,你只要把端口號作為參數(shù)傳入 listen 方法中肺樟,作為監(jiān)聽端口即可檐春。
var port = process.argv[2]//命令的第三個參數(shù)
server.listen(port)
請求方法、訪問地址以及請求頭
當(dāng)處理一個請求時么伯,第一件事你需要做的是看一下這個方法和其訪問地址疟暖,以此決定你到底采取何種合理的行為。Node 通過把這些行為屬性附加到 request 對象上田柔,使得我們處理起來相對而言可以輕松一些俐巴。
var method = request.method
var path = request.url
這里的 method
總是一個普通的 HTTP 方法動作行為 (verb),path
是指沒有服務(wù)器協(xié)議和 端口號的完整訪問地址硬爆。一個典型的訪問地址通常意味著包括第三個斜杠以及后面的所有內(nèi)容欣舵。
請求頭也不是很難得到,它們也在 request 對象里摆屯,稱為 headers邻遏。
var headers = request.headers
var userAgent = headers['user-agent']
所有的請求頭全是小寫字母,而不管實際上它們是怎么進行傳輸?shù)呐捌铩K栽跓o論任何 情況下准验,解析請求頭就得到了簡化。
如果一些請求頭出現(xiàn)重復(fù)廷没,它們的值不是被覆蓋糊饱,就是通過英文分號進行分割。
設(shè)置響應(yīng)頭
響應(yīng)頭通過一個 setHeader
的屬性很方便的設(shè)置颠黎。
response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');
設(shè)置響應(yīng)頭時另锋,它們的名字是大小寫敏感的滞项。如果你重復(fù)設(shè)置響應(yīng)頭,最后一次設(shè)置的值也就是系統(tǒng)得到的值夭坪。
發(fā)送響應(yīng)體
既然 response
對象是一個 WritableStre
文判,向客戶端寫入返回體只是一個普通的流方法的問題。
response.write('<!DOCTYPE>\n<html>' +
'<head><link rel="stylesheet" href="/style.js">' +
'</head><body>' +
'<h1>你好</h1>' +
'<script src="/script.html"></script>' +
'</body></html>')
response.end();
也可以將響應(yīng)體放在end
方法中室梅。
關(guān)于錯誤
請求與響應(yīng)若是發(fā)生錯誤戏仓,則會自動觸發(fā)自身的error
事件。如果你不去處理監(jiān)聽這個事件亡鼠,此錯誤將被拋出赏殃,這導(dǎo)致你的程序崩潰。 你應(yīng)該無論如何都要添加 'error' 事件去監(jiān)聽你的請求對象间涵,哪怕你只是做一個日志或者用你自己的獨有方式去處理仁热。
request.on('error', function() {
// This prints the error message and stack trace to `stderr`.
console.error(err);
});
response.on('error', function() {
// This prints the error message and stack trace to `stderr`.
console.error(err);
});
HTTP 狀態(tài)碼
如果你嫌麻煩不想設(shè)置它,返回客戶端的默認狀態(tài)碼總是 200勾哩。當(dāng)然抗蠢,不是每個 HTTP 返回碼必須都是 200,在某些情況下你一定希望返回一個不同的狀態(tài)碼钳幅,所以你應(yīng)該設(shè)置 statusCode 屬性物蝙。
response.statusCode = 404;
代碼示例
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if(!port){
console.log('請指定端口號好不啦?\nnode server.js 8888 這樣不會嗎敢艰?')
process.exit(1)
}
var server = http.createServer(function(request, response){
var parsedUrl = url.parse(request.url, true)
var path = request.url
var query = ''
if(path.indexOf('?') >= 0){ query = path.substring(path.indexOf('?')) }
var pathNoQuery = parsedUrl.pathname
var queryObject = parsedUrl.query
var method = request.method
/******** 從這里開始看诬乞,上面不要看 ************/
console.log('HTTP 路徑為\n' + path)
if(path == '/style.js'){
response.setHeader('Content-Type', 'text/css; charset=utf-8')
response.write('body{background-color: #ddd;}h1{color: red;}')
response.end()
}else if(path == '/script.html'){
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write('alert("這是JS執(zhí)行的")')
response.end()
}else if(path == '/index.css'){
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.write('<!DOCTYPE>\n<html>' +
'<head><link rel="stylesheet" href="/style.js">' +
'</head><body>' +
'<h1>你好</h1>' +
'<script src="/script.html"></script>' +
'</body></html>')
response.end()
}else{
response.statusCode = 404
response.end()
}
/******** 代碼結(jié)束,下面不要看 ************/
})
server.listen(port)
console.log('監(jiān)聽 ' + port + ' 成功\n請用在空中轉(zhuǎn)體720度然后用電飯煲打開 http://localhost:' + port)
以上內(nèi)容參考node官網(wǎng),感興趣的可以去看看钠导。