《HTML5编程之旅》系列三:WebSockets 技术解析

       本文主要研究HTML5 WebSockets的使用方法,它是HTML5中最强大的通信功能,定义了一个全双工的通信信道,只需Web上的一个Socket即可进行通信,能减少不必要的网络流量并降低网络延迟。HTML5 WebSockets能使数据从几千字节减少到两字节,延迟从150ms减少到50ms,并且完美淘汰传统的Comet和Ajax轮询(polling)、长轮询(long-polling)以及流(streaming)解决方案。

      目前实时Web应用的实现方式,大部分是围绕轮询和其他服务器端推送技术展开的,其中最著名的是Comet。Comet技术可以让服务器主动以异步方式向客户端推送数据。

      使用轮询时,浏览器定期发送HTTP请求,并随即接收响应;使用长轮询时,浏览器向服务器发送一个请求,服务器会在一段时间内将其保持在打开状态;使用流解决方案时,浏览器会发送一个完整的HTTP请求,但服务器会发送并保持一个处于打开状态的响应,该响应持续更新并无限期处于打开状态。

      上述的三个方法,在发送实时数据时都会涉及到HTTP请求和响应包头,且包含大量额外的、不必要的报头数据,会造成传输延迟。

一、解读HTML5 WebSockets

1、WebSocket握手

      为了建立WebSocket通信,客户端和服务器在初始握手时,将HTTP协议升级到WebSocket协议。一旦连接建立成功,就可以在全双工模式下在客户端和服务器之间来回传送WebSocket消息。

      注:在网络中,每个消息以0x00字节开头,以0xFF结尾,中间数据采用UTF-8编码格式。

2、WebSocket接口

      除了对WebSocket协议的定义之外,还定义了用于JavaScript应用程序的WebSocket接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
interface WebSocket{
 
readonly attribute DOMString URL;
 
//就绪状态
 
const unsigned short CONNECTING = 0;
 
const unsigned short OPEN = 1;
 
const unsigned short CLOSED = 2;
 
readonly attribute unsigned short readyState;
 
readonly attribute unsigned short bufferedAmount;
 
//网络
 
attribute Function onopen;
 
attribute Function onmessage;
 
attribute Function onclose;
 
boolean send(in DOMSString data);
 
void close();
 
};
 
WebSocket implements EventTarget;

  注意:ws://和wss://前缀分别表示WebSocket连接和安全的WebSocket连接。

二、HTML5 WebSockets API

      本节讨论HTML5 WebSockets的使用方法

1、检测浏览器是否支持

      通过window.WebSocket来判断浏览器是否支持。

2、API的基本用法

a. WebSocket对象的创建以及与WebSocket服务器的连接

1
2
3
 
ws = new WebSocket(url);

  上述两行代码也可以合并为一行代码:

1
ws = new WebSocket("ws://localhost:8080/echo");

  

b. 添加事件监听器

      WebSocket遵循异步编程模型,打开socket后,只需等待事件发生,而不需主动向服务器轮询,因此需要添加回调函数来监听事件。

      WebSocket对象有三个事件:open、close和message。当连接建立时触发open事件,当收到消息时触发message事件,当WebSocket连接关闭时触发close事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ws.onopen = function(){
 
    log("open");
 
}
 
ws.onmessage = function(){
 
    log(e.data);
 
}
 
ws.onclose = function(){
 
    log("closed");
 
}

 

c. 发送消息

当socket处于打开状态(即调用onopen监听程序之后,调用onclose监听程序之前),可以使用send方法发送消息。

ws.send("Hello World");

三、HTML5 WebSockets 应用示例

本节将结合前面讲述的Geolocation接口来创建一个直接在Web页面中计算距离的应用。

1、编写HTML文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
 
<html>
 
    <head>
 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
        <title>HTML5 WebSocket / Geolocation 追踪器</title>
 
        <link rel="stylesheet" href="styles.css">
 
    </head>
 
    <body onload="loadDemo()">
 
        <h1>HTML5 WebSocket / Geolocation 追踪器</h1>
 
        <div><strong>Geolocation</strong>: <p id="geoStatus">你的浏览器不支持HTML5 Geolocation</p></div>
 
        <div><strong>WebSocket</strong>: <p id="socketStatus">你的浏览器不支持HTML5 Web Sockets</p></div>
 
    </body>
 
</html>

  

2、添加WebSocket代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function loadDemo(){
 
    //确保浏览器支持WebSocket
 
    if(window.WebSocket){
 
        url = "ws://localhost:8080";//broadcast WebSocket服务器位置
 
        ws = new WebSocket(url);
 
        ws.onopen = function(){
 
            updateSocketStatus("连接已建立");
 
        }
 
        ws.onmessage = function(e){
 
            updateSocketeStatus("更新位置数据:" + dataReturned(e.data));
 
        }
 
    }
 
}

 

3、添加Geolocation代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var geo;
 
