OSI 七层模型(是理想化网络模型)
- 应用层
- 表示层
- 会话层 实际中 5.6.7 归成应用层
- 传输层 TCP UDP 协议
- 网络层 路由器
- 数据链路层 交换机 网卡
- 物理层 物理设备 网线 光纤
实际五层
- 实际应用中会将会话层、表示层、应用层合并在一起为应用层
- 用户要传递的数据 装包 HTTP DNS
- 传输层 用户要传递很大的数据包 会对大的数据包进行拆分并标明
序号
端口号
用于传输到哪 TCP、UDP段
- 网络层 对数据再次进行包装 ip 地址 如果传输层的数据还是非常大 网络层会再次进行分包 IP 协议
包
- 数据链路层 再次包装成
帧
- 物理层 这两层涉及到的是硬件
五层模型(统称为 TCP/IP 协议 协议簇)
- 能称为协议的都在数据链路层之上,所以在网络层、传输层、应用层才有协议
- 协议的功能就是把数据按照某个规范去封装,再把数据进行传输(协议就是对数据的封装+传输)
- 网络层
- ip 协议 寻址
- ARP 协议 从 ip 地址获取 mac 地址(局域网下)
- 传输层:TCP、UDP
- 应用层: http 超文本传输协议、 DNS 域名解析成 ip 地址、 FTP 文件传输协议
DNS 协议
- DNS 服务器进行域名和对应 ip 地址转换的服务器
IP 协议
- 寻址 通过 ip 地址定位到最终设备
ARP协议
- 怎么通过ip地址找到mac地址的
- ARP协议:目的就是通过ip地址找到mac地址 (在局域网下)
- 会广播给每一个,对应到要找的IP地址会做出响应,将mac地址返回
- ARP会有缓存记录 有记录就不用广播 缓存的是 ip:mac
- 交换机缓存的是 mac:端口
TCP 协议
- 传输控制协议 特点是可靠、传输效率低
- TCP 提供全双工服务,即数据可在同一时间双向传播
- 数据是无序的在网络间传递,接收方需要有一种算法在接收到数据后恢复原有的顺序
- 三次握手
- 我能主动给你打电话吗
- 可以啊 那我也能给你打电话吗
- 可以 建立连接成功
- 四次挥手
- 我们分手吧
- 回复收到分手的信息
- 好吧 分手
- 行 那就到这
- 小结
- TCP 是双工的,所以握手需要三次,保证双方达成一致(建立连接浪费性能)
- 当断开连接时,发送(FIN)时另一方需要马上回复(ACK),但此时可能不能立即关闭(有未发送完的数据,还有一些准备断开的操作),所以等待确认可以关闭时再发送(FIN)
- 滑动窗口
- 窗口大小是以字节为单位
- 建立TCP连接时,接收方会告知自己的窗口可以接收多少字节的数据
- 滑动窗口的目的是实现丢包重传,并且将数据有效的发送给接收方,而且可以知道每次发送多少,从而做到流量控制
- 所以滑动窗口的核心是
控制流量
- TCP会做什么:传输的时候会做一个滑动窗口,不停地在协商滑动窗口的大小,如果值满了就停止传输,不停地发探测包,如果有了可用窗口大小,接着去传输数据。按照顺序,成功接收到一个数据之后滑动窗口会向后移动,并根据协商的结果修改窗口大小
- 慢启动、拥塞避免、快重传、快恢复
- 也是用来做流量控制的
- 慢开始 超时
- 快重传 不等超时 三次确认就立马重发
http
- 发展历程
- http/0.9 传输过程中没有请求头和请求体 内容采用ASCii字符流传输html
- http/1.0 加了请求头和响应头 实现多类型数据传输
- http/1.1
- 持久连接 一个TCP连接上可以传输多个http请求
- 管线化方式 每个域名最多维护6个TCP持久连接 有队头阻塞问题
- 引入客户端cookie机制,安全机制等
- http/2.0
- 采用多路复用机制 一个域名使用一个TCP长链接
- 头部压缩
- 服务端推送
- http/3.0 基于UDP
- 当访问网页,发生了哪些事情
- 客户端发送一个请求 发送的是一个域名 域名会发送给dns服务器
- dns解析出一个ip给到客户端
- http会把数据进行传输,http协议生成针对目标web服务器的http请求报文
- 交给TCP进行传输:为了方便通信,将http请求报文按序号分割成报文段,把每个报文段可靠的传给对方(丢包或者超时就重发)
- (TCP自身没有传输能力,如何传靠的是网络层)ip协议:寻址和路由 找到对方地址,通过mac地址一个个找到下一站,不停中转
- 从对方接收到TCP报文段, 对报文段按照原来的序号进行重组报文
- http解析传递过来的数据,对内容进行处理
- 处理完成之后再增加一些响应信息,同样利用TCP/IP通信协议向用户进行回传
HTTP是不保存状态的协议, 使用cookie来管理状态
为了防止每次请求都进行tcp链接的建立和断开, 采用保持链接的方式
keep-alive
以前发送请求后需要等待并收到响应,才能发下一个,现在都是管线化方式
http应用
- http也是封装了一些信息和传输
curl -v www.baidu.com
- http分为两部分 (发送请求 client)req -> (接收请求 server)res
- 请求分为三部分(三部分都可以传输数据)
- 请求行 通过方法 路径(传输的数据有限制 url大小限制)
- 如 GET / 版本号
- 请求头 放一些自定义信息 约定的信息 请求头不要过大
- 请求体 传输的数据
- 请求行 通过方法 路径(传输的数据有限制 url大小限制)
- 响应也分为三部分
- 响应行
- 如 版本号 200 ok
- 响应头
- 响应体
- 响应行
- restFul风格
- GET/POST/PUT/SELETE/OPTIONS
- OPTIONS请求代表的是跨域访问时可能会出现 预检请求 试探请求 如果对方确认后 可以发真实的请求
- OPTIONS请求只在复杂请求的状态下才能发送(get、post都是简单请求,如果增加了自定义header那么就是复杂请求)
- OPTIONS可以定义发送的时间间隔
- 常见的状态码
- 可以自己设定 但是浏览器和服务器之间是有一些约定的
- 1xx 服务器收到了信息 等到浏览器后续要做的事 websocket
- 2xx 成功
- 3xx 重定向 缓存
- 4xx 客户端出错 (浏览器参数、或者服务器无法解析客户端参数)
- 5xx 服务器错误
- 200 请求成功
- 204 请求成功 但是无响应内容
- 206部分内容 分段请求
- 301 302 永久重定向和临时重定向
- 304缓存
- 400 客户端请求错误
- 401 权限问题 当前用户没登录 无权限观看
- 403 登陆了 但是还是没有权限
- 404 找不到
- 405 服务器只支持get、post 但是发送了put请求 服务器就会响应找不到此方法
- 500 请求服务解析出错了 无法完成响应
- 502 服务期收到的内容无效
- 503 负载均衡挂了
const http = require("http");
// 下面两种写法是等价的
const server = http.createServer(function(req,res) {
console.log("request")
});
server.on('request', function(req,res) {
console.log("request")
});
let port = 3000;
server.listen(3000, function() {
console.log('server listen on 3000')
});
// 监听错误
server.on("error", function(err){
if(err.code === 'EADDRINUSE') { // 说明端口号被占用
server.listen(++port);// 不用再写回调 监听成功之后会走到上面对应的回调,打印server listen on 3000
}
})
复制代码
- http模块对req、res的封装
const server = http.createServer(function(req, res) {
// req是一个可读流
// 请求行
console.log(req.method); //大写的
console.log(req.url);
console.log(req.httpVersion);
// 请求头
console.log(req.headers); // 统一node处理后全部都是小写
// 请求体
const arr = [];
req.on("data", function(data) {
console.log(data);
arr.push(data);
});
req.on("end", function(){
const data = Buffer.concat(arr).toString();
console.log("end", data);
})
})
const server = http.createServer(function(req, res) {
// res 是一个可写流
res.statusCode = 202;
res.statusMessage = "my 202";
res.setHeader("token", "ok");
res.write("1");
res.end("2");
});
复制代码
实现静态服务
const http = require("http");
// url组成: 协议 ://(用户名:密码)域名:端口号/资源路径?查询参数#hash
const url = require("url");
// 参数加上true 将query转成对象形式
// const {pathname,query} = url.parse("http://username:password@www.zz.com:3000/xxx?a=1#hash", true);
// pathname => /xxx
const mime = require("mime");
const server = http.createServer((req,res) => {
const {pathname} = url.parse(req.url, true);
// 根据路径来读取文件 /public/index.html
const filePath = path.join(__dirname, pathname); // 获取绝对路径
fs.readFile(filePath, function(err, data) {
if(err) {
res.statusCode = 404;
return res.end('not found');
}
// 直接返回 浏览器可以正常展示 是因为在html中设置了 <meta charset="UTF-8">
// 如果浏览器不给编码 浏览器显示就会乱码
// 所以浏览器有可能不加编码 服务器在返回数据的时候就需要添加编码格式
res.setHeader("Content-Type", mime.getType(filePath) + ";charset=utf8");
res.end(data);
})
});
server.listen(3000);
复制代码
- 写一个静态服务器
- 官方:npm i http-server -g 可以在本地启动一个服务
- 使用:命令行 hs 或者 http-server 默认会找public文件夹
// package.json
{
"name": "zhuhaha-server-listen",
"version":"1.0.0",
"description": "",
"main":"1.http.js",
"bin":{
"zsl": "./bin/www"
}
}
/*
这个包默认是无法使用的,要把它放到全局下进行测试
使用 npm link
会生成软链 一个叫做 zhuhaha-server-listen 一个叫 zsl
*/
www文件中:
#!/usr/bin/env node
重新 npm link --fore
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END