JavaScript之WebSocket技术详解


Posted in Javascript onNovember 18, 2016

概述

HTTP协议是一种无状态协议,服务器端本身不具有识别客户端的能力,必须借助外部机制,比如session和cookie,才能与特定客户端保持对话。这多多少少带来一些不便,尤其在服务器端与客户端需要持续交换数据的场合(比如网络聊天),更是如此。为了解决这个问题,HTML5提出了浏览器的WebSocket API。

WebSocket的主要作用是,允许服务器端与客户端进行全双工(full-duplex)的通信。举例来说,HTTP协议有点像发电子邮件,发出后必须等待对方回信;WebSocket则是像打电话,服务器端和客户端可以同时向对方发送数据,它们之间存着一条持续打开的数据通道。

WebSocket协议完全可以取代Ajax方法,用来向服务器端发送文本和二进制数据,而且还没有“同域限制”。

WebSocket不使用HTTP协议,而是使用自己的协议。浏览器发出的WebSocket请求类似于下面的样子:

GET / HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Host: example.com
Origin: null
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
上面的头信息显示,有一个HTTP头是Upgrade。HTTP1.1协议规定,Upgrade头信息表示将通信协议从HTTP/1.1转向该项所指定的协议。“Connection: Upgrade”就表示浏览器通知服务器,如果可以,就升级到webSocket协议。Origin用于验证浏览器域名是否在服务器许可的范围内。Sec-WebSocket-Key则是用于握手协议的密钥,是base64编码的16字节随机字符串。

服务器端的WebSocket回应则是

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Origin: null
Sec-WebSocket-Location: ws://example.com/

服务器端同样用“Connection: Upgrade”通知浏览器,需要改变协议。Sec-WebSocket-Accept是服务器在浏览器提供的Sec-WebSocket-Key字符串后面,添加“258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 字符串,然后再取sha-1的hash值。浏览器将对这个值进行验证,以证明确实是目标服务器回应了webSocket请求。Sec-WebSocket-Location表示进行通信的WebSocket网址。

请注意,WebSocket协议用ws表示。此外,还有wss协议,表示加密的WebSocket协议,对应HTTPs协议。
完成握手以后,WebSocket协议就在TCP协议之上,开始传送数据。

WebSocket协议需要服务器支持,目前比较流行的实现是基于node.js的socket.io,更多的实现可参阅Wikipedia。至于浏览器端,目前主流浏览器都支持WebSocket协议(包括IE 10+),仅有的例外是手机端的Opera Mini和Android Browser。

客户端

浏览器端对WebSocket协议的处理,无非就是三件事:

建立连接和断开连接
发送数据和接收数据
处理错误

建立连接和断开连接

首先,客户端要检查浏览器是否支持WebSocket,使用的方法是查看window对象是否具有WebSocket属性。

if(window.WebSocket != undefined) {

 // WebSocket代码

}

然后,开始与服务器建立连接(这里假定服务器就是本机的1740端口,需要使用ws协议)。

if(window.WebSocket != undefined) {

 var connection = new WebSocket('ws://localhost:1740');

}

建立连接以后的WebSocket实例对象(即上面代码中的connection),有一个readyState属性,表示目前的状态,可以取4个值:

0: 正在连接
1: 连接成功
2: 正在关闭
3: 连接关闭
握手协议成功以后,readyState就从0变为1,并触发open事件,这时就可以向服务器发送信息了。我们可以指定open事件的回调函数。

connection.onopen = wsOpen;

function wsOpen (event) { 
console.log(‘Connected to: ‘ + event.currentTarget.URL); 
}

关闭WebSocket连接,会触发close事件。

connection.onclose = wsClose;

function wsClose () { 
console.log(“Closed”); 
}

connection.close();

发送数据和接收数据

连接建立后,客户端通过send方法向服务器端发送数据。

connection.send(message);
除了发送字符串,也可以使用 Blob 或 ArrayBuffer 对象发送二进制数据。

// 使用ArrayBuffer发送canvas图像数据

var img = canvas_context.getImageData(0, 0, 400, 320);

var binary = new Uint8Array(img.data.length);

for (var i = 0; i < img.data.length; i++) {

 binary[i] = img.data[i];

}

