你好,websocket

你好,WebSocket

定义

WebSocket 是 HTML5 开始提供的一种在单个 TCP 链接上进行全双工通信的协议(注意是协议)。前端

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,容许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只须要完成一次握手,二者之间就直接能够建立持久性的链接,并进行双向数据传输。node

为何使用WebSocket

本来的经过HTTP协议的客户端与服务端的通讯,只能由客户端发起。HTTP 协议作不到服务器主动向客户端推送信息。并且一次客户端与服务端的通讯完成以后,客户端与服务端的连接就会断开,可是WebSocket是长连接,当一次链接创建之后,若是不是其中一端主动断开链接,两端的链接就会一直链接着。web

WebSocket是为了解决相似聊天室的场景,本来HTTP协议这种单向请求的特色,注定了若是服务器有连续的状态变化,客户端要获知就很是麻烦。咱们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。轮询的效率低,很是浪费资源(由于必须不停链接,或者 HTTP 链接始终打开)。所以,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。ajax

一:组成和特色

长链接:一个链接上能够连续发送多个数据包,在链接期间,若是没有数据包发送,须要双方发链路检查包。express

TCP/IP:TCP/IP属于传输层,主要解决数据在网络中的传输问题,只管传输数据。可是那样对传输的数据没有一个规范的封装、解析等处理,使得传输的数据就很难识别,因此才有了应用层协议对数据的封装、解析等,如HTTP协议。npm

HTTP:HTTP是应用层协议,封装解析传输的数据。
从HTTP1.1开始其实就默认开启了长链接,也就是请求header中看到的Connection:Keep-alive。可是这个长链接只是说保持了(服务器能够告诉客户端保持时间Keep-Alive:timeout=200;max=20;)这个TCP通道,直接Request - Response,而不须要再建立一个链接通道,作到了一个性能优化。可是HTTP通信自己仍是Request - Response。segmentfault

socket:与HTTP不同,socket不是协议,它是在程序层面上对传输层协议(能够主要理解为TCP/IP)的接口封装。
咱们知道传输层的协议,是解决数据在网络中传输的,那么socket就是传输通道两端的接口。因此对于前端而言,socket也能够简单的理解为对TCP/IP的抽象协议。浏览器

WebSocket:
WebSocket是包装成了一个应用层协议做为socket,从而可以让客户端和远程服务端经过web创建全双工通讯。websocket提供ws和wss两种URL方案。性能优化

WebSocket 实例
WebSocket 协议本质上是一个基于 TCP 的协议。服务器

为了创建一个 WebSocket 链接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和一般的 HTTP 请求不一样,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"代表这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息而后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 链接就创建起来了,双方就能够经过这个链接通道自由的传递信息,而且这个链接会持续存在直到客户端或者服务器端的某一方主动的关闭链接。

图片描述图片描述

二:WebSocket使用

建立WebSocket对象

var ws = new WebSocket(url, [protocol] );

以上代码中的第一个参数 url, 指定链接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

1:属性

ws.readyState:

0 - 表示链接还没有创建。

1 - 表示链接已创建,能够进行通讯。

2 - 表示链接正在进行关闭。

3 - 表示链接已经关闭或者链接不能打开。

ws.bufferedAmount 实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去。它能够用来判断发送是否结束。

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}
2:事件

open: ws.onopen 链接创建时触发

ws.onopen = function () {
  ws.send('Hello Server!');
}

若是要指定多个回调函数,可使用addEventListener方法。

ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});

message:ws.onmessage 客户端接收服务端数据时触发

注意,服务器数据多是文本,也多是二进制数据(blob对象或Arraybuffer对象)。

error: ws.onerror 通讯发生错误时触发

close: ws.onclose 链接关闭时触发

3:方法

ws.send() 向服务器发送数据

ws.close() 关闭链接

三:websocket的坑

1:WebSocket心跳及重连机制

在使用websocket的过程当中,有时候会遇到网络断开的状况,可是在网络断开的时候服务器端并无触发onclose的事件。这样会有:服务器会继续向客户端发送多余的连接,而且这些数据还会丢失。因此就须要一种机制来检测客户端和服务端是否处于正常的连接状态。所以就有了websocket的心跳了。还有心跳,说明还活着,没有心跳说明已经挂掉了。

实现心跳检测的思路是:每隔一段固定的时间,向服务器端发送一个ping数据,若是在正常的状况下,服务器会返回一个pong给客户端,若是客户端经过
onmessage事件能监听到的话,说明请求正常

实现心跳检测的思路是:每隔一段固定的时间,向服务器端发送一个ping数据,若是在正常的状况下,服务器会返回一个pong给客户端,若是客户端经过
onmessage事件能监听到的话,说明请求正常

心跳链接:
 //添加心跳防止断开链接
  let time=setInterval(() => {     
    ws.send('heartbeat')
  }, 9*60*1000);        //每9分钟 发送一次 防止断开

2:websocket和http长链接的区别

