有了 HTTP 协议,为什么还需要 Websocket?
WebSocket 是一种基于 TCP 连接上进行全双工通信的协议,相对于 HTTP 这种非持久的协议来说,WebSocket 是一个持久化网络通信的协议。
它不仅可以实现客户端请求服务器,同时可以允许服务端主动向客户端推送数据。在 WebSocket API 中,客户端和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
为什么需要 WebSocket
在 Web 应用架构中,连接由 HTTP/1.0 和 HTTP/1.1 处理。HTTP 是客户端/服务器模式中 请求一响应 所用的协议,在这种模式中,客户端(一般是浏览器)向服务器提交 HTTP 请求,服务器响应请求的资源(例如 HTML 页面)。
HTTP 是无状态的,也就是说,它将每个请求当成唯一和独立的。无状态协议具有一些优势,例如,服务器不需要保存有关会话的信息,从而不需要存储数据。但是,这也意味着在每次 HTTP 请求和响应中都会发送关于请求的冗余信息,比如使用 Cookie 进行用户状态的验证。
随着客户端和服务器之间交互的增加,HTTP 协议在客户端和服务器之间通信所需要的信息量快速增加。
从根本上讲,HTTP 还是 半双工 的协议,也就是说,在同一时刻信息的流向只能单向的:客户端向服务器发送请求(单向),然后服务器响应请求(单向)。半双工方式的通信效率是非常低的。
同时 HTTP 协议有一个缺陷:通信只能由客户端发起。
这种单向请求的特点,注定了如果服务器有状态变化,是无法主动通知客户端的。
为了能够及时的获取服务器的变化,我们尝试过各种各样的方式:
轮询(polling):每隔一段时间,就发出一个请求,了解服务器有没有新的信息。不精准,有延时,大量无效数据交换。
长轮询( long polling):客户端向服务器请求信息,并在设定的时间段内保持连接。直到服务器有新消息响应,或者连接超时,这种技术常常称作“挂起GET”或“搁置POST”。占用服务器资源,相对轮询并没有优势,没有标准化。
流化技术:在流化技术中,客户端发送一个请求,服务器发送并维护一个持续更新和保持打开(可以是无限或者规定的时间段)的开放响应。每当服务器有需要交付给客户端的信息时,它就更新响应。服务器从不发出完成 HTTP 响应。代理和防火墙可能缓存响应,导致信息交付的延迟增加。
上述方法提供了近乎实时的通信,但是它们也涉及 HTTP 请求和响应首标,包含了许多附加和不必要的首标数据与延迟。此外,在每一种情况下,客户端都必须等待请求返回,才能发出后续的请求,而这显著地增加了延退。同时也极大地增加了服务器的压力。
什么是 WebSocket
而 Websocket 是一种自然的全双工、双向、单套接字连接,解决了 HTTP 协议中不适合于实时通信的问题。2008 年被提出,2011 年成为国际标准。
Websocket 协议能够通过 Web 进行客户端和服务器之间的全双工通信,并支持二进制数据和文本字符串的传输。
这个协议由开始的握手和之后的基本消息框架组成,是建立在 TCP 协议上的。相比于 HTTP 协议,Websocket 链接一旦建立,即可进行双向的实时通信。
其特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
相似技术
Server-sent Events(SSE):
https://www.ruanyifeng.com/blog/2017/05/server-sent_events.html
https://www.cnblogs.com/goloving/p/9196066.html
SPDY (读作“SPeeDY”):已不再维护,由 HTTP/2 取代
https://baike.baidu.com/item/SPDY/3399551#7
WebRTC
https://baike.baidu.com/item/WebRTC/5522744
通信原理
WebSocket 链接是如何建立的?
前面说过,WebSocket 在握手阶段采用的是 HTTP 协议,Websocket 借用了 HTTP 的一部分协议来完成一次握手。(HTTP的三次握手,此处只完成一次)
HTTP 请求与响应首部
WebSocket 请求与响应首部
链接通信模拟
HTTP 轮询
首先是 ajax 轮询,其原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
场景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Request)客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你 ' 西岭真帅' 。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。没。。。。没。。没有
从上面可以看出,轮询其实就是在不断地建立HTTP连接,然后等待服务端处理,可以体现 HTTP 协议的另外一个特点,被动性。同时,http 的每一次请求与响应结束后,服务器将客户端信息全部丢弃,下次请求,必须携带身份信息(cookie),无状态性。
WebSocket
客户端通过 http(骑马)带着信请求服务器,但同时,携带了 Upgrade:websocket 和Connection:Upgrade(两根管子),服务器如果支持 WebSocket 协议(有两根管子的接口),使用 Websocket 协议返回可用信息(丢弃马匹),此后信息的传递,均使用这两个管子,除非有一方人为的将管子切断。若服务器不支持,客户端请求链接失败,返回错误信息。
Websocket 的出现,干净利落的解决了这些问题。
所以上面的情景可以做如下修改。
客户端:啦啦啦,我要建立 Websocket 协议,需要的服务:chat,Websocket协议版本:13(HTTP Request)
服务端:ok,确认,已升级为 Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
客户端:balabala开始斗图balabala
服务端:苍*空bala
客户端:流鼻血了,我擦……
服务端:哈哈哈牛XX啊哈哈哈哈
服务端:笑死我了哈哈
接下来,我们来看 Websocket 服务端与客户端实现。
Websocket 服务端与客户端实现
经过前面对通信过程的梳理,我们将 WebSocket 通信的基本机制已经说的差不多了,为了方便你快速进入实战阶段,我们暂时放弃纯手写实现,直接选择使用老牌的 WebSocket 库: WebSocket-Nodehttps://github.com/theturtle32/WebSocket-Node
简单介绍一下 WebSocket-Node,它有多老牌呢?
NPM 的包名字就是直接使用的 “WebSocket”。曾经,我们西岭老湿看到之后就给出了两个字的评价:“猖狂”。
这个库完全使用 JavaScript 实现,包含了客户端及服务端的实例。其中,客户端包含了 Node 和 浏览器 两个运行环境的代码,除了支持我们前面提到的 Websocket 协议的 13 版本,它同时还支持 Websocket 协议 8 这个老版本,实属优秀。
接下来,我们就来看看,如何借助 Websocket-Node 实现一个 Websocket 服务。
服务端
安装 npm install websocket 后,创建服务器运行文件 ws-server.js ,代码如下,请认真阅读代码及注释:
// === 作为帅哥,一定要加注释 ===
var Websocket = require('websocket').server
var http = require('http')
// 创建 HTTP 服务,作为第一次握手链接使用
var httpServer = http.createServer().listen(8080,function(){
console.log('http://127.0.0.1:8080')
})
// 创建 websocket 服务实力
var wsServer = new Websocket({
// 配置依赖的握手 http 服务器
httpServer:httpServer,
autoAcceptConnections:false
})
// 保存链接池
var conArr = []
// 监听 ws 请求事件
wsServer.on('request',function(request){
// 获取链接示例
var connection = request.accept()
// 保存连接池
conArr.push(connection)
// 监听消息事件
connection.on('message',function(msg){
console.log(msg)
// 循环连接池,推送广播消息至客户端
for(let i = 0;i<conArr.length;i++){
conArr[i].send(msg.utf8Data)
}
})
})
// 据说,长得好看的都会看注释
过多的描述,就不写了,据说,长得好看的都会看代码注释(●'◡'●)
运行代码文件后,不出意外的情况下,命令行进程会被占用,监听端口也会被占用,证明服务端运行成功。如果两个都没被占用,想啥呢?失败了呀宝子……
如果服务器启动成功,我怎么用客户端建立链接查看呢?有一款 Websocket 客户端工具叫 WebsocketMan,如果感兴趣,你可以下载来试试。
但是像我这样的帅哥,一般都是自己写客户端:)
客户端
Websocket 的客户端并没有什么技术难点,就是浏览器 API 调用。只要你把通信机制够清楚,这玩意就没有不会,因为非常简单,我们直接选择纯手写就可以了,如果你想使用 Websocket-Node 客户端,确实还会更简单。
当然,在写之前,还是要去看看手册的,要不然你怎么知道有哪些 API 呢?来,手册地址给你:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
你先看着,我就不客气,直接开干……
<body> <div id="msg"></div> <input type="text" id="text"> <input type="button" value="发送" onclick="send()">
<script> //调用websocket对象建立连接: //参数:ws/wss(加密)://ip:port (字符串) var websocket = new WebSocket('ws://127.0.0.1:8080') // console.log(websocket.readyState) // 0 // readyState // 0 链接还没有建立(正在建立链接) // 1 链接建立成 // 2 链接正在关闭 // 3 链接已经关闭
// 监听链接开启事件 websocket.onopen = function () { console.log(websocket.readyState) }
// 绑定按钮点击事件 function send() { var text = document.getElementById('text').value // ws 消息发送 websocket.send(text) }
// 监听服务端消息推送事件 websocket.onmessage = function (back) { console.log(back.data) }
// 监听连接错误信息 // websocket.onerror = function (evt, e) { // console.log('Error occured: ' + evt.data); // };
//监听连接关闭 // websocket.onclose = function (evt) { // console.log("Disconnected"); // };</script>
</body>
过多的描述,就不写了,据说,长得好看的都会看代码注释(●'◡'●)
至此,一个完整的 websocket 通信已经建立完成并能够进行双向通信了。
Websocket-Node 确实很好用,但是功能也确实比较单一了,需要你对 WebSocket 机制有一定的理解之后,才能实现相应的能力。如果,我对 websocket 完全不懂,但又想搞个聊天室,能不能行?
指!定!能!行!
Socket.IO
一个目前最为强大且好用的,基本屏蔽了 websocket 概念的 websocket 库。你几乎不用掌握 websocket 相关的知识,只需要按照 Socket.IO 中提供的 API 就能够很好的实现一个 websocket 通信。
注意:程序员要“除机心”。
在不了解 Websocket 时,学习 Websocket 中,强烈不建议使用。
在生产环境下,强烈建议使用。
服务端
const { createServer } = require("http");
const { Server } = require("socket.io");
const httpServer = createServer();
const io = new Server(httpServer, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
io.on("connection", (socket) => {
socket.on('sendMsg',(data)=>{
io.emit('pushMsg',data)
})
});
httpServer.listen(3000, function () {
console.log('http://127.0.0.1:3000')
});
客户端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.socket.io/4.2.0/socket.io.min.js"
integrity="sha384-PiBR5S00EtOj2Lto9Uu81cmoyZqR57XcOna1oAuVuIEjzj0wpqDVfD0JA9eXlRsj"
crossorigin="anonymous"></script>
</head>
<body>
<input type="text" id="text">
<input type="button" value="发送" onclick="send()">
<script>
var socket = io.connect('http://127.0.0.1:3000')
function send() {
var text = document.getElementById('text').value
socket.emit('sendMsg', text)
}
socket.on('pushMsg', (data) => {
console.log(data)
})
</script>
</body>
</html>
没什么可解释的,就直接按照 Socket.IO 的 API 写就完事了。
有了 HTTP 协议,为什么还需要 Websocket?的更多相关文章
- Websocket全讲解。跨平台的通讯协议 !!基于websocket的高并发即时通讯服务器开发。
本博文,保证不用装B的话语和太多专业的语言,保证简单易懂,只要懂JAVAEE开发的人都可以看懂. 本博文发表目的是,目前网上针对Websocket的资料太散乱,导致初学者的知识体系零零散散,学习困难加 ...
- HTTP协议系列(3)---包括WebSocket简单介绍
一.HTTPS HTTP是超文本传输协议,那HTTPS是什么尼?要明白HTTPS是什么先要明白HTTP的缺点,想一下我们在使用HTTP的时候会有那些缺点尼? 1.通信使用的明文(不加密),内容 ...
- C#实现WebSocket协议客户端和服务器websocket sharp组件实例解析
看到这篇文章的题目,估计很多人都会问,这个组件是不是有些显的无聊了,说到web通信,很多人都会想到ASP.NET SignalR,或者Nodejs等等,实现web的网络实时通讯.有关于web实时通信的 ...
- 网络协议之:还在用HTTP代理?弱爆了!快试试SOCKS5
目录 简介 为什么要使用SOCKS SOCKS5 SOCKS5的使用 总结 简介 存在即是合理,SOCKS5的出现是为了解决SOCKS4中不支持身份认证的大问题而出现的,毕竟大家对网络中的安全越来越重 ...
- springboot深入学习(四)-----tomcat配置、websocket
一.更改servlet服务器 springboot中默认可以集成多种servlet容器,当引入如下依赖时: springboot默认以tomcat作为项目的servlet容器,如果用户想要替换tomc ...
- 04 Websocket和Websocketed
一.web socket事件和方法 有了HTTP协议为什么还需要Websocket这种协议呢?因为HTTP协议发起的通信只能通过客户端发起,然后服务端才可以将消息回应到客户端.因此HTTP协议做不到服 ...
- WebSocket 结合 Nginx 实现域名及 WSS 协议访问
简单了解一下 WebSocket 现在,很多网站为了实现推送技术,所用的技术都是轮询.轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器 ...
- WebSocket协议详解与c++&c#实现
摘要: 随着手机游戏.H5游戏以及微信小游戏的普及,越来越多的客户端-服务器端的通讯采用websocket协议.Websocket协议是全双工的.基于数据帧的.建立在tcp之上的长连接协议.Webso ...
- C#封装的websocket协议类
关于VB版之前已经写了,有需要可以进传送门<VB封装的WebSocket模块,拿来即用>,两个使用都差不多,这里简单概述一下: 连接完成后,没有握手就用Handshake()先完成握手之后 ...
随机推荐
- 菜狗、《灵笼》、《时光代理人》,重新审视Z世代的电商逻辑
来源:懂懂笔记 B站还有多少潜力可以挖掘? 虽然B站的最新财报依然还是亏损,但同时也让人看到更多的可能性. 从财报数据的亮点来看,一是营收增长,B站二季度营收为44.95亿元,同比增长72%.营收上B ...
- 通过mstsc复制粘贴失败需要重新启动RDP剪切板监视程序rdpclip.exe
先结束程序 再重新启动程序
- PHP中的输出缓冲控制
在 PHP 中,我们直接进行 echo . 或者 print_r 的时候,输出的内容就会直接打印出来.但是,在某些情况下,我们并不想直接打印,这个时候就可以使用输出缓冲控制来进行输出打印的控制.当然, ...
- TP6自带的跨域中间件无法使用的个人解决方法
使用TP6,因为需要跨域上传图片,一直不成功,网上搜了好久,方法都没解决跨域上传文件 比如下面的方式没成功 $this->app = $app; $this->request = $thi ...
- 解决wampserver无法启动问题
如果无法启动,找不到原因.直接依次点击打开到:控制面板--管理工具--事件查看器--windows日志--应用程序,查看对应进程错误信息对症下药即可. 我这个错误就是8099端口错误,运行cmd命令, ...
- js中针对dom的crud
1.怎样添加.移除.移动.复制.创建和查找节点? 1)创建新节点 createDocumentFragment() //创建一个DOM片段 createElement() //创建一个具体的元素 cr ...
- Windows系统如何找到占用端口的进程并杀掉
1.先建立用户环境变量:C\WINDOWS/system32 2.输入:cmd,打开命令控制台,然后输入ipconfig 3.再输入:netstat -ano(可以找到所有的进程连接端口及对应PID) ...
- springboot pom.xml
Demo project for Spring Boot <?xml version="1.0" encoding="UTF-8"?> <pr ...
- jmeter加密解密(加密篇)
最近刚好在弄jmeter加密解密,可以分享下.(有一段时间没写了,有点不知道从何写起,这篇写的有点乱o(╥﹏╥)o) 需求是:接口中的请求体的部分参数需要先加密再请求,返回的结果中部分字段需解密. 1 ...
- NetCore5实现https请求
前言 本文主要介绍在NetCore5中,实现证书加载和https访问请求. 证书准备 首先我们先创建一个自定义的证书Kiba518.pfx. 证书创建参考:最通俗易懂的RSA加密解密指导. 然后将证书 ...