文档章节

WebSocket的简单介绍及应用

北海vs
 北海vs
发布于 2016/03/31 23:22
字数 2203
阅读 71
收藏 9

web开发中可能遇到这样的场景:网页里的某一块区域里写了一些内容,但这些内容不是固定的,即使看网页的人没有做任何操作,它们也会随时间不断变化。

 

  对此,一般的做法是用setTimeout()或setInverval()定时执行任务,任务内容是Ajax访问一次服务器,并在成功拿到返回数据后去更新页面。

 

  这种定时刷新的做法会有这样一些感觉不足的地方:

  •   频繁的定时网络请求对浏览器(客户端)和服务器来说都是一种负担,尤其是当网页里有多个定时刷新区域的时候。
  •   某几次的定时任务可能是不必要的,因为服务器可能并没有新数据,还是返回了和上一次一样的内容。
  •   页面内容可能不够新,因为服务器可能刚更新了数据,但下一轮定时任务还没有开始。

 

  造成这些不足的原因归结起来,主要还是由于服务器的响应总是被动的。HTTP协议限制了一次通信总是由客户端发起请求,再由服务器端来返回响应。

 

  因此,如果让服务器端也可以主动发送信息到客户端,就可以很大程度改进这些不足。WebSocket就是一个实现这种双向通信的新协议。

 

  WebSocket是基于HTTP的功能追加协议

 

  WebSocket最初由html5提出,但现在已经发展为一个独立的协议标准。WebSocket可以分为协议(Protocol)和API两部分,分别由IETF和W3C制定了标准。

 

  先来看看WebSocket协议的建立过程。

 

  为了实现WebSocket通信,首先需要客户端发起一次普通HTTP请求(也就是说,WebSocket的建立是依赖HTTP的)。请求报文可能像这样:

1 GET ws://websocket.example.com/ HTTP/1.1
2 Host: websocket.example.com
3 Upgrade: websocket
4 Connection: Upgrade
5 Origin: http://example.com
6 Sec-WebSocket-Key:pAloKxsGSHtpIHrJdWLvzQ==
7 Sec-WebSocket-Version:13


  其中HTTP头部字段Upgrade: websocket和Connection: Upgrade很重要,告诉服务器通信协议将发生改变,转为WebSocket协议。支持WebSocket的服务器端在确认以上请求后,应返回状态码为101 Switching Protocols的响应:

1 HTTP/1.1 101 Switching Protocols
2 Upgrade: websocket
3 Connection: Upgrade
4 Sec-WebSocket-Accept: nRu4KAPUPjjWYrnzxDVeqOxCvlM=


  其中字段Sec-WebSocket-Accept是由服务器对前面客户端发送的Sec-WebSocket-Key进行确认和加密后的结果,相当于一次验证,以帮助客户端确信对方是真实可用的WebSocket服务器。

 

  验证通过后,这个握手响应就确立了WebSocket连接,此后,服务器端就可以主动发信息给客户端了。此时的状态比较像服务器端和客户端接通了电话,无论是谁有什么信息想告诉对方,开口就好了。

 

  一旦建立了WebSocket连接,此后的通信就不再使用HTTP了,改为使用WebSocket独立的数据帧(这个帧有办法看到,见后文)。

 

  整个过程像这样:

 

  简单的应用示例

  应用WebSocket有这样几件事要做:

  •   选用支持WebSocket的浏览器。
  •   网页内添加创建WebSocket的代码。
  •   服务器端添加使用WebSocket通信的代码。

 

  服务器端

  以Node的服务器为例,我们使用ws这个组件,这样搭建一个支持WebSocket的服务器端:

01 request("http://uinames.com/api?region=china",
02         function(error, response, body) {
03             if (!error && response.statusCode === 200) {
04                 var jsonObject = JSON.parse(body),
05                     guest = jsonObject.name + jsonObject.surname,
06                     guestInfo = {
07                         guest: guest,
08                         time: dateFormat(new Date(), "HH:MM:ss")
09                     };
10  
11                 if (ws.readyState === WebSocket.OPEN) {
12  
13                     // 发,送
14                     ws.send(JSON.stringify(guestInfo));
15  
16                     // 用随机来“装”得更像不定时推送一些
17                     setTimeout(function() {
18                         sendGuestInfo(ws);
19                     }, (Math.random() * 5 + 3) * 1000);
20                 }
21             }
22         });
23 }

  这个例子使用了姓名生成站点uinames的API服务,来生成{guest: "人名", time:"15:26:01"}这样的数据。函数sendGuestInfo()会不定时执行,并把包含姓名和时间的信息通过send()方法发送给客户端。另外,注意send()方法需要以字符串形式来发送json数据。

 

  这就像是服务器自己在做一些事,然后在需要的时候会通知客户端一些信息。

 

  客户端

 

  客户端我们使用原生javascript来完成(仅支持WebSocket的浏览器):

