3.23
格子量也要无量纲化
计网(参考:小林coding)
TCP断开连接 4次握手
客户端主动断开连接,发送FIN标志位为1的TCP报文,进入fin_wait_1状态,
服务器收到FIN报文,向客户端发送ACK报文,进入CLOSED_WAIT状态,
客户端收到ACK报文,进入FIN_WAIT_2状态
等待服务器处理完数据后,向客户端发送FIN报文,进入LAST_ACK状态
客户端收到FIN报文,回一个ACK报文,进入TIME_WAIT状态
服务器收到ACK报文,进入CLOSED状态,至此服务器断开连接
客户端在经过2MSL一段时间后,自动进入CLOSED状态,至此客户端也实现了连接的关闭
主动关闭连接的一方,才有TIME_WAIT状态
为什么是四次
客户端发送FIN报文,表明俺不想请求数据了,但是俺还能接受数据,服务端接收响应先回了一个ACK报文,就是说好了我知道你不想干了,等等我处理一下相关事宜再一起答复给你,嘀嘀嘀,好了我处理好了把一些数据发给你
即等到服务器不想发送数据时才发送FIN报文
总而言之服务器需要等待数据的发送与处理
为什么需要TIME_WAIT
1 防止重新建立连接时接收到旧的TCP报文
可能结果:收到以前的数据,造成数据紊乱
2 防止服务器没有收到ACK报文
请求建立连接时,收到RST拒绝报文
为什么TIME_WAIT需要 2 MSL(Message Segment Lifetime)
TCP基于IP,IP有一个TTL(TIME TO LIVE路由最大转发次数),MSL的值大于等于TTL,确保没收到的报文能够自然消亡。
合理的解释是一来一回需要两个报文生存时间,例如被动方没有收到ACK报文会重发FIN报文,一来一回正好2个MSL
LINUX系统中默认的是30s,可以在内核代码中更改改时间,但需要重新编译
TIME_WAIT过长的危害
1 占用内存资源
服务器监听端口,将监听到的连接丢给线程池,如果有大量TIME_WAIT时,系统资源会被占满
2 占用端口资源, 客户端有固定的端口数,被占满会导致无法建立新的连接
如何优化TIME_WAIT
三种方式:
打开net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项,复用处于TIME_WAIT的socket为新连接所用
net.ipv4.tcp_max_tw_buckets,设定一个ITME_WAIT最大值,超过这个值将TIME_WAIT重置,危险!
程序中使⽤ SO_LINGER ,应⽤强制使⽤ RST 关闭。
如果已经建立了连接,但是客户端突然出现了故障怎么办
TCP有一个保活机制,tcp中长时间没有数据传输时,保活机制会发送探测报文,
保活机制包含三个参数:保活时间、保活探测的次数、保活探测的间隔
分为以下三种情况:
1 对端正常工作,收到探测报文正常响应,保活时间重置
2 对端崩溃并重启,对端可以响应,但是没有有效的连接信息,会回复一个RST报文,重置TCP连接
3 对端程序崩溃,探测报文石沉大海,再经历了保活探测次数后,该连接被报告已死亡
连接重置?
socket编程
socket是操作系统提供的api(可以进行各种通信方式,可以使用UDP,TCP等多种协议)
套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
针对TCP应该如何socket编程
流程:
服务端、客户端初始化socket,得到文件描述符
服务端调用 bind,将套接字绑定一个IP地址和端口号
服务器调用listen,进行监听
服务器调用accept,等待客户端连接
客户端调用connect,向服务端的地址和端口发起连接请求
服务端accept返回用于传输的socket文件描述符
客户端调用write写入数据,服务端调用read读取数据
客户端断开连接时,会调用close,那么服务端read读取数据的时候,会读到EOF,待处理完数据后,服务端调用close,表示连接关闭
注意,监听的socket和真正用来传数据的socket,是两个socket
listen时候参数backlog的意义
linux内核中会维护两个队列:SYN队列(未完成连接队列,SYN_RECV); Accept队列(已完成连接队列,ESTABLISHED)
应用程序会取出已经完成连接的accept队列里的socket
int listen (int socketfd, int backlog)
socketfd 文件描述符
backlog不同版本不一样:
早期是SYN队列大小,2.2之后就是ACCEPT队列长度
客户端connect返回是在二次握手之后,服务端accept返回是在第三次握手成功之后
用户端调用close后发生了什么
客户端调用close,发送FIN包,进入FIN_WAIT_1
服务器接收到FIN包,TCP协议栈会为FIN包插入⼀个⽂件结束符 EOF 到接收缓冲区中,应⽤程序 可以通过 read 调⽤来感知这个 FIN 包。这个 EOF 会被放在已排队等候的其他已接收的数据之后,这就 意味着服务端需要处理这种异常情况,因为 EOF 表示在该连接上再⽆额外数据到达。此时,服务端进⼊ CLOSE_WAIT 状态; 接着,当处理完数据后,⾃然就会读到 EOF ,于是也调⽤ close 关闭它的套接字,这会使得服务器会发 出⼀个 FIN 包,之后处于 LAST_ACK 状态;
客户端接收到服务端的 FIN 包,并发送 ACK 确认包给服务端,此时客户端将进⼊ TIME_WAIT 状态; 服务端收到 ACK 确认包后,就进⼊了最后的 CLOSE 状态
客户端经过 2MSL 时间之后,也进⼊ CLOSE 状态;
Leetcode
递增子序列
class Solution {
public:
//需要判断是不是递增好家伙
/*可以对数组最后一个数字比较,因为没排序,所以不能用i > index nums[i] == nums[i-1]去重*/
//!path.empty() && nums[i] < path.back()
vector<vector<int>>ans;
vector<int>path;
void bt(vector<int>& nums, int index){
unordered_set<int>set;
if(path.size() > 1){
ans.push_back(path);
}
for(int i = index; i < nums.size(); i++){
if((!path.empty() && nums[i] < path.back()) || set.find(nums[i]) != set.end()) continue;
path.push_back(nums[i]);
set.insert(nums[i]);
bt(nums, i+1);
path.pop_back();
}
}
vector<vector<int>> findSubsequences(vector<int>& nums) {
bt(nums, 0);
return ans;
}
};
全排列
给个数组标记一下
class Solution {
public:
/*忘啦,*/
vector<vector<int>>ans;
vector<int>path;
void bt(vector<int>& nums, vector<bool>& condi){
if(path.size() == nums.size()){
ans.push_back(path);
return;
}
for(int i = 0; i < nums.size(); i++){
if(!condi[i]){
path.push_back(nums[i]);
condi[i] = true;
bt(nums, condi);
path.pop_back();
condi[i] = false;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<bool>condi(nums.size(), false);
bt(nums, condi);
return ans;
}
};
全排列2
两个数组访问版本,效率低
class Solution {
public:
vector<vector<int>>ans;
vector<int>path;
void bt(vector<int>& nums, vector<bool>& condi){
unordered_set<int>uset;
if(path.size() == nums.size()){
ans.push_back(path);
return;
}
for(int i = 0; i < nums.size(); i++){
if(condi[i] || uset.find(nums[i]) != uset.end()){
continue;
}
path.push_back(nums[i]);
uset.insert(nums[i]);
condi[i] = true;
bt(nums, condi);
path.pop_back();
condi[i] = false;
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<bool>condi(nums.size(), false);
bt(nums, condi);
return ans;
}
};
所以不能排序时,用哈希表
能排序时,bool数组就可以啦
改进版:
class Solution {
public:
vector<vector<int>>ans;
vector<int>path;
void bt(vector<int>& nums, vector<bool>& condi){
if(path.size() == nums.size()){
ans.push_back(path);
return;
}
for(int i = 0; i < nums.size(); i++){
if(condi[i] || (i > 0 && nums[i] == nums[i-1] && !condi[i-1])){
continue;
}
path.push_back(nums[i]);
condi[i] = true;
bt(nums, condi);
path.pop_back();
condi[i] = false;
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<bool>condi(nums.size(), false);
bt(nums, condi);
return ans;
}
};
操作系统
进程
运行中的程序,代码写在硬盘里,运行代码时先将他加载到内存中,这和看的讲的内存金字塔结构相呼应
并发,单核交替执行多个程序;并行:多核同时执行多个程序
进程状态:运行态、阻塞态、就绪态
阻塞态不能直接到运行态,就绪态似乎也不能到阻塞态
再加两个:创建态、结束态
阻塞态通常被换出到硬盘,再次运行时再加载到内存
所以又可以加两种了:
阻塞挂起,进程在外存等待事件出现; 就绪挂起:进程在外存,只要进入内存就可以运行
可以通过sleep或ctrl+z命令挂起进程
进程的控制结构
PCB, 进程存在的唯一标识。链表欸,next指针连起来就好了咩
组成
进程基本信息:进程标识符、用户标识符
进程控制和管理信息:进程状态、进程抢占CPU优先级
资源分配清单:虚拟地址空间、所打开的文件列表以及所使用的I/O设备
CPU相关信息:相关寄存器的值
管理
链表:就绪队列、阻塞队列。方便插入和删除
索引:相对麻烦一点
其他
格子单位也需要无量纲化,无量纲化为1、1
思考一下马赫数为什么会随着补偿而改变呢。声速不变,格子速度会变是吧,那么为了和其他尺度相同,是不是应该固定格子速度,这个需要公式推导一下
写小作文哈哈