为什么我们要熟悉这些通信协议? 【精读】
- - SegmentFault 最新的文章前端的最重要的基础知识点是什么. 原生 javaScript, HTML, CSS.. EventLoop和渲染机制. 各类工程化的工具原理以及使用,根据需求定制编写插件和包. (webpack的plugin和babel的预设包). 数据结构和算法(特别是 IM以及超大型高并发网站应用等,例如 B站).
javaScript
, HTML
, CSS
.Dom
操作EventLoop
和渲染机制IM
以及超大型高并发网站应用等,例如 B站
)在使用某个技术的时候,一定要去追寻原理和底层的实现,长此以往坚持,只要自身底层的基础扎实,无论技术怎么变化,学习起来都不会太累,总的来说就是拒绝5分钟技术
url
地址,到显示页面发生了什么出发:TCP
链接之上TCP
呢TCP三次握手的过程如下:
SYN
报文给服务器端,进入 SYN_SEND
状态。SYN
报文,回应一个 SYN
(SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。如图所示:
TCP
的四次挥手:
注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。
特别提示:SYN
报文用来通知,FIN
报文是用来同步的
以上就是面试官常问的三次握手,四次挥手,但是这不仅仅面试题,上面仅仅答到了一点皮毛,学习这些是为了让我们后续方便了解他的优缺点。
TCP
连接建立后,我们可以有多种协议的方式通信交换数据:http 1.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
Demo
的性能对比:
Http
的那些致命缺陷,并没有完全解决,于是有了 https
,也是目前应用最广的协议之一HTTP+ 加密 + 认证 + 完整性保护 =HTTPS
?因为很有可能并不是和原本预想的通信方在实际通信。并且还需要考虑到接收到的报文在通信途中已经遭到篡改这一可能性。
不加密的重要内容被 wireshark
这类工具抓到包,后果很严重~
通常,HTTP 直接和 TCP 通信。
加密和解密都会用到密钥。没有密钥就无法对密码解密,反过来说,任何人只要持有密钥就能解密了。如果密钥被攻击者获得,那加密也就失去了意义。
Https
加密篇幅太长,这篇文章写得很好,大家可以去看看。HTTPS
虽好,非对称加密虽好,但是不要滥用针对速度变慢这一问题,并没有根本性的解决方案,我们会使用 SSL 加速器这种(专用服务器)硬件来改善该问题。该硬件为 SSL 通信专用硬件,相对软件来讲,能够提高数倍 SSL 的计算速度。仅在 SSL 处理时发挥 SSL加速器的功效,以分担负载。
其中一个原因是,因为与纯文本通信相比,加密通信会消耗更多的 CPU 及内存资源。如果每次通信都加密,会消耗相当多的资源,平摊到一台计算机上时,能够处理的请求数量必定也会随之减少。
特别是每当那些访问量较多的 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
静态资源服务器, https://github.com/JinJieTan/...,欢迎 star
~
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
and pong
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, https://github.com/JinJieTan/...
觉得写得不错,可以点个赞支持下,文章也借鉴了一下其他大佬的文章,但是地址都贴上来了~ 欢迎gitHub
点个star
哦~