TCP/IP 与 Node.js服务器

TCP/IP 是供已连接因特网的计算机进行通信的通信协议。
HTTP 协议的底层其实是由 TCP 协议(规定如何传输)和 IP 协议(规定如何连网)(简称 TCP/IP协议)构建的。
TCP和UDP的区别
三次握手和四次挥手
用node.js做服务器,并发出http响应

资料:菜鸟教程 - 学习TCP/IP

TCP 传输控制协议(Transmission Control Protocol)

TCP面试题

问:TCP 和 UDP 的区别是什么
简答:TCP 可靠(能够知道发送的请求是否成功)、面向连接、相对 UDP 较慢;UDP 不可靠,不面向连接、相对 TCP 较快。 补充:

  • 什么叫面向连接呢?事先为所发送的数据开辟出连接好的通道,然后再进行数据发送。【像打电话,只能两人打,第三人打就显示占线。】
  • 非面向连接:是指通信双方不需要事先建立一条通信线路,而是把每个带有目的地址的包(报文分组)送到线路上,由系统自主选定路线进行传输。【就像写信,不管对方有多忙,把信放到邮筒,就与自己无关系了。】

问:TCP 的三次握手指的是什么
简答:每次建立连接前,客户端和服务端之前都要先进行三次对话,成功后才开始正式传输内容,三次对话大概是这样的:

  1. 客户端:我要连接你了,可以吗(浏览器向服务器发送建立连接的请求。)
  2. 服务端:嗯,我准备好了,连接我吧(服务器接收到浏览器发送的请求后,向浏览器发送同意连接的信号。)
  3. 客户端:那我真的连接你咯。(浏览器接受到服务器发出的同意连接的信号后,再次向服务器发出确认连接的信号。)
  4. 三次握手完成,客户端和服务端成功的建立TCP连接,就可以开始传输数据了。(客户端发送http请求的4个部分给服务端,服务端接收请求并处理后,发送http响应的4个部分给客户端。客户端接收响应)

这是因为,TCP要建立连接,必须确认4件事

  1. 客户端可以发请求
  2. 服务端可以收请求,服务端可以发请求
  3. 客户端可以收请求

问:TCP 的四次挥手指的是什么
答:

  1. 浏览器向服务器发送一个断开连接的请求(你把我断开吧);
  2. 服务器接到请求后发送确认收到请求的信号(知道了);
  3. 服务器向浏览器发送断开通知(那我把你断开咯?);
  4. 浏览器接到断开通知后断开连接并反馈一个确认信号(嗯,你断吧),服务器收到确认信号后断开连接

为什么服务器在接到断开请求时不立即同意断开:当服务器收到断开连接的请求时,可能仍然有数据未发送完毕,所以服务器先发送确认信号,等所有数据发送完毕后再同意断开。

IP 网络协议(英语:Internet Protocol)

IP 分为「内网 IP」 和「外网 IP」

  • 路由器有两个 IP,一个外网 IP(每次重启路由器后会重新分配一个新的外网IP)和一个内网 IP(192.168.1.1)
  • 127.0.0.1 localhost 本地ip,表示设备自己
  • 还有一个特别特殊的 IP:0.0.0.0,它不表示任何设备。

端口(Port)

如果一个服务器既提供 HTTP 服务,又提供 FTP 服务,还提供 SMTP 服务(邮件服务),那么只用一个 IP 是无法告诉服务器你想要使用哪种服务。

所以这里有一个重要的原则:一个端口对应一个服务。

  1. 要提供 HTTP 服务使用 80 端口

    1
    2
    访问https://www.baidu.com实际上是访问https://www.baidu.com:80
    浏览器帮你加了默认端口号 80。
  2. 要提供 HTTPS 服务使用 443 端口

  3. 要提供 FTP 服务使用 21 端口

总结:使用 HTTP 协议访问另一个 IP 时,必须同时提供 IP 和端口号,缺一不可。

用Node.js创建一个服务器

接收请求

我们的脚本只需要一个文件就可以搞定

  1. 新建一个目录cd ~/Desktop; mkdir node-demo; cd node-demo
  • touch server.js
  • 编辑 server.js,内容我已经上传到 GitHub
  • 或者curl https://raw.githubusercontent.com/FrankFang/nodejs-test/7f1a0ce15c47a6c2c938fe322f042e5d62bc7d01/server.js > ./node-demo/server.js将代码下载到server.js文件内
  • 运行 node server.js,看到报错
  • 根据报错提示调整你的命令
  • 成功之后,这个 server 会保持运行,持续监听,无法退出
  • 如果你想「中断」这个 server,按 Ctrl + C 即可(C 就是 Cancel 的意思)
  • 中断后你才能输入其他命令
  • 我建议你把这个 server 放在那里别动,新开一个 Bash 窗口,完成下面的教程

curl -s -v -- http://qq.com/xxx?name=ff,其中查询参数是?name=ff,查询参数是包含问号的

好了服务器完成。只不过

  • 这个服务器目前只有一个功能,那就是打印出路径和查询字符串
  • 还缺少一个重要的功能,那就是发出 HTTP 响应

目前我们先只做一个功能玩玩。

接下来你要发起一个请求到这个服务器。这听起来有点怪异,「我向自己发起请求」,目前是的,因为你买不起服务器啊。

在新的 Bash 窗口运行 curl http://localhost:你的指定的端口/xxx?name=yyyy 或者 curl http://127.0.0.1:你指定的端口/xxx?name=yyyy

你会马上发现 server 打印出了路径:

这说明我们的 server 收到了我们用 curl 发出的请求
由于 server 迟迟没有发出响应,所以 curl 就一直等在那里,无法退出(用 Ctrl + C 中断这个傻 curl)

发出响应

接下来我们让我们 server 发出响应

  1. 编辑 server.js
  2. 在中间我标注的区域添加两行代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    console.log('方方说:得到 HTTP 路径\n' + path)
    if (path == '/') {
    response.write('Hi\n')
    } else if (path == '/index') {
    response.setHeader('Content-Type', 'text/html; charset=utf-8')
    response.write('<!DOCTYPE html>\n<head><body><h1>你好</h1></body></head>')
    } else {
    response.statusCode = 404
    }
    response.end()
  3. Ctrl+C 中断之前的 server,重新运行 node server.js 8888

  4. curl http://127.0.0.1:8888/xxx,结果如下:
    1
    Hi%

这个 % 不是我们的内容,% 表示结尾。

  1. 好了,响应添加成功
  2. 使用 curl -s -v -- "http://localhost:8888/xxx" 可以查看完整的请求和响应

根据请求返回不同的响应

  • 响应 /
  • 响应 /xxx
  • 响应 404
  • 响应 /xxx.html
  • 响应 /xxx.frank
  • 再次强调,后缀是废话。文件内容是有 HTTP 头中的 Content-Type 保证的
  • 响应 /xxx.css
  • 响应 /xxx.js
  • HTTP 路径不是文件路径!!!/xxx.html 不一定对应 xxx.html 文件

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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;}')
} else if (path == '/main.js') {
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write('alert("这是js执行的")')
} else if (path == '/') {
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.write('<!DOCTYPE html>\n<html>'+
'<head><link rel="stylesheet" href="/style.css"><body>'+
'<h1>你好</h1>'+'<script src="/main.js"></script></body></head></html>')
} else {
response.statusCode = 404
}
response.end()
/******** 代码结束,下面不要看 ************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)

-------------本文结束感谢您的阅读-------------