今天的学习简单的了解了服务器,写好脚本,用node.js把自己电脑当做服务器,(1号)一个窗口执行node.js脚本命令,(2号)另一个窗口执行请求,成功了就把1号窗口的脚本内容返回到2号窗口中显示
TCP 传输控制协议(Transmission Control Protocol)
简答:TCP 可靠、面向连接、相对 UDP 较慢;UDP 不可靠,不面向连接、相对 TCP 较快。搞定。
简答:每次建立连接前,客户端和服务端之前都要先进行三次对话才开始正式传输内容,三次对话大概是这样的:
1. 客户端:我要连接你了,可以吗
2. 服务端:嗯,我准备好了,连接我吧
3. 客户端:那我连接你咯。
4. 开始后面步骤
IP 网络协议(英语:Internet Protocol)
只要你在互联网中,那么你就会有一个 IP。通俗上理解,IP 分为「内网 IP」 和「外网 IP」,以下图为例:
你从电信那里买来带宽,一年一千多。
电信为你提供 DNS 服务。
你买了一个路由器,然后用电脑和手机分别连接路由器广播出来的无线 WIFI。
只要路由器连上电信的服务器,那么路由器就会有一个「外网 IP」,比如「14.17.32.211」就是一个外网 IP。这就是你在互联网中的地址。
但是如果你重启路由器,那么你很有「可能」被重新分配一个「外网 IP」,也就是说 你的路由器没有「固定的外网 IP」
你可以花每年几千块钱租用一个「固定的外网 IP」,但是显然不会这么浪费钱。像腾讯、阿里这样的大公司租用了很多外网 IP,这样才能对我们提供稳定的服务。
但是有个问题,你的路由器的外网 IP 如果是14.17.32.211,那么你的手机和电脑的 IP 又是什么呢?答案是「内网 IP」
路由器会在你家里创建一个内网,内网中的设备使用内网 IP,一般来说这个 IP 的格式都是 192.168.xxx.xxx。
一般路由会给自己分配一个好记的内网 IP,如 192.168.1.1
然后路由会给每一个内网中的设备分配一个不同的内网 IP,如电脑是 192.168.1.2,手机是 192.168.1.3,以此类推。
现在路由器有两个 IP,一个外网 IP(14.17.32.211)和一个内网 IP(192.168.1.1)
内网中的设备可以互相访问(比如你可以用电脑或手机进入 http://192.168.1.1 来查看你的路由器),但是不能直接访问外网,内网设备想要访问外网,就必须经过路由器中转。
外网中的设备可以互相访问(比如 qq.com 可以把首页发送给你的路由器,你的路由器有外网 IP),但是外网中的设备无法访问你的内网设备(这很好理解,内网是一个封闭的网络,外人进不来,所以实际上 qq.com 无法直接把首页放送给你的电脑和手机)
问题来了,那 qq.com 是怎么把首页发送到我的手机上的呢?答案是通过路由器来中转。
路由器接收到 qq.com 的页面后,把页面发送给你的电脑或手机。路由器知道如何给这些信息指路,路由器就是一个指路人,这就是「路由」两个字的来历。
路,就是「必由之路」中的路。由,就是「必由之路」中的由(由是经过、缘由的意思)。所有的信息都要经过路由器,然后被指向一条它该去的路。
也就是说内网和外网就像两个隔绝的空间,无法互通,唯一的联通点就是路由器(因为路由器既有外网 IP 也有内网 IP),所以路由器有时候也被叫做「网关」,这个「关」是「一夫当关,万夫莫开」的「关」。如果路由器到电信的连接中断了,那么内网中所有的设备也就无法上网了。(这很好理解,相当于唯一一条出去的路断了)
除了内网 IP 和外网 IP,还有两个特别特殊的 IP,就是本地 IP:127.0.0.1。本地 IP 永远表示设备自己。不信你可以 ping 127.0.0.1 一下,会发现只需要 0.01 ms 就得到了响应(你 ping qq.com 需要几十毫秒才得到响应)
默认情况下,hosts 文件里会有一行127.0.0.1 localhost,意思就是 localhost 指向 127.0.0.1,所以 localhost 也表示设备自己。不信你 ping localhost 试试,会发现实际上是在 ping 127.0.0.1
还有一个特别特殊的 IP:0.0.0.0,它不表示任何设备。这个 IP 不同的地方含义不同,以后我们会用到,用到的时候再说意思。
端口
你想要访问一个设备(前提是你使用的是 TCP 或 UDP 协议。还记得吗,HTTP 就使用了 TCP),只指定 IP 是不够的,还必须指定端口(Port)。
端口其实就是一个编号,并不是一种硬件。
一个服务器(硬件)不一定只提供一种服务,比如一个服务器既提供 HTTP 服务,又提供 FTP 服务,还提供 SMTP 服务(邮件服务),那么只用一个 IP 是无法告诉服务器你想要使用哪种服务。
所以这里有一个重要的原则:一个端口对应一个服务。
比如
要提供 HTTP 服务你最好使用 80 端口(能不能使用别的端口?可以,不过不建议你违反约定)
要提供 HTTPS 服务你最好使用 443 端口(能不能使用别的端口?可以,不过不建议你违反约定)
要提供 FTP 服务你最好使用 21 端口(能不能使用别的端口?可以,不过不建议你违反约定)
问题1:我怎么知道应该使用什么端口?
维基百科 把 0 到 1023 号端口对应的服务都告诉你了,点进去看看吧。
问题2:一共由多少端口?
每个机器一共有 65535(2的16次方减1)个端口(这是协议规定的)。不过这些端口的使用由一些规定
0 到 1023(2的10次方减1)号端口是留给系统使用的,你只有拥有了管理员权限后,才能使用这 1024 个端口。
其他端口可以给普通用户使用
如果一个端口正在提供服务,也就是被占用了,那么就不能再使用这个端口。除非你先停掉正在占用这个端口的服务。以后你们会经常遇到这个问题。
总结
上面都是 TCP/IP 相关的知识,如果你记不住,就记住下面这句话:
使用 HTTP 协议访问另一个 IP 时,必须同时提供 IP 和端口号,缺一不可。
那么问题来了
我访问 http://qq.com 时并没有提供端口号,为什么我依然可以访问
答:因为浏览器帮你加了默认端口号 80。
Node.js 服务器
接收请求
我们的脚本只需要一个文件就可以搞定
新建一个安全的目录,不需要我重复强调吧?
cd ~/Desktop; mkdir node-demo; cd node-demo
touch server.js
编辑 server.js
运行 node server 或者 node server.js,看到报错
根据报错提示调整你的命令
成功之后,这个 server 会保持运行,无法退出
如果你想「中断」这个 server,按 <kb>Ctrl</kbd> + <kbd>C</kbd> 即可(C 就是 Cancel 的意思)
中断后你才能输入其他命令
我建议你把这个 server 放在那里别动,新开一个 Bash 窗口,完成下面的教程
好了服务器完成。只不过
这个服务器目前只有一个功能,那就是打印出路径和查询字符串
还缺少一个重要的功能,那就是发出 HTTP 响应
目前我们先只做一个功能玩玩。
接下来你要发起一个请求到这个服务器。这听起来有点怪异,「我向自己发起请求」,目前是的,因为你买不起服务器啊。
在新的 Bash 窗口运行 curl http://localhost:你的指定的端口/xxx 或者 curl http://127.0.0.1:你指定的端口/xxx。
你会马上发现 server 打印出了路径:
这说明我们的 server 收到了我们用 curl 发出的请求
由于 server 迟迟没有发出响应,所以 curl 就一直等在那里,无法退出(用 <kb>Ctrl</kbd> + <kbd>C</kbd> 中断这个傻 curl)
发出响应
接下来我们让我们 server 发出响应
编辑 server.js
在中间我标注的区域添加两行代码
response.write('Hi')
response.end()
中断之前的 server,重新运行 node server 8888
curl http://127.0.0.1:8888/xxx,结果如下:
Hi%
这个 % 不是我们的内容,% 表示结尾。别再问我了。如果你看 % 不爽,就把 'Hi' 换成 'Hi\n'。
好了,响应添加成功
使用 curl -s -v -- "http://localhost:8888/xxx" 可以查看完整的请求和响应(上节课的内容)
根据请求返回不同的响应
用户请求 / 时,返回 html 内容
该 html 内容里面由一个 css link 和一个 script
css link 会请求 /style.css,返回 css 内容
script 会请求 /main.js,返回 js 内容
请求 / /style.css /main.js 以外的路径,则一律返回 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.css'){
response.setHeader('Content-Type', 'text/css; charset=utf-8')
response.write('body{background-color: #ddd;}h1{color: red;}')
response.end()
}else if(path == '/main.js'){
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write('alert("这是JS执行的")')
response.end()
}else if(path == '/'){
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()
}
/******** 代码结束,下面不要看 ************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)
版权出自饥人谷,禁止转载