JavaScript之WebSocket技术详解
概述
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属性。
1
2
3
4
5
|
if (window.WebSocket != undefined) { // WebSocket代码 } |
然后,开始与服务器建立连接(这里假定服务器就是本机的1740端口,需要使用ws协议)。
1
2
3
4
5
|
建立连接以后的WebSocket实例对象(即上面代码中的connection),有一个readyState属性,表示目前的状态,可以取4个值:
0: 正在连接
1: 连接成功
2: 正在关闭
3: 连接关闭
握手协议成功以后,readyState就从0变为1,并触发open事件,这时就可以向服务器发送信息了。我们可以指定open事件的回调函数。
connection.onopen = wsOpen;
1
2
3
|
function wsOpen (event) { console.log(‘Connected to: ‘ + event.currentTarget.URL); } |
关闭WebSocket连接,会触发close事件。
connection.onclose = wsClose;
1
2
3
4
5
|
function wsClose () { console.log(“Closed”); } connection.close(); |
发送数据和接收数据
连接建立后,客户端通过send方法向服务器端发送数据。
connection.send(message);
除了发送字符串,也可以使用 Blob 或 ArrayBuffer 对象发送二进制数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 使用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;
1
2
3
|
function wsMessage (event) { console.log(event.data); } |
上面代码的回调函数wsMessage的参数为事件对象event,该对象的data属性包含了服务器返回的数据。
如果接收的是二进制数据,需要将连接对象的格式设为blob或arraybuffer。
1
2
3
4
5
|
connection.binaryType = 'arraybuffer' ; connection.onmessage = function (e) { console.log(e.data.byteLength); // ArrayBuffer对象有byteLength属性 }; |
处理错误
如果出现错误,浏览器会触发WebSocket实例对象的error事件。
connection.onerror = wsError;
1
2
3
|
function wsError(event) { console.log(“Error: “ + event.data); } |
服务器端
服务器端需要单独部署处理WebSocket的代码。下面用node.js搭建一个服务器环境。
1
2
3
|
var http = require( 'http' ); var server = http.createServer( function (request, response) {}); |
假设监听1740端口。
1
2
3
4
5
|
server.listen(1740, function () { console.log(( new Date()) + ' Server is listening on port 1740' ); }); |
接着启动WebSocket服务器。这需要加载websocket库,如果没有安装,可以先使用npm命令安装。
1
2
3
4
5
6
7
|
var WebSocketServer = require( 'websocket' ).server; var wsServer = new WebSocketServer({ httpServer: server }); |
WebSocket服务器建立request事件的回调函数。
var connection;
wsServer.on(‘request', function(req){
1
2
|
connection = req.accept(‘echo-protocol', req.origin); }); |
上面代码的回调函数接受一个参数req,表示request请求对象。然后,在回调函数内部,建立WebSocket连接connection。接着,就要对connection的message事件指定回调函数。
1
2
3
|
wsServer.on(‘request ', function(r){ connection = req.accept(‘echo-protocol' , req.origin); |
1
2
3
4
5
|
< 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事件。
1
2
3
4
5
|
connection.on( 'close' , function (reasonCode, description) { console.log(connection.remoteAddress + ' disconnected.' ); }); |
使用ws模块,部署一个简单的WebSocket服务器非常容易。
1
2
3
4
5
6
7
8
9
10
|
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框架)。
1
2
3
4
5
6
7
8
9
|
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)的回调函数。
1
2
3
|
socket.on( 'news' , function (data){ console.log(data); }); |
最后,用emit方法向服务器端发送信号,触发服务器端的anotherNews事件。
socket.emit('anotherNews');
请注意,emit方法可以取代Ajax请求,而on方法指定的回调函数,也等同于Ajax的回调函数。
第四步,在服务器端的app.js,加入以下代码。
1
2
3
4
5
6
|
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之WebSocket技术详解的更多相关文章
- 【JavaScript脚本编程技术详解-----(一)】
首先说明,本系列教程是写给有一定的JavaScript编程基础的同学看的,最好还有其它的编程语言经验,因为里面可能涉及一些其它的程序设计语言写的源代码,这都是我自己总结的经验,我喜欢在学习一门新的编程 ...
- 【前端阅读】——《JavaScript应用开发技术详解指南》摘记&思维导图
读这本书,我主要关注三个部分:JavaScript内置函数,程序调试以及Ajax基础.由于多是介绍基本概念,所以,采用思维导图的方式,做了一个梳理,以下就是精简的主要内容. 注:转载请注明出处
- Comet技术详解:基于HTTP长连接的Web端实时通信技术
前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Ser ...
- SSE技术详解:一种全新的HTML5服务器推送事件技术
前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Ser ...
- CDN学习笔记二(技术详解)
一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精华放上网.公诸同 ...
- CDN技术详解及实现原理
CDN技术详解 一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精 ...
- CSS3图片翻转动画技术详解
CSS动画非常的有趣:这种技术的美就在于,通过使用很多简单的属性,你能创建出漂亮的消隐效果.其中代表性的一种就是CSS图片翻转效果,能让你看到一张卡片的正反两面上的内容.本文就是要用最简单的方法向大家 ...
- 了解iOS消息推送一文就够:史上最全iOS Push技术详解
本文作者:陈裕发, 腾讯系统测试工程师,由腾讯WeTest整理发表. 1.引言 开发iOS系统中的Push推送,通常有以下3种情况: 1)在线Push:比如QQ.微信等IM界面处于前台时,聊天消息和指 ...
- Javascript常用的设计模式详解
Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...
随机推荐
- c语言求最大公约数和最小公倍数(转)
最大公约数与最小公倍数的求解是很多初学C的人所面临的一道问题.当然这道问题并不难解答,也有很多人已经写过相关的博客,我在此书写此篇博客,一是为了让自己能够夯实基础,另外就是希望能够帮到和我一样的初学者 ...
- Deinstall卸载RAC之Oracle软件及数据库+GI集群软件
Deinstall卸载Oracle软件及数据库+GI集群软件 1. 本篇文档应用场景: 需要安装新的ORACLE RAC产品,系统没有重装,需要对原环境中的RAC进行卸载: #本篇文档,在AIX 6. ...
- Python网络爬虫第二弹《http和https协议》
一.HTTP协议 1.官方概念: HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文 ...
- [LeetCode&Python] Problem 401. Binary Watch
A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom ...
- lame定理求欧几里得算法的求余和赋值次数
根据lame定理,根据欧几里得算法求(a,b)的最大公因数过程如下(假设a>b):
- java中类与方法叙述正确的是
这道题的4个选项全是错的. A.一个文件中,可以有多个public class public class Main { public class Inner{ } } 即,外部类为public,还 ...
- day 05JVM和深入理解java虚拟机
-----------------Java 虚拟机发展史 PS: Sun公司有 HotSpot, BEA公司有JRockit,IBM有 J9 这三个是高性能VM 在Oracle收购Sun和BEA这两 ...
- torchvision库简介(翻译)
部分跟新于:4.24日 torchvision 0.2.2.post3 torchvision是独立于pytorch的关于图像操作的一些方便工具库. torchvision的详细介绍在:http ...
- Centos7使用PXE+Kickstart无人值守安装服务
首先,关闭selinux和防火墙(二次修改版本) 我们需要安装的软件如下 dhcp(用来获取ip) tftp-server httpd syslinux(不安装这个后面会没有pxelinux.0这个文 ...
- Gravitee.io docker-compose运行
Gravitee.io 是一个相对比较完整的api gateway 平台,包含了api 相对比较完整的生命周期管理 同时在访问控制以及日志监控上也做的比较好,是一款可以尝试试用的api gateway ...