01 var socket = new WebSocket("ws://localhost:8080/guest");
02  
03 socket.onopen = function(openEvent) {
04     console.log("WebSocket conntected.");
05 };
06  
07 socket.onmessage = function(messageEvent) {
08     var data = messageEvent.data,
09         dataObject = JSON.parse(data);
10     console.log("Guest at " + dataObject.time + ": " + dataObject.guest);
11 };
12  
13 socket.onerror = function(errorEvent) {
14     console.log("WebSocket error: ", errorEvent);
15 };
16  
17 socket.onclose = function(closeEvent) {
18     console.log("WebSocket closed.");
19 };

  WebSocket的URL格式是ws://与wss://。因此,需要注意下URL地址的写法,这也包括注意WebSocket服务器端的路径(如这里的/guest)等信息。因为是本地的示例所以这里是localhost。

 

  客户端代码的流程很简单:创建WebSocket对象,然后指定onopen、onmessage等事件的回调即可。其中onmessage是客户端与服务器端通过WebSocket通信的关键事件,想要在收到服务器通知后做点什么,写在onmessage事件的回调函数里就好了。

 

  效果及分析

 

  通过node server(假定服务器端的文件名为server.js)启动WebSocket服务器后,用浏览器打开一个引入了前面客户端代码的html(直接文件路径file:///就可以),就可以得到像这样的结果:

  联系前面客户端的代码可以想到,实际从创建WebSocket对象的语句开始,连接请求就会发送,并很快建立起WebSocket连接(不出错误的话),此后就可以收到来自服务器端的通知。如果此时客户端还想再告诉服务器点什么,这样做:

1 socket.send("Hello, server!");


  服务器就可以收到:

  当然,这也是因为前面服务器端的代码内同样设置了message事件的回调。在这个客户端和服务器都是javascript的例子中,无论是服务器端还是客户端,都用send()发送信息,都通过message事件设置回调,形式上可以说非常一致。

 

  其他可用的数据类型

 

  WebSocket的send()可以发送的消息,除了前面用的字符串类型之外,还有两种可用,它们是Blob和ArrayBuffer。

 

  它们都代表二进制数据,可用于原始文件数据的发送。比如,这是一个发送Blob类型数据以完成向服务器上传图片的例子:

1 var fileEl = document.getElementById("image_upload");
2 var file = fileEl.files[0];
3 socket.send(file);


  然后服务器端可以这样把文件保存下来:

01 var fs = require("fs");
02  
03 wss.on("connection", function(ws) {
04     ws.on("message", function(message) {
05         fs.writeFile("upload.png", message, "binary", function(error) {
06             if (!error) {
07                 console.log("File saved.");
08             }
09         });
10     });
11 });


  在客户端接收二进制数据时,需注意WebSocket对象有一个属性binaryType,初始值为"blob"。因此,如果接收的二进制数据是ArrayBuffer,应在接收之前这样做:

1 socket.binaryType = "arraybuffer";


  其他WebSocket服务器端

  其他语言来做WebSocket服务器是怎样的呢?下面是一个php的WebSocket服务器的例子(使用Ratchet):

01 <!--?php
02 use Ratchet\ConnectionInterface;
03 use Ratchet\MessageComponentInterface;
04  
05 require __DIR__ . '/vendor/autoload.php';
06  
07 class GuestServer implements MessageComponentInterface {
08  
09     public function onOpen(ConnectionInterface $conn) {
10         $conn--->send('The server is listening to you now.');
11     }
12  
13     public function onMessage(ConnectionInterface $conn, $msg) {
14         $conn->send($this->generateGuestInfo());
15     }
16  
17     public function onClose(ConnectionInterface $conn) {
18     }
19  
20     public function onError(ConnectionInterface $conn, \Exception $e) {
21         $conn->close();
22     }
23  
24     private function generateGuestInfo() {
25         $jsonString = file_get_contents('http://uinames.com/api?region=china');
26         $jsonObject = json_decode($jsonString, true);
27         $guest = $jsonObject['name'] . $jsonObject['surname'];
28         $guestInfo = array(
29             'guest' => $guest,
30             'time' => date('H:i:s', time()),
31         );
32  
33         return json_encode($guestInfo);
34     }
35 }
36  
37 $app = new Ratchet\App('localhost', 8080);
38 $app->route('/guest', new GuestServer(), array('*'));
39 $app->run();
40 ?>

  这个例子也同样是由服务器返回{guest: "人名", time: "15:26:01"}的json数据,不过由于php不像Node那样可以用setTimeout()很容易地实现异步定时任务,这里改为在客户端发送一次任意信息后,再去uinames取得信息并返回。

 

  也可以看到,php搭建的WebSocket服务器仍然是近似的,主要通过WebSocket的open、message等事件来实现功能。

 

  在Chrome开发工具中查看WebSocket数据帧

 

  Chrome开发工具中选择Network,然后找到WebSocket的那个请求,里面可以选择Frames。在Frames里看到的,就是WebSocket的数据帧了:

  可以看到很像聊天记录,其中用浅绿色标注的是由客户端发送给服务器的部分。

 

  结语

  总的来说,把服务器和客户端拉到了一个聊天窗口来办事,这确实是很棒的想法。

  即使只从形式上说,WebSocket的事件回调感觉也比定时任务用起来要更亲切一些。

本文转载自:http://www.html5cn.org/article-9442-1.html

北海vs
粉丝 1
博文 29
码字总数 936
作品 0
海淀
私信 提问
高效简易websocket服务开发包beetle

 websocket的主要是为了解决在web上应用长连接进行灵活的通讯应用而产生,但websocket本身只是一个基础协议,对于消息上还不算灵活,毕竟websocket只提供文本和二进制流这种基础数据格式.在...

泥水佬
2015/07/10
757
0
使用 HTML5 WebSocket 构建实时 Web 应用

作为下一代的 Web 标准,HTML5 拥有许多引人注目的新特性,如 Canvas、本地存储、多媒体编程接口、WebSocket 等等。这其中有“Web 的 TCP ”之称的 WebSocket 格外吸引开发人员的注意。WebSo...

lyg945
2014/08/27
1K
3
【转载】使用 HTML5 WebSocket 构建实时 Web 应用

作为下一代的 Web 标准,HTML5 拥有许多引人注目的新特性,如 Canvas、本地存储、多媒体编程接口、WebSocket 等等。这其中有“Web 的 TCP ”之称的 WebSocket 格外吸引开发人员的注意。WebSo...

长平狐
2012/09/06
364
0
常见的Web实时消息交互方式和SignalR

原文:常见的Web实时消息交互方式和SignalR 标签: WebSocket SignalR 前言 1. Web消息交互技术 1.1 常见技术 1.2 WebSocket介绍 1.3 WebSocket示例 2. Signal 2.1 SignalR是什么 2.2 默认传输...

杰克.陈
2018/06/08
0
0
转发:websocket 通信协议介绍

websocket通信协议实现的是基于浏览器的原生socket,在客户端用JS即可轻松完成,前些 天都在学习websocket 协议(但实际上websocket 协议甚为简约),并且粗略的思考过websocket的对于下一代...

红薯
2010/06/02
4.6K
7

没有更多内容

加载失败,请刷新页面

加载更多

JAVA 编写redisUtils工具类,防止高并发获取缓存出现并发问题

import lombok.extern.slf4j.Slf4j;import org.springframework.data.redis.core.BoundHashOperations;import org.springframework.data.redis.core.BoundValueOperations;import org.......

huangkejie
34分钟前
5
0
JMM内存模型(一)&volatile关键字的可见性

在说这个之前,我想先说一下计算机的内存模型: CPU在执行的时候,肯定要有数据,而数据在内存中放着呢,这里的内存就是计算机的物理内存,刚开始还好,但是随着技术的发展,CPU处理的速度越...

走向人生巅峰的大路
51分钟前
94
0
你对AJAX认知有多少(2)?

接着昨日内容,我们几天继续探讨ajax的相关知识点 提到ajax下面几个问题又是必须要了解的啦~~~ 8、在浏览器端如何得到服务器端响应的XML数据。 通过XMLHttpRequest对象的responseXMl属性 9、 ...

理性思考
今天
5
0
正则表达式基础(一)

1.转义 转义的作用: 当某个字符在表达式中具有特殊含义,例如字符串引号中出现了引号,为了可以使用这些字符本身,而不是使用其在表达式中的特殊含义,则需要通过转义符“\”来构建该字符转...

清自以敬
今天
4
0
idea中@Data标签getset不起作用

背景:换电脑以后在idea中有@data注解都不生效 解决办法:idea装个插件 https://blog.csdn.net/seapeak007/article/details/72911529...

栾小糖
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部