自己寫(xiě)一個(gè)Web服務(wù)器
- 在此之前需要先了解一些東西巷送。
一蕴忆、網(wǎng)絡(luò)與IP
HTTP 協(xié)議的底層其實(shí)是由 TCP 協(xié)議和 IP 協(xié)議(簡(jiǎn)稱(chēng) TCP/IP)構(gòu)建的。
TCP傳輸控制協(xié)議(Transmission Control Protocol)
TCP協(xié)議的特點(diǎn)是:
面向連接、點(diǎn)對(duì)點(diǎn)(一對(duì)一)颤绕、可靠交付、面向字節(jié)流祟身,也就是說(shuō)僅僅把上層協(xié)議傳遞過(guò)來(lái)的數(shù)據(jù)當(dāng)成字節(jié)傳輸奥务。
為了實(shí)現(xiàn)TCP上述的特點(diǎn),TCP協(xié)議需要解決的是面向連接(建立連接和關(guān)閉連接的方式)袜硫、可靠傳輸(錯(cuò)誤確認(rèn)和重傳)氯葬、流量控制(發(fā)送方和接收方的傳輸速率協(xié)調(diào))、擁塞控制四個(gè)方面婉陷。
IP網(wǎng)絡(luò)協(xié)議(Internet Protocol)
只要你在互聯(lián)網(wǎng)中帚称,那么你就會(huì)有一個(gè) IP。通俗上理解秽澳,IP 分為「內(nèi)網(wǎng) IP」和「外網(wǎng) IP」
- 什么是外網(wǎng)IP闯睹?
例如,你買(mǎi)了一個(gè)路由器担神,只要路由器連接上了電信的服務(wù)器楼吃,那么路由器就會(huì)有一個(gè)「外網(wǎng) IP」,比如「14.17.32.211」就是一個(gè)外網(wǎng) IP。這就是你在互聯(lián)網(wǎng)中的地址孩锡。 - 什么是內(nèi)網(wǎng)IP酷宵?
答案就是你的電腦和手機(jī)的IP,路由器會(huì)在你家里創(chuàng)建一個(gè)內(nèi)網(wǎng)躬窜,內(nèi)網(wǎng)中的設(shè)備使用內(nèi)網(wǎng) IP忧吟,一般來(lái)說(shuō)這個(gè) IP 的格式都是 192.168.xxx.xxx。
一般路由會(huì)給自己分配一個(gè)好記的內(nèi)網(wǎng) IP斩披,如 192.168.1.1
然后路由會(huì)給每一個(gè)內(nèi)網(wǎng)中的設(shè)備分配一個(gè)不同的內(nèi)網(wǎng) IP溜族,如電腦是 192.168.1.2,手機(jī)是 192.168.1.3垦沉,以此類(lèi)推煌抒。
現(xiàn)在路由器有兩個(gè) IP,一個(gè)外網(wǎng) IP(14.17.32.211)和一個(gè)內(nèi)網(wǎng) IP(192.168.1.1)
內(nèi)網(wǎng)中的設(shè)備可以互相訪問(wèn)(比如你可以用電腦或手機(jī)進(jìn)入 http://192.168.1.1 來(lái)查看你的路由器)厕倍,但是不能直接訪問(wèn)外網(wǎng)寡壮,內(nèi)網(wǎng)設(shè)備想要訪問(wèn)外網(wǎng),就必須經(jīng)過(guò)路由器中轉(zhuǎn)讹弯。外網(wǎng)中的設(shè)備也是這樣况既。
二、端口
- 你想要訪問(wèn)一個(gè)設(shè)備组民,只指定IP是不夠的棒仍,還必須在指定端口。
- 端口其實(shí)就是一個(gè)編號(hào)臭胜,并不是一種硬件莫其。一個(gè)服務(wù)器(硬件)不一定只提供一種服務(wù),比如一個(gè)服務(wù)器既提供 HTTP 服務(wù)耸三,又提供 FTP 服務(wù)乱陡,還提供 SMTP 服務(wù)(郵件服務(wù)),那么只用一個(gè) IP 是無(wú)法告訴服務(wù)器你想要使用哪種服務(wù)仪壮。
所以這里有一個(gè)重要的原則:一個(gè)端口對(duì)應(yīng)一個(gè)服務(wù)憨颠。
問(wèn)題一:我怎么知道應(yīng)該使用什么端口?
答:維基百科可以告訴你积锅,點(diǎn)進(jìn)去看看吧爽彤。
問(wèn)題二:一共由多少端口?
答:每個(gè)機(jī)器一共有 65535(2的16次方減1)個(gè)端口(這是協(xié)議規(guī)定的)乏沸。
不過(guò)這些端口的使用由一些規(guī)定:
1淫茵、 0 到 1023(2的10次方減1)號(hào)端口是留給系統(tǒng)使用的爪瓜,你只有擁有了管理員權(quán)限后蹬跃,才能使用這 1024 個(gè)端口。
2、其他端口可以給普通用戶使用蝶缀。
3丹喻、如果一個(gè)端口正在提供服務(wù),也就是被占用了翁都,那么就不能再使用這個(gè)端口碍论。除非你先停掉正在占用這個(gè)端口的服務(wù)。以后你們會(huì)經(jīng)常遇到這個(gè)問(wèn)題柄慰。
問(wèn)題三:我訪問(wèn) http://qq.com 時(shí)并沒(méi)有提供端口號(hào)鳍悠,為什么我依然可以訪問(wèn)?
答:因?yàn)?strong>瀏覽器幫你加了默認(rèn)端口號(hào) 80。
三坐搔、一個(gè)簡(jiǎn)易Server
1藏研、服務(wù)器你已經(jīng)有了,你使用的電腦就是你的服務(wù)器概行。
2蠢挡、但是你還沒(méi)有提供 HTTP 服務(wù)的「程序」,用腳本就可以提供 HTTP 服務(wù)凳忙,例如Node.js腳本业踏。
Node.js服務(wù)器
- 新建一個(gè)安全的目錄
cd ~/Desktop
mkdir node-demo
cd node-demo
touch server.js
- 編輯 server.js,內(nèi)容如下:
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if(!port){
console.log('請(qǐng)指定端口號(hào)好不啦?\nnode server.js 8888 這樣不會(huì)嗎涧卵?')
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
/******** 從這里開(kāi)始看勤家,上面不要看 ************/
console.log('得到 HTTP 路徑\n' + path)
console.log('查詢字符串為\n' + query)
console.log('不含查詢字符串的路徑為\n' + pathNoQuery)
/******** 代碼結(jié)束,下面不要看 ************/
})
server.listen(port)
console.log('監(jiān)聽(tīng) ' + port + ' 成功\n請(qǐng)用在空中轉(zhuǎn)體720度然后用電飯煲打開(kāi) http://localhost:' + port)
4.運(yùn)行node server.js
5.根據(jù)報(bào)錯(cuò)提示調(diào)整你的命令
6.成功之后柳恐,這個(gè) server 會(huì)保持運(yùn)行却紧,無(wú)法退出
- 如果你想「中斷」這個(gè) server,按 Ctrl C即可(C 就是 Cancel 的意思)
- 中斷后你才能輸入其他命令
- 建議把這個(gè) server 放在那里別動(dòng)胎撤,新開(kāi)一個(gè) Bash 窗口晓殊,完成下面的教程好了服務(wù)器完成。
7.這個(gè)服務(wù)器目前只有一個(gè)功能伤提,那就是打印出路徑和查詢字符串巫俺。
8.在新的 Bash 窗口運(yùn)行 curl http://localhost:你的指定的端口/xxx 或者 curl http://127.0.0.1:你指定的端口/xxx。你會(huì)馬上發(fā)現(xiàn) server 打印出了路徑:
9.這說(shuō)明我們的 server 收到了我們用 curl 發(fā)出的請(qǐng)求
10.由于 server 遲遲沒(méi)有發(fā)出響應(yīng)肿男,所以 curl 就一直等在那里介汹,無(wú)法退出(用 Ctrl+C中斷 curl)
發(fā)出響應(yīng)
接下來(lái)讓我們的server 發(fā)出響應(yīng)
1.編輯 server.js
2.在中間標(biāo)注的區(qū)域添加兩行代碼
response.write('Hi')
response.end()
3.中斷之前的 server,重新運(yùn)行 node server.js 8888
4.curl http://127.0.0.1:8888/xxx舶沛,結(jié)果如下:
Hi%
(這里注意嘹承,Windows系統(tǒng)可能有不一樣的結(jié)果,例如下圖如庭,原因我暫未知)
這個(gè) % 不是我們的內(nèi)容叹卷,% 表示結(jié)尾。如果你不喜歡 % ,就把 'Hi' 換成 'Hi\n'骤竹。
5.好了帝牡,響應(yīng)添加成功
6.使用 curl -s -v -- "http://localhost:8888/xxx" 可以查看完整的請(qǐng)求和響應(yīng)。
下面是一個(gè)完整的例子:
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if(!port){
console.log('請(qǐng)指定端口號(hào)好不啦蒙揣?\nnode server.js 8888 這樣不會(huì)嗎贮尉?')
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
/******** 從這里開(kāi)始看粱栖,上面不要看 ************/
console.log('HTTP 路徑為\n' + path)
if(path == '/style.css'){
response.setHeader('Content-Type', 'text/css; charset=utf-8')
response.write('body{background-color: #ddd;}h1{color: red;}')
response.end()
}else if(path == '/script.js'){
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write('alert("這是JS執(zhí)行的")')
response.end()
}else if(path == '/index.html'){
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.write('<!DOCTYPE>\n<html>' +
'<head><link rel="stylesheet" href="/style.css">' +
'</head><body>' +
'<h1>你好</h1>' +
'<script src="/script.js"></script>' +
'</body></html>')
response.end()
}else{
response.statusCode = 404
response.end()
}
/******** 代碼結(jié)束,下面不要看 ************/
})
server.listen(port)
console.log('監(jiān)聽(tīng) ' + port + ' 成功\n請(qǐng)用在空中轉(zhuǎn)體720度然后用電飯煲打開(kāi) http://localhost:' + port)
完