资讯专栏INFORMATION COLUMN

浅谈 WebSocket

Jensen / 594人阅读

摘要:接口用于接收服务器发送的事件。因此,是目前来说最佳的选择。最大特点就是,服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送信息,是一种不受限的全双工通信。若是,则交给的回调函数处理,否则,还是走正常的回调的路子。

使用 WebSocket 的理由

传统的http协议有一个根本性的缺陷,那就是请求只能由客户端向服务器发起,服务器接收到请求后再进行响应,把数据返回给客户端。也就是说,服务器是没有办法主动向客户端传送消息的。

这样一来,如果服务器有状态是频繁变化的,那么,客户端想要实时获悉这些状态势必非常麻烦。如在线多人游戏,聊天室等。

一种可行的解决方案是使用轮询。轮询是指浏览器通过JavaScript启动一个定时器,然后以固定的间隔给服务器发请求,询问服务器有没有新消息。

这种机制不仅效率低下,实时性不够,而且频繁地发起请求也会给服务器带来极大的压力。

另外一种比较靠谱的技术是HTML5的EventSource。EventSource 接口用于接收服务器发送的事件。它通过HTTP连接到一个服务器,以text/event-stream 格式接收事件, 不关闭连接。

相对于WebSocket,这种技术要简单很多,但是其只是从服务器端往客户端单向传输数据,并不能实现真正意义上的全双工通信。因此,WebSocket是目前来说最佳的选择。

有兴趣了解EventSource的小伙伴可以点击这里

WebSocket 协议

WebSocket是HTML5新增的协议,其诞生于2008年。最大特点就是,服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送信息,是一种不受限的全双工通信。

该协议有以下特征:

握手阶段利用了HTTP协议来建立连接,因此WebSocket连接必须由浏览器发起。

建立在 TCP 协议之上,服务器端的实现比较容易。

其可以接收和发送的数据有两种,一种是文本,一种是二进制数据(blob对象或Arraybuffer对象)。一般情况下,我们可以发送JSON格式的文本,这样,在浏览器处理起来就十分容易。

数据格式比较轻量,性能开销小,通信高效。

请求是以ws://为开头的地址(如果加密,则为wss://)。

WebSocket协议本身不要求同源策略,也就是某个地址为"http://a.com"的网页可以通过WebSocket连接到"ws://b.com"。但是,浏览器会发送Origin的HTTP头给服务器,服务器可以根据Origin拒绝这个WebSocket请求。

浏览器支持情况

Chrome

Firefox

IE >= 10

Sarafi >= 6

Android >= 4.4

iOS >= 8

服务端实现

不同的编程语言和框架,实现方式各有不同。这里主要讲一下用node如何实现。

node常用的实现有一下几种:

Socket.IO

WebSocket-Node

µWebSockets

具体如何使用可以查看它们各自的api。下面,我要详细介绍的是另一个WebSocket模块ws。

通过npm install ws --save之后,我们就可以可以编写一个简单WebSocket服务器程序。

// 首先导入ws模块
let WebSocket = require("ws");

// 通过ws模块的Server类实例化一个websocket服务器
let webSocketServer = new WebSocket.Server({
  port: 8030
}, err => {
  console.log("The WebSocket Server already running on: 8030");
});

// 监听客户端请求接入的connection事件,连接建立后,回调函数中会传入这个WebSocket连接实例
webSocketServer.on("connection", ws => {
  console.log(`Server is connected`)
  // 对于每个WebSocket连接,可以绑定监听某些事件来进行不同的处理。这里,通过响应message事件,在收到客户端发来消息后再返回一个消息过去。
  ws.on("message", mes => {
    console.log(`Message sent by client: ${mes}`);
    ws.send(`data responded by Server: ${mes}`, err => {
      if (err) {
        console.log(`Server error: ${err}`);
      }
    })
  })
})

也可以对http服务器进行拓展,在其基础上建立WebSocket服务器。

const Koa = require("koa");
const WebSocket = require("ws");
const bodyParser = require("koa-bodyparser");
const controller = require("./controller.js");

const server = new Koa();

const webSocketServer = new WebSocket.Server({server}, () => console.log("The WebSocket Server already running on: 8030"));

// 为websocket服务器添加一个broadcast()方法
webSocketServer.broadcast = data => {
    // 通过遍历webSocketServer.clients,找到所有与该服务器成功建立websocket连接的客户端,发送同一条消息
    for (const client of webSocketServer.clients) {
        if (client.readyState === WebSocket.OPEN) {
            client.send(data, err => console.log(`Server error: ${err}`));
        }
    }
}