if(navigator.geolocation){
 
    geo = navigator.geolocation;
 
    updateGeolocationStatus("浏览器支持HTML5 Geolocation");
 
}
 
geo.watchPosition(updateLocation,handleLocationError,{maximumAge:20000});//每20s更新一次
 
function updateLocation(position){
 
    var latitude = position.coords.latitude;
 
    var longitude = position.coords.longitude;
 
    var timestamp = position.timestamp;
 
    updateGeolocationStatus("位置更新时间:" + timestamp);
 
    var toSend = JSON.stringify([myId,latitude,longitude]);
 
    sendMyLocation(toSend);
 
}

  

4、合并所有内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<!DOCTYPE html>
 
<html>
 
    <head>
 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
        <title>HTML5 WebSocket / Geolocation 追踪器</title>
 
        <link rel="stylesheet" href="styles.css">
 
    </head>
 
    <body onload="loadDemo()">
 
        <h1>HTML5 WebSocket / Geolocation 追踪器</h1>
 
        <div><strong>Geolocation</strong>: <p id="geoStatus">你的浏览器不支持HTML5 Geolocation</p></div>
 
        <div><strong>WebSocket</strong>: <p id="socketStatus">你的浏览器不支持HTML5 Web Sockets</p></div>
 
        </body>
 
    <script>
 
        //WebSocket的引用
 
        var ws;
 
        //为该会话生成的唯一随机的ID
 
        var myId = Math.floor(100000*Math.random());
 
        //当前显示的行数
 
        var rowCount;
 
        function updateSocketStatus(message){
 
            document.getElementById("socketStatus").innerHTML = message;
 
        }
 
        function updateGeolocationStatus(message){
 
            document.getElementById("geoStatus").innerHTML = message;
 
        }
 
        function loadDemo(){
 
        //确保浏览器支持WebSocket
 
            if(window.WebSocket){
 
                url = "ws://localhost:8080";//broadcast WebSocket服务器位置
 
                ws = new WebSocket(url);
 
                ws.onopen = function(){
 
                updateSocketStatus("连接已建立");
 
            }
 
            ws.onmessage = function(e){
 
                updateSocketStatus("更新位置数据:" + dataReturned(e.data));
 
            }
 
        }
 
        var geo;
 
        if(navigator.geolocation){
 
            geo = navigator.geolocation;
 
            updateGeolocationStatus("浏览器支持HTML5 Geolocation");
 
        }
 
        geo.watchPosition(updateLocation,handleLocationError,{maximumAge:20000});//每20s更新一次
 
        function updateLocation(position){
 
            var latitude = position.coords.latitude;
 
            var longitude = position.coords.longitude;
 
            var timestamp = position.timestamp;
 
            updateGeolocationStatus("位置更新时间:" + timestamp);
 
            var toSend = JSON.stringify([myId,latitude,longitude]);
 
            sendMyLocation(toSend);
 
        }
 
        function sendMyLocation(newLocation){
 
            if(ws){
 
                ws.send(newLocation)
 
            }
 
        }
 
        function dataReturned(locationData){
 
            var allData = JSON.parse(locationData);
 
            var incomingId = allData[1];
 
            var incomingLat = allData[2];
 
            var incomingLong = allData[3];
 
            var incomingRow = document.getElementById(incomingId);
 
            if(!incomingRow){
 
                incomingRow = document.getElementById("div");
 
                incomingRow.setAttribute("id",incomingId);
 
                incomingRow.userText = (incomingId == myId)?"Me":‘User‘ + rowCount;
 
                rowCount++;
 
                document.body.appendChild(incomingRow);
 
            }
 
            incomingRow.innerHTML = incomingRow.userText + " \\ Lat: " +
 
                                            incomingLat + " \\ Lon: " +
 
                                            incomingLong;
 
            return incomingRow.userText;
 
        }
 
        function handleLocationError(error){
 
            switch(error.code){
 
                case 0:
 
                  updateGeolocationStatus("检索位置信息出错: " + error.message);
 
                  break;
 
                case 1:
 
                  updateGeolocationStatus("用户阻止获取位置信息。");
 
                  break;
 
                case 2:
 
                  updateGeolocationStatus("浏览器不能检测你的位置信息: " + error.message);
 
                  break;
 
                case 3:
 
                  updateGeolocationStatus("浏览器检索位置信息超时。");
 
                  break;
 
            }
        }
 
    </script>
 
</html>

  注意:上述代码的运行需要一个额外服务器的支持,在此把原书里提供的使用Python写的服务器文件附上,关于如何运行Python,请参考网上其他资料。

  本文链接:http://www.cnblogs.com/oooweb/p/html5-websockets-tutorials.html

  via ocshina

《HTML5编程之旅》系列三:WebSockets 技术解析,布布扣,bubuko.com

《HTML5编程之旅》系列三:WebSockets 技术解析

上一篇:How to choose between zombie.js and PhantomJS for automated web testing? [closed]


下一篇:【读书笔记】XHTML与HTML5 的差异