aaronphy / aaronphy.github.io Goto Github PK
View Code? Open in Web Editor NEWBlog
Blog
case-sensitive-paths-webpack-plugin
friendly-errors-webpack-plugin
terser-webpack-plugin
老婆想吃千岛湖鱼头,于是决定这次清明节去千岛湖,其实去年团队outing去过一次,行程安排的不是很好,很多优质的景点没有玩到,所以决定再去一次,列了几个点一定要去:
等待我的美食与照片吧
更新
脚崴了,居然骨折我擦····,千岛湖之游估计只能到我脚好了,估计4月下旬了~
文章
OAuth 2.0 的四种方式
React Fiber架构
jquery一样玩转AST
React SSR
ES6应该知道的东西
React Fiber
5分钟理解Set
antd upload 后端处理
深入理解ESM
工具
GASP中文文档
渐变色站点
mac重置电源
微信openAPI
react fast fresh
webpack build less or scss without require them in javascript
rive web版本 完全用flutter构建 真的NB
代码重构件
问答
github upload file with nodejs
hooks FAQ
using-interfaces-in-generic-interfaces
Ctrl+b 或左箭bai头du键 左移一个字符(移zhi至前一个字符)
Ctrl+f 或右dao箭头键 右移一个字符(移至后一个字符)
Ctrl+a 移至行首
Ctrl+e 移至行尾
Esc b 左移一个单词
Esc f 右移一个单词
Del 删除光标所在处的字符
Ctrl+d 删除光标所在处的字符
BACKSPACE或Ctrl+h 删除光标左边的字符
Ctrl+k 删除至行尾
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
“要么匹配字符,要么匹配位置”--至理名言
let reg = /abc/;
let reg = new RegExp('abc');
正则表达式也是对象: RegExp 有 exec 以及 test方法。
当然还有就是字符串的一些方法也可以使用正则表达式比如:match,matchAll,replace,search以及split。
方法 | 描述 |
---|---|
exec | 一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回 null) |
test | 一个在字符串中测试是否匹配的RegExp方法,它返回 true 或 false。 |
match | 一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null。 |
matchAll | 一个在字符串中执行查找所有匹配的String方法,它返回一个迭代器(iterator)。 |
search | 一个在字符串中测试匹配的String方法,它返回匹配到的位置索引,或者在失败时返回-1。 |
replace | 一个在字符串中执行查找匹配的String方法,并且使用替换字符串替换掉匹配到的子字符串。 |
split | 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法。 |
主要是用量词来实现:
/ab{2,5}c/
{m,n} :连续出现,最少m次,最多n次
量词 | 含义 |
---|---|
{m,} | 至少出现m次 |
{m} | 出现m次 |
? | 有或者没有,等价于{0,1} |
+ | 至少出现1次,等价于{1,} |
* | 出现任意次,等价于{0,} |
主要是使用字符组来实现:
/a[134]b/
[xyz] : x,y,z中任何一个
需要注意的虽然叫字符组,但是匹配的只是一个字符
字符组 | 含义 |
---|---|
\d | [0-9] 表示一位数字 |
\D | [^0-9]表示除数字意外的任意字符 |
\w | [0-9a-zA-Z_]表示数组字母下划线 |
\W | [^0-9a-zA-Z_]表示非数字字母下划线 |
\s | [\t\v\b\r\f]表示空白符,包括空格,制表,换行,回车,换页 |
\S | [^\t\v\b\r\f]表示非空白符 |
. | [^\n\r\u2028\u2029]通配符,但换行,回车,行分隔,段分隔除外 |
有钱可以上黑曜版本,的确很好看
预算再高点,可以上ES300H, 混动的很省油,ES200h 好像没有了
乞丐版本估计没有货,没必要上quattro
有混动版本,贯穿式车尾,乳白色很不错,稍微显沉稳点
优惠很多,可以看看,油耗惊人
新能源版本可以考虑下
有款蓝色很好看,坐着炒鸡舒服,隔音好,视野也不错
内饰无敌,隔音不错
后排空间没有想象大,不过sportback蓝色很好看,操控很好
独一无二的存在,操控以及安全性,空间也很大
太帅了,非承载车身,开过,很不错,国产之光
丰田神车之一
银色真的好看
const withLoading = <T extends {}>(Component:React.ComponentType<T>)=>{
const Loading:React.FC<T = (props:T)=>{
const [loading,setLoading] = React.useState(false)
return (
<>
{loading? <div>loading...</div>
: <Component {...props} visible={(e)=>setLoading(e)}>
}
<>
)
}
}
函数组件使用
const XXXHOC = withLoading(XXX)
类组件使用
const DecoratorWithLoading:<T extends {}>(Component:ComponentType<T>) => any = withLoading;
@DecoratorWithLoading
class XXX extends React.Component{}
const arr = ['a','b','c'];
const tuple = (...args:T)=>args;
const tupleArr = tuple('a','b','c');
type Type = {
[k in (typeof tupleArr)[number]]:string
}
type Type = {a:string,b:string,c:string}
原来的各种指标, 很晦涩哦~
名称 | 含义 |
---|---|
FP | First Paint ,第一个像素被绘制在屏幕上的时刻 |
FCP | First Contentful Paint ,第一个内容被绘制在屏幕上的时刻 |
FMP | First Meaningful Paint ,主要内容被绘制在屏幕上的时刻 |
SI | Speed Index 页面可见内容填充速度 |
LCP | Largest Contentful Paint 最大元素被绘制的时刻 |
FCI | First CPU Idle 第一次CPU空闲可处理IO时刻 |
TTI | Time To Interactive 可稳定交互的时刻 |
FID | First Input Delay 代表用户第一次交互处理消耗时间 |
根据RAIL模型来收集对应的指标:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// `name` will be either 'first-paint' or 'first-contentful-paint'.
const metricName = entry.name;
const time = Math.round(entry.startTime + entry.duration);
//发送数据到后台
request.post({
eventCategory:'Performance Metrics',
eventAction: metricName,
eventValue: time,
nonInteraction: true,
})
}
});
observer.observe({entryTypes: ['paint']});
更多的是标记元素的方法来去监听
import ttiPolyfill from 'tti-polyfill';
ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
request.post({
eventCategory:'Performance Metrics',
eventAction:'TTI',
eventValue: tti,
nonInteraction: true,
});
});
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
request.post({
eventCategory:'Performance Metrics',
eventAction: 'longtask',
eventValue:Math.round(entry.startTime + entry.duration),
eventLabel:JSON.stringify(entry.attribution),
});
}
});
observer.observe({entryTypes: ['longtask']});
由于LCP 关心的是文本块和图像,因此寻找的元素节点限定为:
webpack的工作流程是基于事件机制,而它的核心就是tapable 这个事件生命周期扩展类。
HyperText Transfer Protocol 超文本传输协议,基于TCP协议的应用层传输协议,在客户端与服务端进行数据传输的一种协议规则
http://host[":"port][/path]
请求行 -> 必须在第一行
请求头 -> 键值对
请求体 -> 具体根据请求方法以及请求头来确定
http使用统一资源标识符来建立连接和传输数据(Uniform Resource Identifiers)
基于 HTTP 1.1
方法 | 描述 | 特点 |
---|---|---|
GET | 用来请求访问已被URI识别的资源 | 请求参数追加在URL后面,没有请求体,参数长度因为客户端URL而有限制,比如ie /safari 为2kb,firefox/chrome为8kb, 一般Web服务器限制为8kb |
POST | 在Request-URI所标识的资源后附加新的数据 | 请求参数放在请求体,相对于GET较为安全,且数据大小没有限制,服务器一般为2GB |
HEAD | 和GET相似 | 服务端接收到HEAD请求只返回响应头,不返回响应内容,如果要查看某个页面的状态时,HEAD更高效,因为传输时间更节省 |
DELETE | 用于删除某个资源 | |
PUT | 存储某个资源,与DELETE相反 | 与POST很相似,但PUT通常制定了资源的存放位置,而POST则没有,POST存放数据由服务器自己决定 |
TRACE | 回显服务器收到的请求 | 主要用于测试或者诊断 |
OPTIONS | 用于获取当前URL所支持的方法 | 若请求成功,会在HTTP头中包含一个名为"ALLOW"的头,值是所支持的方法 |
CONNECT | 预留方法,能够将连接改为管道方式的代理服务器 | 通常用于SSL加密服务器的连接与非加密的HTTP代理服务器的通信 |
对HTTP协议本身的使用做了一些约束:
字段名 | 描述 | 例子 |
---|---|---|
Accept | 客户端可以处理的响应类型(MIME类型) | Accept:image/gif |
Accept-Charset | 客户端可处理的字符集 | UTF-8 |
Accept-Encoding | 客户端支持的数据压缩格式 | Accept-Encoding: gzip, deflate, br |
Accept-Langulage | 客户端指定的语言类型 | Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7 |
Authrization | web认证信息 | |
Connection | 连接的管理,keep-live持久连接,close已关闭 | |
Cache-Control | 控制缓存行为 | cache-control: no-cache |
Content-Legnth | 请求体的长度 | |
Cookie | 客户端cookie | 客户端的信息通过这个带给服务端使用 |
Host | 请求报头域主要用于指定被请求资源的 Internet 主机和端口号 | 比如www.baidu.com |
If-Match | 比较实体标记 | Etag |
If-Modified-Since | 比较资源更新时间 | |
If-None-Match | 与If-Match相反比较 | |
if-Range | 资源未更新时发送实体Byte的范围请求 | |
Referer | 表示这个请求从哪儿URL跳过来的 | 常用于放盗链 |
Range | 实体的字节范围请求 | |
User-Agent | 客户端浏览器与操作系统相关信息 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 |
当请求方式是POST/PUT的时候,请求体会有请求的参数
状态行
消息报头
响应正文
常见状态码:
更多参考MDN
字段名 | 描述 | 例子 |
---|---|---|
Access-Control-Allow-Origin | 一个返回的 HTTP 标头可能会具有 Access-Control-Allow-Origin ,Access-Control-Allow-Origin 指定一个来源,它告诉浏览器允许该来源进行资源访问 | |
Server | 服务器标头包含有关原始服务器用来处理请求的软件的信息。 | Server: Apache/2.4.1 (Unix) |
Set-Cookie | Set-Cookie 用于服务器向客户端发送 sessionID。 | |
Expires | 数据过期时间 | |
Last-Modified | 资源的最后修改时间 | |
Age | 资源的创建时间 | |
Location | 客户端重定向至指定的URL | |
Date | 普通报头域表示消息产生的日期和时间 | |
Cache-Control | 控制缓存行为 |
最早的0.9版本,1991年提出,目的是两个网络之间传输HTML内容,特点:
1994年,支持多协议,多编码,特性:
基于上面的1.0的缺点,1.1做了如下优化:
这里要说的是,HTTP/1.1 管道化所谓的并行传输其实并未做到真正的并行。服务器必须按照客户端请求的先后顺序依次回送相应的结果,以保证客户端能够区分出每次请求的响应内容。即管道化做的事情是把先进先出的队列从客户端迁移到了服务器,由服务器来维护这个状态。
但是在服务器的响应是有先后顺序的。比如:
客户端同时发送两个请求:
目前浏览器厂商采用的做法是针对同一个域名,最多允许同时发起6个请求,所以通过这种方式真正的做到了并行。
HTTP/2 对于 HTTP1.1的改造和提升主要集中在性能上:多路复用,标头压缩,优先级划分和协议协商之类的一类关键功能。
HTTP 是 无状态 的,这既是优点也是缺点。优点是服务器没有状态差异,可以很容易地组成集群,而缺点就是无法支持需要记录状态的事务操作。
那该怎么样让原本无 记忆能力 的服务器拥有 记忆能力 呢?服务器记不住,那就在外部想办法记住。相当于是服务器给每个客户端都贴上一张小纸条,上面写了一些只有服务器才能理解的数据,需要的时候客户端把这些信息发给服务器,服务器看到 Cookie,就能够认出对方是谁了。
这要用到两个字段:响应头字段 Set-Cookie 和请求头字段Cookie。
当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是 key=value ,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。
浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。
因为第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。
不过因为服务器的 记忆能力 实在是太差,一张小纸条经常不够用。所以,服务器有时会在响应头里添加多个 Set-Cookie,存储多个 key=value 。但浏览器这边发送时不需要用多个 Cookie 字段,只要在一行里用 ; 隔开就行。
首先,我们应该设置 Cookie 的生存周期,也就是它的有效期,让它只能在一段时间内可用,就像是食品的 保鲜期 ,一旦超过这个期限浏览器就认为是 Cookie 失效,在存储里删除,也不会发送给服务器。
Cookie 的有效期可以使用 Expires 和 Max-Age 两个属性来设置。
Expires 俗称 过期时间 ,用的是绝对时间点,可以理解为 截止日期 (deadline)。
Max-Age用的是相对时间,单位是秒,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。
Expires 和 Max-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用 Max-Age 计算失效期。
比如在这个例子里,Expires 标记的过期时间是 GMT 2019 年 6 月 7 号 8 点 19 分 ,而 Max-Age 则只有 10 秒,如果现在是 6 月 6 号零点,那么 Cookie 的实际有效期就是 6 月 6 号零点过 10 秒 。
其次,我们需要设置 Cookie 的作用域,让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用。
作用域的设置比较简单,Domain 和 Path 指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。
使用这两个属性可以为不同的域名和路径分别设置各自的 Cookie,比如 /19-1 用一个 Cookie, /19-2 再用另外一个 Cookie,两者互不干扰。不过现实中为了省事,通常 Path 就用一个 / 或者直接省略,表示域名下的任意路径都允许使用 Cookie,让服务器自己去挑。
最后要考虑的就是Cookie 的安全性了,尽量不要让服务器以外的人看到。
写过前端的同学一定知道,在 JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致 跨站脚本 (XSS)攻击窃取数据。
属性 HttpOnly 会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。
另一个属性 SameSite可以防范 跨站请求伪造 (XSRF)攻击,设置成SameSite=Strict 可以严格限定 Cookie 不能随着跳转链接跨站发送,而 SameSite=Lax 则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。
还有一个属性叫 Secure ,表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。
服务器标记资源有效期使用的头字段是 Cache-Control,里面的值 max-age=30 就是资源的有效时间,相当于告诉浏览器, 这个页面只能缓存 30 秒,之后就算是过期,不能用。
max-age 是 生存时间 (又叫 新鲜度 缓存寿命 ,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。
比如,服务器设定 max-age=5,但因为网络质量很糟糕,等浏览器收到响应报文已经过去了 4 秒,那么这个资源在客户端就最多能够再存 1 秒钟,之后就会失效。
此外在响应报文里还可以用其他的属性来更精确地指示浏览器应该如何使用缓存:
其实不止服务器可以发 Cache-Control 头,浏览器也可以发 Cache-Control ,也就是说 请求 - 应答 的双方都可以用这个字段进行缓存控制,互相协商缓存的使用策略。
当你点 刷新 按钮的时候,浏览器会在请求头里加一个 Cache-Control: max-age=0 。因为 max-age 是 生存时间 ,max-age=0 的意思就是 我要一个最最新鲜的西瓜 ,而本地缓存里的数据至少保存了几秒钟,所以浏览器就不会使用缓存,而是向服务器发请求。服务器看到 max-age=0,也就会用一个最新生成的报文回应浏览器。
Ctrl+F5 的 强制刷新 又是什么样的呢?
它其实是发了一个 Cache-Control: no-cache ,含义和 max-age=0 基本一样,就看后台的服务器怎么理解,通常两者的效果是相同的。
浏览器可以用两个连续的请求组成 验证动作 :先是一个 HEAD,获取资源的修改时间等元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个 GET 请求,获取最新的版本。
但这样的两个请求网络成本太高了,所以 HTTP 协议就定义了一系列 If 开头的 条件请求 字段,专门用来检查验证资源是否过期,把两个请求才能完成的工作合并在一个请求里做。而且,验证的责任也交给服务器,浏览器只需坐享其成。
条件请求一共有 5 个头字段,我们最常用的是 if-Modified-Since 和 If-None-Match 这两个。需要第一次的响应报文预先提供 Last-modified 和 ETag ,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。
如果资源没有变,服务器就回应一个 304 Not Modified ,表示缓存依然有效,浏览器就可以更新一下有效期,然后放心大胆地使用缓存了。
Last-modified 很好理解,就是文件的最后修改时间。ETag 是什么呢?
ETag 是 实体标签 (Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。
比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。
再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。
使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存。
ETag 还有 强 、弱 之分。
强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个 W/ 标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。
还是拿生鲜速递做比喻最容易理解:
你打电话给超市, 我这个西瓜是 3 天前买的,还有最新的吗? 。超市看了一下库存,说: 没有啊,我这里都是 3 天前的。 于是你就知道了,再让超市送货也没用,还是吃冰箱里的西瓜吧。这就是 if-Modified-Since 和 Last-modified 。
但你还是想要最新的,就又打电话: 有不是沙瓤的西瓜吗? ,超市告诉你都是沙瓤的(Match),于是你还是只能吃冰箱里的沙瓤西瓜。这就是 If-None-Match 和 弱 ETag 。
第三次打电话,你说 有不是 8 斤的沙瓤西瓜吗? ,这回超市给了你满意的答复: 有个 10 斤的沙瓤西瓜 。于是,你就扔掉了冰箱里的存货,让超市重新送了一个新的大西瓜。这就是 If-None-Match 和 强 ETag 。
条件请求里其他的三个头字段是 If-Unmodified-Since If-Match 和 If-Range ,其实只要你掌握了if-Modified-Since 和 If-None-Match ,可以轻易地举一反三 。
一图胜千言:
test2
check image URL is valid,including base64
async function checkImage(url){
const res = await fetch(url);
const buff = await res.blob();
return buff.type.startsWith('image/');
}
export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.ustc.edu.cn/brew.git"
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.ustc.edu.cn/homebrew-core.git/"
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
LC3:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
思路:滑动窗口,没有就进,有就剔除以及它左边的所有
let lengthOfLongestSubstring = function(s) {
let arr = new Array();
let max = 0;
for(let i=0; i < s.length; i++) {
let index = arr.indexOf(s[i])
if(index !== -1) {
arr.splice(0,index + 1)
}
arr.push(s[i])
max = Math.max(arr.length,max)
}
return max
}
LC:189:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
function rotate(nums,k){
while(k>0){
nums.unshift(nums.pop());
k--
}
return nums;
}
一些常见的手撕代码
exist.js
ramda.js
ruby version
stackoverflow:use rvm or rbenv to manage the ruby version
rvm install 2.2.5
rvm use 2.2.5
Remove python [version number] completely step by step
ls /Library/Frameworks/Python.framework/Versions/
sudo rm -rf /Library/Frameworks/Python.framework/Versions/[version number]
sudo rm -rf "/Applications/Python [version number]"
ls -l /usr/local/bin | grep '../Library/Frameworks/Python.framework/Versions/[version number]'
cd /usr/local/bin/
ls -l /usr/local/bin | grep '../Library/Frameworks/Python.framework/Versions/[version number]' | awk '{print $9}' | tr -d @ | sudo xargs rm
test
IP协议对应于网络层,TCP协议对应于传输层,主要解决数据如何在网络中传输,看下一个比较形象的比喻图:
通俗的解释:
Transmision Control Protocal 面向连接的传输控制协议
最开始的时候客户端和服务端都处于CLOSED状态,主动打开链接的为客户端,被动打开链接的是服务器
为什么客户端最后还要发送一次确认?
主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误
如果已经建立连接,但是客户端突然出现故障怎么办?
TCP设有一个保活计时器,客户端出现故障,服务端不可能一直等下去浪费资源。服务器每收到一个客户端的请求后都会重新复位这个计时器,一般为2小时,若两小时后客户端没有任何数据,服务器就会每个75秒发送一个探测报文段,连发10次还没有响应,就关闭连接。
数据传输完毕后,双方都可以释放连接,客户端主动关闭,服务器被动关闭
客户端发送释放数据报文,首部FIN=1,其序列号为seq=u,客户端进入FIN-WAIT-1状态
服务端收到连接释放报文,发出确认报文,ACK=1,seq=u+1,并且带上自己的序列seq=v,此时服务端进入CLOSE-WAIT,需要注意的是服务端TCP此时处于半关闭状态,因为服务端若发送数据,客户端依然要接受。
客户端接受到服务端的确认请求后,此时客户端就进入FIN-WAIT-2,等待服务端发送释放报文,在这之前还需要接收服务端发送的数据。
服务端将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1, 由于半关闭状态,服务端很可能又发送了一些数据,假设此时的序列号为seq=w,此时服务端进入了LAST-ACK 状态,等待客户端确认
客户端接受到服务器的连接释放后,发出确认,ACK=1,ack=w+1而自己的序列号为seq=u+1,此时客户端进入了TIME-WAIT ,注意测试TCP还没有释放,经过2*MSL(最长报文寿命)后,进入CLOSED状态
服务端接收到客户端的确认后,立即进入CLOSED ,所以结束的比客户端早一些
为什么客户端最后还要等待2MSL?
MSL(maximum segment lifetime)
- 保证客户端发送的最后一个ACK能够到达服务器,因为这个ACK可能丢失,站在服务器的角度,已经发送了FIN+ACK, 客户端还没有回应,可能没有收到,所以我再发一次。而客户端就能在这2个MSL时间段内收到这个重传的报文,接着给出回应,并且重启2MSL计时器
- 防止类似与三次握手中提到的已经失效的链接请求报文段出现在本链接中,客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以是本连接持续的时间内所产生的所有报文段都从网络中小时,这样新的连接中不会出现旧连接的请求报文
为什么建立连接是3次握手,而关闭连接是4次挥手呢?
建立连接的时候,服务器在LISTEN状态下,收到建立连接请求的SYN报文后,会把ACK和SYN放在一个报文里发送给客户端
而关闭连接时候,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据,但还能接收数据,而且服务端可能还有数据没有发送完,所以可以立即关闭,也可以在发送数据给对方后,再发送FIN报文给对方来同意关闭连接,因此ACK和FIN一般是分开发送,从而导致多了一次。
A打电话给B
A:听得到吗?
B:听得到!你呢?
A:我也听到了,出来烧烤?
A:真好吃,走吧,
B:等下,我去把帐结了
B:好,走吧
A:走!各自回家
费曼学习法可以简化为四个单词:Concept (概念)、Teach (教给别人)、Review (回顾)、Simplify (简化)
举个栗子:
浏览器JS事件循环,调用堆栈,任务队列等一些知识 如何让这些知识印象深刻呢?
useful ffmpeg commands collections
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.