3.11
HTTP
get与post
安全:请求不会破坏服务器上的资源
幂等:多次执行相同的操作,结果是相同的
get是获取资源,安全且幂等
post是相反操作,向url指定的资源提交数据,数据放在报文的body中
http优缺点
优点:
简单、灵活易于扩展、应用广泛跨平台
双刃剑:
无状态,好处在于服务器不需要记忆耗费额外资源记录http状态;坏处在于在进行关联性操作时(登录-添加购物车-下单-结算-支付)会非常麻烦
解决方案:cookie,第一次访问是服务器回应cookie,以后带着cookie服务器就认识了
明文传输:好处在于方便阅读、方便调试;坏处在于不太安全
缺点:
不安全:明文传输;不验证通信方身份;无法验证报文完整性;
http1.1
长连接
改善了http1.0的短连接,变为长连接(之前每一次操作都要进行TCP三次握手建立连接)
长连接之后只要一方没有主动断开,就不会断开
即请求头带上connection:keep_alive。或者请求头没有connection:closed字段
管道网络传输
可以同时发出多个请求,以往只有前一个请求得到了响应才可以发出下一个请求,这样能够减少整体的响应时间;但是若是前面请求的响应比较慢,会造成队头堵塞
队头堵塞
只要有一个响应被堵塞了,之后的响应都被堵塞住
解决方案之一:并发长连接,eg:一个请求一个tcp连接
HTTPS
http与https的区别
http明文传输,不太安全;https在tcp和http中加入了SSL/TLS安全协议
http连接相对简单,TCP三次握手;https还需要SSL/TLS握手
HTTP端口号80;https端口号443
HTTPS还需要向CA(证书权威机构)申请数字证书,来保证服务器是可靠的
HTTPS的策略
窃听风险—>混合加密----信息加密
篡改风险----摘要算法-----校验机制
冒充风险----数字证书-----身份证书
混合加密
非对称加密
公钥和私钥,公钥可以任意分发私钥保密,速度慢
通信建立前采用来交换会话密钥,后续采用对称加密
对称加密
只有一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换
通信过程中全部使用对称加密
摘要算法
摘要算法用来实现完整性,能够为数据提供独一无二的指纹
客户端在发送明文之前会算出明文的指纹,将明文和指纹一起发送,服务器在解密后得到明文,用相同的摘要算法得出另一份指纹,与客户端发来的指纹对比,若相同则说明数据是完整的
数字证书
客户端先向服务器索要公钥来发送信息,服务器再用私钥解密
问题来了,如何保证公钥的可行度
—>第三方权威机构(CA),将服务器公钥放在数字证书中,只要证书可信,公钥就是可信的
过程:
(CA公钥事先置入操作系统中)
首先服务器把公钥注册到CA—CA用自己的私钥将服务器公钥数字签名并颁发数字证书----服务器将公钥和数字签名
发送给客户端----客户端拿到服务器的数字证书后使用CA的公钥确认其真实性----使用公钥将信息加密发送给服务器—服务器用私钥解密
HTTPS如何建立连接,期间交互了什么?
SSL/TLS协议基本流程
客户端向服务器索要公钥
双方协商生产会话密钥
采用会话密钥进行加密通信
前两点就是握手过程:
1 客户端发送client hello:第一个随机数;客户端支持的SSL/TLS版本/客户端支持的密码套件列表
2 服务端发送server hello:第二个随机数;数字证书;确认的密码套件列表;确认SSL/TLS版本
3 客户端回应,确认数字证书没有问题后取出公钥:第三个随机数(pre-master key);加密通信算法改变通知,之后使用会话密钥;客户端握手结束,将之前内容做个摘要供服务器检验
4 服务器最后回应,通过加密算法算出会话密钥:加密算法改变通知;服务器握手结束通知,摘要供检验
http1.1的性能瓶颈
1 头部压缩不了,只能压缩body,延迟大
2 头部都一样,浪费
3 队头堵塞
4 只能客户端请求,服务器响应
5 没有请求优先级控制
HTTP2
基于https,比较安全
1 头部压缩
压缩头(header),如果发出多个请求,头部分是一样的,协议会帮你消除重复部分 即HPACK算法
在客户端服务器同时维护一张头信息表,头部信息只需发送索引号,提高速度
2 二进制格式
不同于http1.1纯文本形式的报文,所有信息采用二进制,分为:头信息帧、数据帧,增加效率
3 数据流
数据包不是按顺序发送的,每个请求或回应的数据包,称为数据流(Stream),每个数据流都有编号,客户端编号为奇数,服务器为偶数,并且数据流中还可以指定优先级
4 多路复用
可以在一个连接中并发多个请求或回应,不用按照顺序一一回应,解决了队头堵塞的问题
5 服务器推送
可以在请求之前提前把可能的静态资源发送给客户端,减少等待时间
HTTP2的缺陷
因为他是基于TCP的,如果出现丢包的情况,会触发超时重传机制,那么所有的HTTP请求都会被阻塞
解决方案
HTTP3
QUIC
放弃TCP,采用UCP连接,采用QUIC协议来保证可靠性
QUIC:当某个数据流丢失时,阻塞这个流,不影响其他流
TLS1.3+
QPCK
三次QUIC握手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMMBrqEo-1646920186139)(C:\Users\yzh\AppData\Roaming\Typora\typora-user-images\image-20220310104029059.png)]
QUIC 是⼀个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复⽤的协议!
“https和http相⽐,就是传输的内容多了对称加密,可以这么理解吗?”
参考:小林coding 图解网络
leetcode
[ 删除链表的倒数第 N 个结点]
数数
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int count = 0, cur = 0;
ListNode* dummyhead = new ListNode();
dummyhead->next = head;
auto node = dummyhead;
while(head){
count++;
head = head->next;
}
while(node){
//之前是 = 注意!
if(cur == count - n){
node->next = node->next->next;
break;
}
node = node->next;
cur++;
}
return dummyhead->next;
}
};
递归
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
int count = 0;
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(!head) return head;
head->next = removeNthFromEnd(head->next, n);
count++;
if(count == n) return head->next;
return head;
}
};
双指针
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(!head || !head->next) return nullptr;
auto node = head, node1 = head;
while(n--){
head = head->next;
}
if(!head) return node1->next;
while(head->next){
node = node->next;
head = head->next;
}
node->next = node->next->next;
return node1;
}
};
疑问:没有报错?
63. 不同路径 II - 力扣(LeetCode) (leetcode-cn.com)
计算机系统
硬件结构
冯诺依曼模型:
cpu、内存、输入设备、输出设备、总线
内存
数据存储单位为bit(二进制位),最小的存储单位是字节,8位
CPU:
一次可以计算4个字节—32位(位宽)
一次可以计算8个字节—64位
cpu组成
寄存器
通用寄存器:存放需要运算的数据
程序寄存器:存储下一次程序执行的地址
指令寄存器:存储程序寄存器存储地址指向的程序
运算单元
逻辑单元
总线
地址总线
指定cpu将要操作内存的地址
数据总线
用于读取数据
控制总线
用于发送和接收信号
eg:读取数据,先通过地址总线找到数据,再通过数据总线传输数据
线路位宽和地址位宽
通过操作电压来进行线路传输,高电平表示1,低电平表示0,如果只有一条线路,CPU就只能访问到2个地址
为了提高效率,并行传输,增加线路条数,而不是反复的进行串行传输。
要想访问到4G的地址,线路位宽得有32位, 2^32 = 4G
cpu位宽最好不小于线路位宽,要不然就不太协调,有些浪费
CPU如何运行程序的
1 CPU读取程序寄存器,通过地址总线访问程序地址,然后将程序由数据总线传到cpu,cpu将指令存储在指令寄存器
2cpu分析指令寄存器中的指令,运算就交给运算单元,读写就交给控制单元
3 程序寄存器自增,增量大小看指令大小,由CPU位宽决定,例如32位系统中需要四个字节存储指令,所以程序计数器+4
这个过程称为cpu的指令周期
a = 1 + 2
首先编译器将高级语言代码转码为汇编代码在翻译成计算机认识的机器码
然后1、2被放入数据段,机器指令被放入正文段
编译完成后,cpu取值执行就可以啦
指令大小由cpu位宽决定,数据大小由数据类型决定
指令
不同cpu有不同的指令集,对应着不同的汇编语言和机器码,以MIPS为例
是32位整数,高6位代表操作码表示指令类型,主要有以下三种类型
R:算术逻辑运算,操作码6+3*5位寄存器+位移量5位+功能码6位
I:数据传输、条件分支,操作码6 + 2 * 5位寄存器 + 地址16位
J:跳转, 6位操作码 + 26位地址
cpu执行指令过程
fetch
decode
excute
store
执行过程一般都是运算器处理,但是若是简单的地址跳转,就是控制器的事儿了
指令类型
数据传输、运算类型、跳转类型、信号类型、闲置类型
指令的执行速度
GHz,1GHz代表1秒能产生1G(10亿次)次数的信号
时钟周期:每一次脉冲信号高低电平转换时间
注意一个时钟周期不一定能执行完一条指令
如何让程序跑的更快
程序CPU执行时间 = 时钟周期 * 时钟周期数
时钟周期数 = 每条指令耗费平均时钟周期(CPI) * 指令数
指令数—编译器
时钟周期—厂家硬件设置
每条指令平均周期—cpu流水线技术
硬件的 64 位和 32 位指的是 CPU 的位宽,软件的 64 位和 32 位指的是指令的位宽。
C++
static:改变变量的存储方式和可见性
1 修饰局部变量:作用域没有变,存储位置由栈变为静态区,生命周期由本函数变为整个程序
2 修饰全局变量:作用域改变,由工程可见变为本文件可见
3 修饰函数:同上
4 修饰类:函数属于类而不属于任何一个对象,无法通过对象调用,且无法调用非静态成员变量;成员变量归所有对象所有,非常量只能在类外初始化。
5 静态成员函数不能被virtual修饰
const
1 修饰基本数据类型:前后无所谓
2 修饰指针和引用变量:在*前面代表指针指向的内容变不了—常量指针;在**后面代表指针的指向变不了—指针常量
3 const应用到函数中:const返回值,一般和引用一起使用,不能做左值;const修饰实参;const成员函数,const常量只能调用const成员函数,但是非const变量可以调用const成员函数
4 const在类中用法:const成员变量只能在构造函数初始化列表初始化,const和static不能同时修饰成员函数
如果成员变量恒不变,用const+static修饰;如果const成员函数想修改成员变量,用mutable修饰变量
C++常量存储的位置
对于局部常量,放在栈
对于全局常量,放在符号表
对于字面值常量,字符串等,放在常量区
重写、重载、重定义的区别
重写:virtual
重载:具有不同参数列表的同名函数
重定义:派生类重新定义父类的非虚函数
各种构造函数
无参构造函数
一般构造函数
**拷贝构造函数:当类中右指针或者手动分配了内存,自己写更好
类型转换构造函数:不默认转换的话,使用explicit关键字
赋值运算符重载:等号左右两边必须都已经初始化
其他
lx值没变是因为精度丢失,数组内是double,传入的时候误写为int了
雷诺数越大,入口努塞尔数越小
弛豫时间不变增加分辨率,减少空间步长,会造成压缩?密度变大,是否应该对时间步长也进行处理
函数初始化列表初始化,const和static不能同时修饰成员函数
如果成员变量恒不变,用const+static修饰;如果const成员函数想修改成员变量,用mutable修饰变量
C++常量存储的位置
对于局部常量,放在栈
对于全局常量,放在符号表
对于字面值常量,字符串等,放在常量区
重写、重载、重定义的区别
重写:virtual
重载:具有不同参数列表的同名函数
重定义:派生类重新定义父类的非虚函数
各种构造函数
无参构造函数
一般构造函数
**拷贝构造函数:当类中右指针或者手动分配了内存,自己写更好
类型转换构造函数:不默认转换的话,使用explicit关键字
赋值运算符重载:等号左右两边必须都已经初始化
其他
lx值没变是因为精度丢失,数组内是double,传入的时候误写为int了
雷诺数越大,入口努塞尔数越小
弛豫时间不变增加分辨率,减少空间步长,会造成压缩?密度变大,是否应该对时间步长也进行处理