webSocketServer.on("connection", ws => {
    console.log(`Server is connected`);
    ws.on("message", mes => {
        console.log(`Message sent by client: ${mes}`);
        // 接受到其中一个客户端发来的消息后,广播给所有同时连接过来的客户端
        const data = {
            message: mes
        }
        webSocketServer.broadcast(JSON.stringify(data))
    })
})

server.use(bodyParser());

server.use(controller());

server.listen(8030);
console.log("server running on 8030...");

现在,websocket服务器与http服务器同时使用8030端口。当有一个请求发送过来,首先会判断其是否ws请求。若是,则交给WebSocketServer的回调函数处理,否则,还是走正常的http server回调的路子。

另外,我们注意到,这里还给WebSocketServer添加了一个broadcast()方法,用于将消息广播到所有与该服务器成功建立WebSocket连接的客户端上。

每当从其中一个客户端收到一条消息,就将该消息发送到所有WebSocket连接上,基于这种方式,我们就可以搭建一个聊天室应用的后台服务。

ws模块的完整使用方法请看这里

客户端实现

客户端要创建一个WebSocket连接就比较简单了。下面是一个简单的事例:

// 创建一个WebSocket连接:
var ws = new WebSocket("ws://localhost:8030/ws");

ws.onopen = function(event) { 
  console.log("Connection open"); 
  // 给服务器发送一个消息:
  ws.send("Hello WebSocket!");
};

// 响应onmessage事件:
ws.onmessage = function(event) { 
  console.log(event.data); 
};

// 也可以指定接收的二进制数据类型为blob对象
ws.binaryType = "blob";
ws.onmessage = function(event) {
  console.log(event.data.size);
};

// 或

// 指定接收的二进制数据类型为ArrayBuffer对象
ws.binaryType = "arraybuffer";
ws.onmessage = function(event) {
  console.log(event.data.byteLength);
};

详细的属性和方法可以看这里

另外,使用ws模块提供的WebSocket构造函数也可以充当客户端创建连接。

let ws = new WebSocket("ws://localhost:8030/ws");

// 打开WebSocket连接后立刻发送一条消息
ws.on("open", () => {
    console.log(`Client open`);
    ws.send("Hello WebSocket!");
});

// 响应收到的消息
ws.on("message", mes => {
    console.log(mes);
});
参考链接

http://www.ruanyifeng.com/blo...

https://www.liaoxuefeng.com/w...

http://es6.ruanyifeng.com/#do...

https://developer.mozilla.org...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99736.html

相关文章

  • 浅谈前端跨域

    摘要:一什么是跨域跨域简单的理解就是同源策略的限制。同源策略限制的内容请求不能正常进行。同源策略默认地址是网页的本身。 一、什么是跨域? 跨域简单的理解就是JavaScript同源策略的限制。是出于安全的考虑,a.com域名下的js不能操作b.com或者c.com域名下的对象。 当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算叫跨域。 一个正常...

    dunizb 评论0 收藏0
  • 浅谈前端的模块化

    摘要:关于模块化的一点想法对于模块化,我还是想说明一下,模块化重要的是思想,而不是实现。而且这样的模块,复用基本是零。 没有模块化之前,我们是怎么开发的 刚刚从业那会,公司是没用模块化的,那代码是怎么组织的呢?首先有一个common.js,这里面放的是一些公用的东西,比如:用户的登录注册、工具类、经常用到的全局变量、还有一些常用的函数;还有一个head.js,这里面放的是一些网站头部的一些东...

    freewolf 评论0 收藏0
  • Java学习路线总结,搬砖工逆袭Java架构师(全网最强)

    摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...

    Scorpion 评论0 收藏0
  • 2018 浅谈前端面试那些事

    摘要:声明的变量不得改变值,这意味着,一旦声明变量,就必须立即初始化,不能留到以后赋值。 虽然今年没有换工作的打算 但为了跟上时代的脚步 还是忍不住整理了一份最新前端知识点 知识点汇总 1.HTML HTML5新特性,语义化浏览器的标准模式和怪异模式xhtml和html的区别使用data-的好处meta标签canvasHTML废弃的标签IE6 bug,和一些定位写法css js放置位置和原因...

    LiuRhoRamen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<