javaScript
,HTML
,CSS
.Dom
操做EventLoop
和渲染机制IM
以及超大型高并发网站应用等,例如B站
)在使用某个技术的时候,必定要去追寻原理和底层的实现,久而久之坚持,只要自身底层的基础扎实,不管技术怎么变化,学习起来都不会太累,总的来讲就是拒绝5分钟技术css
url
地址,到显示页面发生了什么出发:TCP
连接之上TCP
呢TCP三次握手的过程以下:html
SYN
报文给服务器端,进入SYN_SEND
状态。SYN
报文,回应一个SYN
(SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。如图所示:前端
TCP
的四次挥手:java
特别提示:
SYN
报文用来通知,FIN
报文是用来同步的react
以上就是面试官常问的三次握手,四次挥手,可是这不只仅面试题,上面仅仅答到了一点皮毛,学习这些是为了让咱们后续方便了解他的优缺点。webpack
TCP
链接创建后,咱们能够有多种协议的方式通讯交换数据:http 1.0
早先1.0的HTTP版本,是一种无状态、无链接的应用层协议。git
HTTP1.0规定浏览器和服务器保持短暂的链接,浏览器的每次请求都须要与服务器创建一个TCP链接,服务器处理完成后当即断开TCP链接(无链接),服务器不跟踪每一个客户端也不记录过去的请求(无状态)。github
这种无状态性能够借助cookie/session机制来作身份认证和状态记录。而下面两个问题就比较麻烦了。web
首先,无链接的特性致使最大的性能缺陷就是没法复用链接。每次发送请求的时候,都须要进行一次TCP的链接,而TCP的链接释放过程又是比较费事的。这种无链接的特性会使得网络的利用率很是低。面试
其次就是队头阻塞(headoflineblocking)。因为HTTP1.0规定下一个请求必须在前一个请求响应到达以前才能发送。假设前一个请求响应一直不到达,那么下一个请求就不发送,一样的后面的请求也给阻塞了。
Http 1.0
的致命缺点,就是没法复用TCP
链接和并行发送请求,这样每次一个请求都须要三次握手,并且其实创建链接和释放链接的这个过程是最耗时的,传输数据相反却不那么耗时。还有本地时间被修改致使响应头expires
的缓存机制失效的问题~(后面会详细讲)
Http 1.1
,这也是技术的发展必然结果~Http 1.1
出现,继承了Http1.0
的优势,也克服了它的缺点,出现了keep-alive
这个头部字段,它表示会在创建TCP
链接后,完成首次的请求,并不会马上断开TCP
链接,而是保持这个链接状态~进而能够复用这个通道
Http 1.1
而且支持请求管道化,“并行”发送请求,可是这个并行,也不是真正意义上的并行,而是可让咱们把先进先出队列从客户端(请求队列)迁移到服务端(响应队列)
例如:客户端同时发了两个请求分别来获取html和css,假如说服务器的css资源先准备就绪,服务器也会先发送html再发送css。
B站
首页,就有keep-alive
,由于他们也有IM
的成分在里面。须要大量复用TCP
链接~HTTP1.1
好像仍是没法解决队头阻塞的问题实际上,现阶段的浏览器厂商采起了另一种作法,它容许咱们打开多个TCP的会话。也就是说,上图咱们看到的并行,实际上是不一样的TCP链接上的HTTP请求和响应。这也就是咱们所熟悉的浏览器对同域下并行加载6~8个资源的限制。而这,才是真正的并行!
Http 1.1
的致命缺点:咱们也能够用
dns-prefetch和 preconnect tcp
来优化~
<link rel="preconnect" href="//example.com" crossorigin>
<link rel="dns=prefetch" href="//example.com">
复制代码
Tip
: webpack
能够作任何事情,这些均可以用插件实现Http 2.0
HTTP 2.0 在客户端和服务器端使用“首部表”来跟踪和存储以前发送的键-值对,对于相同的数据,再也不经过每次请求和响应发送;通讯期间几乎不会改变的通用键-值对(用户代理、可接受的媒体类型,等等)只 需发送一次。事实上,若是请求中不包含首部(例如对同一资源的轮询请求),那么 首部开销就是零字节。此时全部首部都自动使用以前请求发送的首部。
若是首部发生变化了,那么只须要发送变化了数据在Headers帧里面,新增或修改的首部帧会被追加到“首部表”。首部表在 HTTP 2.0 的链接存续期内始终存在,由客户端和服务器共同渐进地更新 。
本质上,固然是为了减小请求啦,经过多个js或css合并成一个文件,多张小图片拼合成Sprite图,可让多个HTTP请求减小为一个,减小额外的协议开销,而提高性能。固然,一个HTTP的请求的body太大也是不合理的,有个度。文件的合并也会牺牲模块化和缓存粒度,能够把“稳定”的代码or 小图 合并为一个文件or一张Sprite,让其充分地缓存起来,从而区分开迭代快的文件。
Demo
的性能对比:![]()
Http
的那些致命缺陷,并无彻底解决,因而有了https
,也是目前应用最广的协议之一HTTP+ 加密 + 认证 + 完整性保护 =HTTPS
?不加密的重要内容被
wireshark
这类工具抓到包,后果很严重~
HTTPS 并不是是应用层的一种新协议。只是 HTTP 通讯接口部分用 SSL(SecureSocket Layer)和 TLS(Transport Layer Security)协议代替而已。 一般,HTTP 直接和 TCP 通讯。
当使用 SSL 时,则演变成先和 SSL 通讯,再由 SSL和 TCP 通讯了。简言之,所谓 HTTPS,其实就是身披 SSL 协议这层外壳的HTTP。
在采用 SSL 后,HTTP 就拥有了 HTTPS 的加密、证书和完整性保护这些功能。SSL 是独立于 HTTP 的协议,因此不光是 HTTP 协议,其余运行在应用层的 SMTP和 Telnet 等协议都可配合 SSL 协议使用。能够说 SSL 是当今世界上应用最为普遍的网络安全术。
加密和解密都会用到密钥。没有密钥就没法对密码解密,反过来讲,任何人只要持有密钥就能解密了。若是密钥被攻击者得到,那加密也就失去了意义。
Https
加密篇幅太长,这篇文章写得很好,你们能够去看看。HTTPS
虽好,非对称加密虽好,可是不要滥用既然 HTTPS 那么安全可靠,那为什么全部的 Web 网站不一直使用 HTTPS? 其中一个缘由是,由于与纯文本通讯相比,加密通讯会消耗更多的 CPU 及内存资源。若是每次通讯都加密,会消耗至关多的资源,平摊到一台计算机上时,可以处理的请求数量一定也会随之减小。
所以,若是是非敏感信息则使用 HTTP 通讯,只有在包含我的信息等敏感数据时,才利用 HTTPS 加密通讯。 特别是每当那些访问量较多的 Web 网站在进行加密处理时,它们所承担着的负载不容小觑。在进行加密处理时,并不是对全部内容都进行加密处理,而是仅在那些须要信息隐藏时才会加密,以节约资源。
除此以外,想要节约购买证书的开销也是缘由之一。 要进行 HTTPS 通讯,证书是必不可少的。而使用的证书必须向认证机构(CA)购买。证书价格可能会根据不一样的认证机构略有不一样。一般,一年的受权须要数万日元(如今一万日元大约折合 600 人民币)。那些购买证书并不合算的服务以及一些我的网站,可能只会选择采用HTTP 的通讯方式。
请求报文格式
响应报文格式
所谓响应头,请求头,其实均可以本身添加字段,只要先后端给对应的处理机制便可
Node.js
代码实现响应头的设置if (config.cache.expires) {
res.setHeader("expries", new Date(Date.now() + (config.cache.maxAge * 1000)))
}
if (config.cache.lastModified) {
res.setHeader("last-modified", stat.mtime.toUTCString())
}
if (config.cache.etag) {
res.setHeader('Etag', etagFn(stat))
}
}
复制代码
Node.js
静态资源服务器,github.com/JinJieTan/u… star
~首次请求:
非首次请求:
用户行为与缓存:
HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
须要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
通过HTTPS安全加密的请求(有人也通过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public以后,可以对HTTPS的资源进行缓寸)
通过HTTPS安全加密的请求(有人也通过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public以后,可以对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)
POST请求没法被缓存
HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求没法被缓存
websocket
协议开始:传统的协议没法服务端主动
push
数据,因而有了这些骚操做:
webSocket
.webSockets
的目标是在一个单独的持久链接上提供全双工、双向通讯。在Javascript建立了Web Socket以后,会有一个HTTP请求发送到浏览器以发起链接。在取得服务器响应后,创建的链接会将HTTP升级从HTTP协议交换为WebSocket协议。
webSocket
原理: 在TCP
链接第一次握手的时候,升级为ws
协议。后面的数据交互都复用这个TCP
通道。
客户端代码实现:
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = function () {
ws.send('123')
console.log('open')
}
ws.onmessage = function () {
console.log('onmessage')
}
ws.onerror = function () {
console.log('onerror')
}
ws.onclose = function () {
console.log('onclose')
}
复制代码
Node.js
语言实现const express = require('express')
const { Server } = require("ws");
const app = express()
const wsServer = new Server({ port: 8080 })
wsServer.on('connection', (ws) => {
ws.onopen = function () {
console.log('open')
}
ws.onmessage = function (data) {
console.log(data)
ws.send('234')
console.log('onmessage' + data)
}
ws.onerror = function () {
console.log('onerror')
}
ws.onclose = function () {
console.log('onclose')
}
});
app.listen(8000, (err) => {
if (!err) { console.log('监听OK') } else {
console.log('监听失败')
}
})
复制代码
webSocket
的报文格式有一些不同:客户端和服务端进行Websocket消息传递是这样的:
ping
andpong
Go
实现:package main
import (
"net/http"
"time"
"github.com/gorilla/websocket"
)
var (
//完成握手操做
upgrade = websocket.Upgrader{
//容许跨域(通常来说,websocket都是独立部署的)
CheckOrigin:func(r *http.Request) bool {
return true
},
}
)
func wsHandler(w http.ResponseWriter, r *http.Request) {
var (
conn *websocket.Conn
err error
data []byte
)
//服务端对客户端的http请求(升级为websocket协议)进行应答,应答以后,协议升级为websocket,http创建链接时的tcp三次握手将保持。
if conn, err = upgrade.Upgrade(w, r, nil); err != nil {
return
}
//启动一个协程,每隔5s向客户端发送一次心跳消息
go func() {
var (
err error
)
for {
if err = conn.WriteMessage(websocket.TextMessage, []byte("heartbeat")); err != nil {
return
}
time.Sleep(5 * time.Second)
}
}()
//获得websocket的长连接以后,就能够对客户端传递的数据进行操做了
for {
//经过websocket长连接读到的数据能够是text文本数据,也能够是二进制Binary
if _, data, err = conn.ReadMessage(); err != nil {
goto ERR
}
if err = conn.WriteMessage(websocket.TextMessage, data); err != nil {
goto ERR
}
}
ERR:
//出错以后,关闭socket链接
conn.Close()
}
func main() {
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("0.0.0.0:7777", nil)
}
复制代码
Node.js
实现):this.heartTimer = setInterval(() => {
if (this.heartbeatLoss < MAXLOSSTIMES) {
events.emit('network', 'sendHeart');
this.heartbeatLoss += 1;
this.phoneLoss += 1;
} else {
events.emit('network', 'offline');
this.stop();
}
if (this.phoneLoss > MAXLOSSTIMES) {
this.PhoneLive = false;
events.emit('network', 'phoneDisconnect');
}
}, 5000);
复制代码
new Socket
开始:SDK
接入,可是逼格高些仍是本身重写比较好。IM
桌面应用开发的~const {Socket} = require('net')
const tcp = new Socket()
tcp.setKeepAlive(true);
tcp.setNoDelay(true);
//保持底层tcp连接不断,长链接
指定对应域名端口号连接
tcp.connect(80,166.166.0.0)
创建链接后
根据后端传送的数据类型 使用对应不一样的解析
readUInt8 readUInt16LE readUInt32LE readIntLE等处理后获得myBuf
const myBuf = buffer.slice(start);//从对应的指针开始的位置截取buffer
const header = myBuf.slice(headstart,headend)//截取对应的头部buffer
const body = JSON.parse(myBuf.slice(headend-headstart,bodylength).tostring())
//精确截取数据体的buffer,而且转化成js对象
复制代码
即时通信强烈推荐使用
Golang
,GRPC
,Prob
传输数据。
webpack-electron-react-websocket
的Demo, github.com/JinJieTan/r…以为写得不错,能够点个赞支持下,文章也借鉴了一下其余大佬的文章,可是地址都贴上来了~ 欢迎
gitHub
点个star
哦~