connection.send(binary.buffer);
// 使用Blob发送文件 
var file = document.querySelector(‘input[type=”file”]').files[0]; 
connection.send(file);

客户端收到服务器发送的数据,会触发message事件。可以通过定义message事件的回调函数,来处理服务端返回的数据。

connection.onmessage = wsMessage;

function wsMessage (event) { 
console.log(event.data); 
}

上面代码的回调函数wsMessage的参数为事件对象event,该对象的data属性包含了服务器返回的数据。

如果接收的是二进制数据,需要将连接对象的格式设为blob或arraybuffer。

connection.binaryType = 'arraybuffer';

connection.onmessage = function(e) {
 console.log(e.data.byteLength); // ArrayBuffer对象有byteLength属性
};

处理错误

如果出现错误,浏览器会触发WebSocket实例对象的error事件。

connection.onerror = wsError;

function wsError(event) { 
console.log(“Error: “ + event.data); 
}

服务器端

服务器端需要单独部署处理WebSocket的代码。下面用node.js搭建一个服务器环境。

var http = require('http');

var server = http.createServer(function(request, response) {});

假设监听1740端口。

server.listen(1740, function() {

 console.log((new Date()) + ' Server is listening on port 1740');

});

接着启动WebSocket服务器。这需要加载websocket库,如果没有安装,可以先使用npm命令安装。

var WebSocketServer = require('websocket').server;

var wsServer = new WebSocketServer({

 httpServer: server

});

WebSocket服务器建立request事件的回调函数。

var connection;
wsServer.on(‘request', function(req){

connection = req.accept(‘echo-protocol', req.origin); 
});

上面代码的回调函数接受一个参数req,表示request请求对象。然后,在回调函数内部,建立WebSocket连接connection。接着,就要对connection的message事件指定回调函数。

wsServer.on(‘request', function(r){

 connection = req.accept(‘echo-protocol', req.origin);
<span class="nx">connection</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
 <span class="kd">var</span> <span class="nx">msgString</span> <span class="o">=</span> <span class="nx">message</span><span class="p">.</span><span class="nx">utf8Data</span><span class="p">;</span>
 <span class="nx">connection</span><span class="p">.</span><span class="nx">sendUTF</span><span class="p">(</span><span class="nx">msgString</span><span class="p">);</span>
<span class="p">});</span>
});

最后,监听用户的disconnect事件。

connection.on('close', function(reasonCode, description) {

 console.log(connection.remoteAddress + ' disconnected.');

});

使用ws模块,部署一个简单的WebSocket服务器非常容易。

var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
 ws.on('message', function incoming(message) {
 console.log('received: %s', message);
 });

 ws.send('something');
});

Socket.io简介

Socket.io是目前最流行的WebSocket实现,包括服务器和客户端两个部分。它不仅简化了接口,使得操作更容易,而且对于那些不支持WebSocket的浏览器,会自动降为Ajax连接,最大限度地保证了兼容性。它的目标是统一通信机制,使得所有浏览器和移动设备都可以进行实时通信。

第一步,在服务器端的项目根目录下,安装socket.io模块。

$ npm install socket.io

第二步,在根目录下建立app.js,并写入以下代码(假定使用了Express框架)。

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);

server.listen(80);

app.get('/', function (req, res) {
 res.sendfile(__dirname + '/index.html');
});

上面代码表示,先建立并运行HTTP服务器。Socket.io的运行建立在HTTP服务器之上。

第三步,将Socket.io插入客户端网页。

<script src="/socket.io/socket.io.js"></script>

然后,在客户端脚本中,建立WebSocket连接。

var socket = io.connect('http://localhost:80');

由于本例假定WebSocket主机与客户端是同一台机器,所以connect方法的参数是http://localhost。接着,指定news事件(即服务器端发送news)的回调函数。

socket.on('news', function (data){
 console.log(data);
});

最后,用emit方法向服务器端发送信号,触发服务器端的anotherNews事件。

socket.emit('anotherNews');

请注意,emit方法可以取代Ajax请求,而on方法指定的回调函数,也等同于Ajax的回调函数。
第四步,在服务器端的app.js,加入以下代码。

io.sockets.on('connection', function (socket) {
 socket.emit('news', { hello: 'world' });
 socket.on('anotherNews', function (data) {
 console.log(data);
 });
});

上面代码的io.sockets.on方法指定connection事件(WebSocket连接建立)的回调函数。在回调函数中,用emit方法向客户端发送数据,触发客户端的news事件。然后,再用on方法指定服务器端anotherNews事件的回调函数。

