Node 网络详解
1、 OSI & TCP/IP模型设计
1.1 OSI七层模型
应用层:(Application Layer)
- 功能是直接向用户提供服务,完成用户希望在网络上完成的各种工作
- 文件服务、目录服务、文件传输服务(FTP)、远程登录服务(Telnet)、电子邮件服务(E-mail)、打印服务、安全服务、网络管理服务、数据库服务、DNS服务etc
- 在应用层交互的数据单元称为报文
表示层:(Presentation Layer)
- 对来自应用层的命令和数据进行解释,对各种语法赋予相应的含义,并按照一定的格式传送给会话层。其主要功能是“处理用户信息的表示问题,如编码、数据格式转换和加密解密”
会话层:(Session Layer)
- 向两个实体的表示层提供建立和使用连接的方法。将不同实体之间的表示层的连接称为会话。因此会话层的任务就是组织和协调两个会话进程之间的通信,并对数据交换进行管理
传输层:(Transport Layer)
- 以下三层:数据通信,以上三层:数据处理,是通信子网和资源子网的接口和桥梁,起到承上启下的作用
- 向用户提供可靠的端到端的差错和流量控制,保证报文的正确传输,向高层屏蔽下层数据通信的细节,即向用户透明地传送报文,常见的协议:TCP、UDP
网络层:(Network Layer)
- 通过路由选择算法,为报文或分组通过通信子网选择最适当的路径
- 数据链路层的数据在这一层被转换为数据包,然后通过路径选择、分段组合、顺序、进/出路由等控制,将信息从一个网络设备传送到另一个网络设备
数据链路层:(Data Link Layer)
- 负责建立和管理节点间的链路
- 通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路
- 接受物理层的数据,封装成帧传给上层,接受上层数据解析成比特数据换发给物理层
物理层:(Physical Layer)
- 实现计算机节点之间比特流的透明传送
- 物理层的主要任务描述为确定与传输媒体的接口的一些特性,提供用于建立、保持和断开物理连接的机械的、电气的、功能的和过程的条件,也就是说物理层提供有关同步和比特流在物理媒体上的传输手段。
- 是物理硬件上的底层能力,比如光缆、电缆等设备连接形成组网
- 包括信号的调制及信道复用等
1.2 TCP/IP协议
应用层
- 大多数普通与网络相关的程序为了通过网络与其他程序通信所使用的层。这个层的处理过程是应用特有的;数据从网络相关的程序以这种应用内部使用的格式进行传送,然后被编码成标准协议的格式;
- 应用层负责处理特定的应用程序细节。包括Telnet(远程登录)、FTP(文件传输协议)、SMTP(简单邮件传送协议)以及SNMP(简单网络管理协议)等;
传输层
- 两台主机上的应用程序提供端到端的通信,有2种传输协议:TCP(传输控制协议)和UDP(用户数据报协议);
网络层
- 处理分组在网络中的活动,例如分组的选路。网络层协议包括IP协议(网际协议)、ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议);
网络接口层
- 也称作数据链路层,包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。一起处理与电缆(或其他任何传输媒介)的物理接口细节;
1.3 OSI与TCP/IP协议间的关系
- OSI引入了服务、接口、协议、分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型;
- OSI先有模型,后有协议,先有标准,后进行实践;而TCP/IP则相反,先有协议和应用再提出了模型,且是参照的OSI模型;
- OSI是一种理论下的模型,而TCP/IP已被广泛使用,成为网络互联事实上的标准。
1.3 每层间的合作与加工
Q:以下两种内容分别属于哪层模型
html:应用层
路由器:网络层
2、TCP&UDP
2.1 TCP、UDP区别
- TCP(Transmission Control Protocol),传输控制协议,是一种可靠、面向字节流的通信协议,把上面应用层交下来的数据看成无结构的字节流来发送,需要20个字节
- UDP(User Datagram Protocol),用户数据包协议,是一个简单的面向数据报的通信协议,只是在其上面加上首部后就交给了下面的网络层,只占用8个字节(64bit)
Q:直播属于哪种协议? - 直播使用的RTMP、HLS都是基于TCP的;
- 不是所有流媒体通信都是UDP;
2.2 TCP的三次握手、四次挥手
TCP三次握手
为了保证客户端和服务器端的可靠连接,TCP建立连接时必须要进行三次会话,也叫TCP三次握手,进行三次握手的目的是为了确认双方的接收能力和发送能力是否正常。
举个栗子
公安局长王哥 和 陈某打电话
公安局:你好!陈某,听得到吗?(一次会话)
陈某:听到了,王哥,你能听到吗 (二次会话)
公安局:听到了,你过来自首吧 (开始会话)(三次会话)
通过这个例子我们可以知道三次会话的目的就是为了确保双方的连接正常,同理,TCP三次握手也是这个过程,下面用图文形式来解释一下TCP三次握手。
TCP建立连接过程
最开始的时候客户端和服务器都是处于CLOSED关闭状态。主动打开连接的为客户端,被动打开连接的是服务器。
TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了 LISTEN 监听状态
第一次握手 TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT 同步已发送状态
第二次握手 TCP服务器收到请求报文后,如果同意连接,则会向客户端发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了 SYN-RCVD 同步收到状态
第三次握手 TCP客户端收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED已建立连接状态 触发三次握手
有人可能会很疑惑为什么要进行第三次握手?
主要原因:防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误
第一次握手: 客户端向服务器端发送报文
证明客户端的发送能力正常
第二次握手:服务器端接收到报文并向客户端发送报文
证明服务器端的接收能力、发送能力正常
第三次握手:客户端向服务器发送报文
证明客户端的接收能力正常
TCP四次挥手
建立TCP连接需要三次握手,终止TCP连接需要四次挥手
举个例子
张三和李四的对话
张三:好的,那我先走了
李四:好的,那你走吧
李四:那我也走了?
张三:好的,你走吧
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
第一次挥手 客户端发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态
第二次挥手 服务器端接收到连接释放报文后,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT 关闭等待状态
第三次挥手 客户端接收到服务器端的确认请求后,**客户端就会进入FIN-WAIT-2(终止等待2)**状态,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
第四次挥手 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态,但此时TCP连接还未终止,必须要经过2MSL后(最长报文寿命),当客户端撤销相应的TCB后,客户端才会进入CLOSED关闭状态,服务器端接收到确认报文后,会立即进入CLOSED关闭状态,到这里TCP连接就断开了,四次挥手完成
为什么客户端要等待2MSL?
主要原因是为了保证客户端发送那个的第一个ACK报文能到到服务器,因为这个ACK报文可能丢失,并且2MSL是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃,这样新的连接中不会出现旧连接的请求报文。
Q:
1. TCP是否每次连接都需要3次握手?
- TCP快速打开(TCP Fast Open,TFO)
- 第一次握手:
- SYN在fast open带上cookie
- 服务端产生cookie,在ACK返回改cookie给客户端
- 客户端存储cookie
以后: - 客户端带有cookie
- 服务端验证该cookie,有效,在返回ACK的同时,将数据直接返回给应用层,无效,3次握手
- 少一次ACK的RTT(round-trip time,往返时延)
2. 为什么最后要等2个MSL(Maximum Segment Lifetime,最长报文寿命)
- 确保接收方收到ACK;
- 确保没有新的报文到发送方
3、HTTP
3.1 HTTP组件系统
代理(Proxies)
在浏览器和服务器之间,有许多计算机和其他设备转发了 HTTP 消息。由于 Web 栈层次结构的原因,它们大多都出现在传输层、网络层和物理层上,对于 HTTP 应用层而言就是透明的,虽然它们可能会对应用层性能有重要影响。还有一部分是表现在应用层上的,被称为代理(Proxies)。代理(Proxies)既可以表现得透明,又可以不透明(“改变请求”会通过它们)。代理主要有如下几种作用:
- 缓存(可以是公开的也可以是私有的,像浏览器的缓存)
- 过滤(像反病毒扫描,家长控制…)
- 负载均衡(让多个服务器服务不同的请求)
- 认证(对不同资源进行权限管理)
- 日志记录(允许存储历史信息)
3.2 HTTP特点
HTTP 本质是无状态的,使用 Cookies 可以创建有状态的会话。
- HTTP是无状态的,有会话的
HTTP 是无状态的:在同一个连接中,两个执行成功的请求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品。而使用 HTTP 的头部扩展,HTTP Cookies 就可以解决这个问题。把 Cookies 添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态。
3.3 状态码
- 1XX:
- 100 continue
- 101 http ->websocket 请求服务器切换更高的协议
- 2XX:
- 200 ok
- 204 只有响应头无body
- 206: partial content HTTP1.1 断点续传、分块下载
- 3XX:
- 301 永久重定向
- 302 暂时重定向
- 304 缓存
- 4XX:
- 400 bad request 通用
- 403 禁止访问
- 404 无资源
- 405 method not allowed
- 5XX:
- 500 服务器错误
- 501 不支持客户端请求
- 502 bad gateway 代理服务器异常
- 503 太多,无法响应
3.4 HTTP 攻击
SQL:
- 通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中
- 处理:对用户的输入进行转义
XSS:(Cross-site scripting)
- 存储型 XSS
- 注入型脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器上传回并执行。
- 反射型 XSS
- 当用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。Web 服务器将注入脚本,比如一个错误信息,搜索结果等 返回到用户的浏览器上。由于浏览器认为这个响应来自"可信任"的服务器,所以会执行这段脚本。
- 基于 DOM 的 XSS
- 通过修改原始的客户端代码,受害者浏览器的 DOM 环境改变,导致有效载荷的执行。也就是说,页面本身并没有变化,但由于 DOM 环境被恶意修改,有客户端代码被包含进了页面,并且意外执行。
- 处理:对用户的输入进行转义
CSRF:(Cross-site request forgery)跨站请求伪造
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
- 一个典型的CSRF攻击有着如下的流程:
- 受害者登录a.com,并保留了登录凭证(Cookie)。
- 攻击者引诱受害者访问了b.com。
- b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
- a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
- a.com以受害者的名义执行了act=xx。
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
- 处理:cookie samesite
4、 websocket
WebSockets 是一种先进的技术(H5开始支持)。它可以在用户的浏览器和服务器之间打开交互式通信会话。使用此 API,可以从客户端向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。
- websocket-client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<!-- 引用Socket.io的客户端js文件,由于Socket.io已在服务端监听了HTTP服务器的请求,一旦收到对该文件的请求,则会自动返回该文件,不需要开发人员配置。 -->
<!-- 该文件在服务端的位置为/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js -->
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
<script>
// 与服务器建立WebSocket连接,该连接为ws协议,socket.io不需要担心跨域问题。
const socket = io.connect('ws://localhost:8080/');
// 根据事件名,向服务端发送数据,数据数量不限。
socket.emit('msg', '客户端向服务端发送数据第一条', '客户端向服务端发送数据第二条');
// 根据事件名接收服务端返回的数据
socket.on('msg', (...msgs) => {
console.log(msgs);
});
// 接收服务端通过计时器发送来的数据
socket.on('timer', time => {
console.log(time);
});
</script>
</body>
</html>
- websocket-server.js
const http = require('http');
const io = require('socket.io');
// 1. 建立HTTP服务器。
const server = http.createServer((req, res) => {});
server.listen(8080);
// 2. 建立WebSocket,让socket.io监听HTTP服务器,一旦发现是WebSocket请求,则会自动进行处理。
const ws = io.listen(server);
// 建立连接完成后,触发connection事件。
// 该事件会返回一个socket对象(https://socket.io/docs/server-api/#Socket),可以利用socket对象进行发送、接收数据操作。
ws.on('connection', socket => {
// 根据事件名,向客户端发送数据,数据数量不限。
socket.emit('msg', '服务端向客户端发送数据第一条', '服务端向客户端发送数据第二条');
// 根据事件名接收客户端返回的数据
socket.on('msg', (...msgs) => {
console.log(msgs);
});
// 使用计时器向客户端发送数据
setInterval(() => {
socket.emit('timer', new Date().getTime());
}, 500);
});