咱们都知道如今咱们广泛使用的HTTP1.1,也是有长链接的,也就是keep-alive。因此在学习的时候咱们必定会有疑问:不都是长链接吗?有啥区别啊?

先说 comet 和 WebSocket 表现的区别:
comet 发送 http 请求后服务端若是没有返回则链接是一直连着的,等服务端有东西要“推送”给浏览器时,至关于给以前发送的这个 http 请求回了一个 http 响应。而后这个保持的时间比较长的 http 链接就断了。而后浏览器再次发送一个 http 请求,服务器端再 hold 住不返回,等待有东西须要“推送”给浏览器时,再给这个 http 请求一个响应,而后断开链接。循环往复。一旦浏览器不给服务器发送 http 请求,那么服务器是不能主动给浏览器推送消息的,由于根本没有连着的链接给你推。

WebSocket 则不一样,它握手后创建的链接是不会断的(除了意外状况和程序主动掐断)。不须要浏览器在每次收到服务器推送的消息后再发起请求。并且服务器端能够随时给浏览器推送消息,不须要等浏览器发 http 请求,由于 WebSocket 的链接一直在没断。

为何会有这样的区别?

这是协议层面的区别。http 协议规定了 http 链接是一个一来(request)一回(response)的过程。一个请求得到一个响应后必须断掉。并且只有先有请求才会有响应。拿 http1.1 keep-alive 来讲,即便底层 tcp 链接没有断,服务端平白无故给浏览器发一个 http 响应,浏览器是不收的,他找不到收的人啊,由于这个响应没有对应的请求。你看 ajax 必须先发请求才会有一个 onsuccess 回调来响应这个请求。这个 onsuccess 的回调会在你 ajax 不发送的状况下被调用到吗?

而 WebSocket 协议不一样,他经过握手以后规定说你链接给我保持着,别断咯。因此浏览器服务器在这种状况下能够相互的发送消息。浏览器端 new 一个 WebSocket 以后注册 onmessage 回调,那么这个 onmessage 能够被反复调用,只要服务器端有消息过来。而不会说是 new 一个 WebSocket onmessage 只会被调用一次,下次还得再 new 一个 websocket。

上面说到 http 链接,tcp 链接,websockt 链接到底啥区别。其实这是新人最容易搞不懂的地方。接下来我就要胡诌了,为啥说胡诌,由于我只是看了个皮毛,而后按我本身的理解说下区别。网络5层分层(自下而上):

物理层

数据链路层

网络层

传输层

应用层

http,websocket都是应用层协议,他们规定的是数据怎么封装,而他们传输的通道是下层提供的。就是说不管是 http 请求,仍是 WebSocket 请求,他们用的链接都是传输层提供的,即 tcp 链接(传输层还有 udp 链接)。只是说 http1.0 协议规定,你一个请求得到一个响应后,你要把链接关掉。因此你用 http 协议发送的请求是没法作到一直连着的(若是服务器一直不返回也能够保持至关一段时间,可是也会有超时而被断掉)。而 WebSocket 协议规定说等握手完成后咱们的链接不能断哈。虽然 WebSocket 握手用的是 http 请求,可是请求头和响应头里面都有特殊字段,当浏览器或者服务端收到后会作相应的协议转换。因此 http 请求被 hold 住不返回的长链接和 WebSocket 的链接是有本质区别的。

参考链接:https://segmentfault.com/a/11...

四:node.js+express+websocket实现服务端推送

使用socket.io

socket.io文档地址:https://socket.io/docs/

不懂得地方能够看文档

开始:
npm install socket.io socket.io-client --save
1:客户端
let socket = io("ws://localhost:3001"); // 创建连接
    socket.emit('msg', {message:"你好服务器,我是客户端"}); //向服务器发送消息

    socket.on('msg',function(data){ // 监听服务端的消息“msg”
        console.log(data);
    });
2:服务端代码
let express = require('express');
let router = express.Router();
let app = require('express')();
let server = require('http').createServer(app);
let io = require('socket.io')(server);

io.on('connection', function(socket){ // socket相关监听都要放在这个回调里
    console.log('a user connected');
    
    // 链接断开时触发的回调
    socket.on("disconnect", function() {
        console.log("a user go out");
    });
     
    socket.on("msg", function(obj) {
      // 打印客户端传过来的消息
      let { message } = obj
      console.log(message);
      
        //延迟3s返回信息给客户端
        setTimeout(function(){
            io.emit("msg", '你好客户端,我是服务器');
        },3000);
    });
});
//开启端口监听socket
server.listen(3001);
 
router.get('/', function(req, res, next) {
    res.render('im/imRoom');
});
module.exports = router;

运行结果:

服务端:
a user connected
你好服务器,我是客户端

服务端:
你好客户端,我是服务器(3秒后)

本人菜鸟,以上就是我对websocket的一些学习,不少不足之后会持续进行补充。

相关文章
相关标签/搜索