HTML5新特性之WebSocket
在Web应用越来越复杂的今天,消息推送已经成为一个非常重要的功能了,有了消息推送,Web页面就能够及时的接收到来自服务端的消息,为用户展现最好的交互体验。过去我们在实现Web页面的消息推送时,一般都是由页面发起请求,以轮询的方式向服务器获取数据,实现起来极为不便,业务逻辑也相对较为复杂。
为了解决这个问题,进而实现真正意义上的消息推送,HTML5定义了一套新的规范,这就是WebSocket。下面我们就来介绍一下WebSocket这个新特性。
WebSocket是一种用于在服务器和客户端之间实现高效的双向通信的机制,通过WebSocket,能够实现在一个HTTP连接上自由地双向收发消息。
在通过WebSocket进行双向通信时,首先我们需要和服务器建立连接,这需要由客户端通过HTTP方式发送,服务器将会确认连接对象的源和协议,并发送连接许可响应,在客户端收到响应之后,一个WebSocket通信就正式建立起来了。
那么我们如何在客户端向服务器端发送请求呢?这时我们需要实例化一个WebSocket,代码如下所示:
- var socket = new WebSocket('ws://www.websocket-testing.com:9000/server', 'subprotocol');
在执行构造函数之后,通信内部就会建立连接,一旦连接建立成功,WebSocket实例的open事件就会被触发:
- socket.onopen = function(e) {
- console.log('websocket connection has established');
- }
- //send message to websocket server
- socket.send('hello, server');
- //receive message from websocket server
- socket.onmessage = function(e){
- //obtain the message from server
- var message = e.data;
- //do something
- }
在通信结束后,客户端可以调用close方法主动断开与服务器端的连接,如果连接断开,close事件就会被触发,我们可以捕获此事件,做进一步的操作处理:
- //cut off the connection
- socket.close();
- socket.onclose = function(e){
- //do something
- }
有些时候,因为网络问题或者服务器宕机的原因,客户端连接被切断,此时我们可以在捕获close事件后,试图重新建立连接,如下面代码所示:
- socket.onclose = function() {
- setTimeout(function() {
- //reconnect operation
- }, 100000);
- }
如果服务器宕机之后,有新的客户端试图连接,这种情况下会导致连接失败,error就会被触发,我们可以捕获error事件,做相应的处理操作:
- socket.onerror = function() {
- console.log('socket error occurred');
- };
接下来,为了能够更好的展示WebSocket的交互过程,我们会建立起从客户端到服务端的一个简易的实时在线聊天程序。
服务端
要建立起聊天程序,首先我们需要搭建服务端环境,使其支持WebSocket通信,目前大多服务器端编程语言都有自己相对应的库,大家可以根据自己的熟悉程度去选择,在这里我们选用Node.js搭建一个简易的WebSocket服务。
Node.js平台上有很多功能强大的WebSocket库,感兴趣的同学们都可以去了解一下,这里我们为了阐述基本用法,就选取一个叫ws的简易的库,使用npm命令安装即可:
- npm install ws
那么接下来我们就要创建一个简单的服务了,安装完ws库之后,只需在node_modules同级目录下创建一个websocket-server.js文件即可,如图:
下面是websocket-server.js中的一段简易的代码:
- var WebSocketServer = require('ws').Server;
- var socketServer = new WebSocketServer({ port: 9000 });
- //to store all connected clients
- var clients = [];
- socketServer.on('connection', function(socket) {
- //if not the specified origin, disconnect the socket
- var origin = socket.upgradeReq.headers.origin;
- if (origin !== 'http://localhost') {
- socket.close();
- return;
- }
- //add to clients when socket is connected
- clients.push(socket);
- //broadcast to clients when new message comes from one client
- socket.on('message', function(message) {
- console.log(message);
- clients.forEach(function(client) {
- if (client !== socket) {
- client.send(message);
- }
- });
- });
- //remove from clients when socket is offline or disconnected
- socket.on('close', function() {
- for (var i = 0; i < clients.length; i++) {
- var client = clients[i];
- if (client === socket) {
- clients.splice(i, 1);
- }
- }
- });
- });
- console.log('socketServer is listening on 9000...');
这时,我们只需在命令行中使用node命令将服务运行起来:
- node websocket-server.js
客户端
在服务端的服务建立起来之后,接下来我们就可以实现客户端逻辑了。在客户端中,我们会建立一个聊天窗口,上面显示聊天的消息内容,下面放置一个输入区,可以将输入内容发送至服务器端,服务器再分发给其他客户端,每个客户端收到消息后将消息追加到显示区域,单个客户端的UI视图如下:
下面来看一下基本的html结构:
- <html>
- <head>
- <title>WebSocket</title>
- <link rel="stylesheet" type="text/css" href="css/main.css">
- </head>
- <body>
- <div id="chatting-room">
- <div class="description">chattings</div>
- <div id="chatting-messages">
- <!-- chatting messages will be here -->
- </div>
- <div class="input-container">
- <div id="chatting-input" contenteditable></div>
- </div>
- <div class="action-container">
- <button onclick="sendMessage();">Send</button>
- </div>
- </div>
- <script type="text/javascript" src="js/jquery.js"></script>
- <script type="text/javascript" src="js/main.js"></script>
- </body>
- </html>
我们知道,客户端和服务器端的通信功能是基于WebSocket的,所以首先我们必须创建一个WebSocket实例对象:
- window.username = prompt('please input your name') || 'scott';
- //connect to a socket server
- window.socket = new WebSocket('ws://localhost:9000');
- //capture open event
- socket.onopen = function() {
- console.log('socket connected');
- };
- //capture message event
- socket.onmessage = function(e) {
- appendMessage(JSON.parse(e.data), false);
- };
- //capture close event
- socket.onclose = function(e) {
- console.log('socket disconnected');
- };
- //append message to chatting-messages area
- function appendMessage(data, isSelf) {
- var message = '<img class="icon" src="img/icon.png">'
- + '<div class="message">'
- + ' <span class="message-name">' + data.name + '</span>'
- + ' <div class="message-content">' + data.message + '</div>'
- + '</div>';
- var messageBody = $('<div class="message-body"></div>');
- if (isSelf) {
- messageBody.addClass('message-self');
- }
- messageBody.html(message);
- $('#chatting-messages').append(messageBody);
- }
- //collect user's input and send the message to server
- function sendMessage() {
- var editArea = $('#chatting-input'),
- message = editArea.text().trim();
- if (!message) return;
- var messageObject = {name: window.username, message: message};
- window.socket.send(JSON.stringify(messageObject));
- appendMessage(messageObject, true);
- editArea.html('');
- }
到这里,我们的客户端逻辑就完成了,是不是非常简单?其实我们使用WebSocket只需要处理好几个相关的函数,便可与服务器端无缝地进行实时双向通信。
WebSocket的讲解到这里就结束了,在实际的应用开发中,客户端只需要配合服务器端,在数据和事件处理方面做到统一,便可构建一个实时高效的双向通信系统,实现真正意义上Web消息推送。
谢谢大家。