不管是服务器还是客户端,socket.io提供两个核心方法:emit方法用于发送消息,on方法用于监听对方发送的消息。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery 弹出框定位实现方法
Dec 02 Javascript
web前端开发也需要日志
Dec 09 Javascript
JavaScript ECMA-262-3 深入解析.第三章.this
Sep 28 Javascript
一个页面放2段图片滚动代码出现冲突的问题如何解决
Dec 21 Javascript
原生JS实现加入收藏夹的代码
Oct 24 Javascript
IE8中使用javascript动态加载CSS的解决方法
Jun 17 Javascript
JS实现的车标图片提示效果代码
Oct 10 Javascript
jquery插件格式实例分析
Jun 16 Javascript
利用Node.js了解与测量HTTP所花费的时间详解
Sep 22 Javascript
Angular4编程之表单响应功能示例
Dec 13 Javascript
Bootstarp在pycharm中的安装及简单的使用方法
Apr 19 Javascript
实用Javascript调试技巧分享(小结)
Jun 18 Javascript
仿iframe效果Aajx文件上传实例
Nov 18 #Javascript
JavaScript之cookie技术详解
Nov 18 #Javascript
js前端解决跨域问题的8种方案(最新最全)
Nov 18 #Javascript
js前端实现多图图片上传预览的两个方法(推荐)
Nov 18 #Javascript
js实时获取窗口大小变化的实例代码
Nov 18 #Javascript
Kendo Grid editing 自定义验证报错提示的解决方法
Nov 18 #Javascript
整理一下常见的IE错误
Nov 18 #Javascript
You might like
PHP中for循环语句的几种变型
2006/11/26 PHP
PHP访问Google Search API的方法
2015/03/05 PHP
javascript语句中的CDATA标签的意义
2007/05/09 Javascript
javascript showModalDialog,open取得父窗口的方法
2010/03/10 Javascript
jquery 注意事项与常用语法小结
2010/06/07 Javascript
JavaScript中清空数组的三种方法分享
2011/04/07 Javascript
js根据给定的日期计算当月有多少天实现思路及代码
2013/02/25 Javascript
javascript教程:关于if简写语句优化的方法
2014/05/17 Javascript
简介JavaScript中getUTCMonth()方法的使用
2015/06/10 Javascript
Vue自定义指令介绍(2)
2016/12/08 Javascript
canvas红包照片实例分享
2017/02/28 Javascript
web前端vue filter 过滤器
2018/01/12 Javascript
浅谈React + Webpack 构建打包优化
2018/01/23 Javascript
Vue指令v-for遍历输出JavaScript数组及json对象的常见方式小结
2019/02/11 Javascript
实例分析Array.from(arr)与[...arr]到底有何不同
2019/04/09 Javascript
node.js 基于cheerio的爬虫工具的实现(需要登录权限的爬虫工具)
2019/04/10 Javascript
Python使用reportlab将目录下所有的文本文件打印成pdf的方法
2015/05/20 Python
python制作最美应用的爬虫
2015/10/28 Python
Python实现嵌套列表及字典并按某一元素去重复功能示例
2017/11/30 Python
Python paramiko模块的使用示例
2018/04/11 Python
使用NumPy和pandas对CSV文件进行写操作的实例
2018/06/14 Python
Python3自动签到 定时任务 判断节假日的实例
2018/11/13 Python
Python操作json的方法实例分析
2018/12/06 Python
如何运行.ipynb文件的图文讲解
2019/06/27 Python
python ubplot使用方法解析
2020/01/10 Python
canvas绘图按照contain或者cover方式适配并居中显示
2019/02/18 HTML / CSS
德国家具在线:Fashion For Home
2017/03/11 全球购物
国际商务专业学生个人的自我评价
2013/09/28 职场文书
旅游管理专业个人求职信范文
2013/12/24 职场文书
工业学校毕业生自荐书
2014/01/03 职场文书
护士医德医风自我评价
2014/09/15 职场文书
2015年个人实习工作总结
2014/12/12 职场文书
九华山导游词
2015/02/03 职场文书
Python竟然能剪辑视频
2021/05/25 Python
Vue实现跑马灯样式文字横向滚动
2021/11/23 Vue.js
讲解Python实例练习逆序输出字符串
2022/05/06 Python