zoukankan      html  css  js  c++  java
  • HTML5新特性之WebSocket

    WebSocket作用

    在Web应用越来越复杂的今天,消息推送已经成为一个非常重要的功能了,有了消息推送,Web页面就能够及时的接收到来自服务端的消息,为用户展现最好的交互体验。过去我们在实现Web页面的消息推送时,一般都是由页面发起请求,以轮询的方式向服务器获取数据,实现起来极为不便,业务逻辑也相对较为复杂。

    为了解决这个问题,进而实现真正意义上的消息推送,HTML5定义了一套新的规范,这就是WebSocket。下面我们就来介绍一下WebSocket这个新特性。

    WebSocket是一种用于在服务器和客户端之间实现高效的双向通信的机制,通过WebSocket,能够实现在一个HTTP连接上自由地双向收发消息。

    在通过WebSocket进行双向通信时,首先我们需要和服务器建立连接,这需要由客户端通过HTTP方式发送,服务器将会确认连接对象的源和协议,并发送连接许可响应,在客户端收到响应之后,一个WebSocket通信就正式建立起来了。

    WebSocket实例

    那么我们如何在客户端向服务器端发送请求呢?这时我们需要实例化一个WebSocket,代码如下所示:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. var socket = new WebSocket('ws://www.websocket-testing.com:9000/server', 'subprotocol');  

    上面构造函数中的第一个参数是将要进行连接的WebScoket服务器的URL,第二个参数则是子协议名。需要注意的是,URL中我们可以选择“ws://”或“wss://”这两种协议,这两种协议分别默认使用80和443端口来通信,如果使用后者,是可以用TLS对通信加密的。第二个参数也可以是一个数组,以指定多个子协议名。

    在执行构造函数之后,通信内部就会建立连接,一旦连接建立成功,WebSocket实例的open事件就会被触发:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. socket.onopen = function(e) {  
    2.     console.log('websocket connection has established');  
    3. }  

    在连接建立成功之后,就可以进行消息收发了,这时我们可以调用send方法将消息发送到服务器,然后通过捕获message事件接收来自服务器的消息:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. //send message to websocket server  
    2. socket.send('hello, server');  
    3.   
    4. //receive message from websocket server  
    5. socket.onmessage = function(e){  
    6.     //obtain the message from server  
    7.     var message = e.data;  
    8.       
    9.     //do something  
    10. }  

    在上面的代码中,我们发送了一个字符串类型的消息到服务器端,然后接收来自服务器端消息时,我们可以从event的data属性中取得。目前WebSocket可以收发消息的类型有String、Blob和ArrayBuffer,后面两个主要用于收发二进制数据的,日常开发中我们使用String类型就足够了,如果我们想要发送自定义对象,只需将其转换成JSON字符串后发送,如果客户端和服务端是以JSON形式通信,客户端收到消息后在将其转换为JavaScript对象即可。

    在通信结束后,客户端可以调用close方法主动断开与服务器端的连接,如果连接断开,close事件就会被触发,我们可以捕获此事件,做进一步的操作处理:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. //cut off the connection  
    2. socket.close();  
    3.   
    4. socket.onclose = function(e){  
    5.     //do something  
    6. }  

    有些时候,因为网络问题或者服务器宕机的原因,客户端连接被切断,此时我们可以在捕获close事件后,试图重新建立连接,如下面代码所示:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. socket.onclose = function() {  
    2.     setTimeout(function() {  
    3.         //reconnect operation  
    4.     }, 100000);  
    5. }  

    如果服务器宕机之后,有新的客户端试图连接,这种情况下会导致连接失败,error就会被触发,我们可以捕获error事件,做相应的处理操作:

    [javascript] view plain copy
     
    1. socket.onerror = function() {  
    2.     console.log('socket error occurred');  
    3. };  

    需要注意的是,连接失败后,error和close会相继触发,所以实际开发中要避免处理操作的重叠。

    接下来,为了能够更好的展示WebSocket的交互过程,我们会建立起从客户端到服务端的一个简易的实时在线聊天程序。

    服务端

    要建立起聊天程序,首先我们需要搭建服务端环境,使其支持WebSocket通信,目前大多服务器端编程语言都有自己相对应的库,大家可以根据自己的熟悉程度去选择,在这里我们选用Node.js搭建一个简易的WebSocket服务。

    Node.js平台上有很多功能强大的WebSocket库,感兴趣的同学们都可以去了解一下,这里我们为了阐述基本用法,就选取一个叫ws的简易的库,使用npm命令安装即可:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. npm install ws  

    如果对这个库不太熟悉的话,可以去github上看一下其使用说明:https://github.com/websockets/ws。

    那么接下来我们就要创建一个简单的服务了,安装完ws库之后,只需在node_modules同级目录下创建一个websocket-server.js文件即可,如图:

    下面是websocket-server.js中的一段简易的代码:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. var WebSocketServer = require('ws').Server;  
    2. var socketServer = new WebSocketServer({ port: 9000 });  
    3.   
    4. //to store all connected clients  
    5. var clients = [];  
    6.   
    7. socketServer.on('connection', function(socket) {  
    8.   
    9.     //if not the specified origin, disconnect the socket  
    10.     var origin = socket.upgradeReq.headers.origin;  
    11.     if (origin !== 'http://localhost') {  
    12.         socket.close();  
    13.         return;  
    14.     }  
    15.   
    16.     //add to clients when socket is connected  
    17.     clients.push(socket);  
    18.   
    19.     //broadcast to clients when new message comes from one client  
    20.     socket.on('message', function(message) {  
    21.         console.log(message);  
    22.         clients.forEach(function(client) {  
    23.             if (client !== socket) {  
    24.                 client.send(message);  
    25.             }  
    26.         });  
    27.     });  
    28.   
    29.     //remove from clients when socket is offline or disconnected  
    30.     socket.on('close', function() {  
    31.         for (var i = 0; i < clients.length; i++) {  
    32.             var client = clients[i];  
    33.             if (client === socket) {  
    34.                 clients.splice(i, 1);  
    35.             }  
    36.         }  
    37.     });  
    38. });  
    39.   
    40. console.log('socketServer is listening on 9000...');  

    在上面的代码中,首先我们创建一个独立运行的WebSocket服务,在9000端口监听客户端的连接请求,然后在客户端连接到服务端时,如果是安全的请求,就将其加入到clients中,当一个客户端发送消息时,服务端就会将这条消息广播到其他的客户端,最后当有客户端断开连接时,服务端将其从clients中移除。

    这时,我们只需在命令行中使用node命令将服务运行起来:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. node websocket-server.js  



    客户端

    在服务端的服务建立起来之后,接下来我们就可以实现客户端逻辑了。在客户端中,我们会建立一个聊天窗口,上面显示聊天的消息内容,下面放置一个输入区,可以将输入内容发送至服务器端,服务器再分发给其他客户端,每个客户端收到消息后将消息追加到显示区域,单个客户端的UI视图如下:

    下面来看一下基本的html结构:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <html>  
    2.     <head>  
    3.         <title>WebSocket</title>  
    4.         <link rel="stylesheet" type="text/css" href="css/main.css">  
    5.     </head>  
    6.     <body>  
    7.         <div id="chatting-room">  
    8.             <div class="description">chattings</div>  
    9.             <div id="chatting-messages">  
    10.                 <!-- chatting messages will be here -->  
    11.             </div>  
    12.             <div class="input-container">  
    13.                 <div id="chatting-input" contenteditable></div>  
    14.             </div>  
    15.             <div class="action-container">  
    16.                 <button onclick="sendMessage();">Send</button>  
    17.             </div>  
    18.         </div>  
    19.         <script type="text/javascript" src="js/jquery.js"></script>  
    20.         <script type="text/javascript" src="js/main.js"></script>  
    21.     </body>  
    22. </html>  

    在上面的代码中,我们看到button绑定了click事件,点击发送按钮,会触发sendMessage函数,将消息通过WebSocket发送至服务器端,而从服务器端返回的数据将会显示在#chatting-messages区域。

    我们知道,客户端和服务器端的通信功能是基于WebSocket的,所以首先我们必须创建一个WebSocket实例对象:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. window.username = prompt('please input your name') || 'scott';  
    2.   
    3. //connect to a socket server  
    4. window.socket = new WebSocket('ws://localhost:9000');  
    5.   
    6. //capture open event  
    7. socket.onopen = function() {  
    8.     console.log('socket connected');  
    9. };  
    10.   
    11. //capture message event  
    12. socket.onmessage = function(e) {  
    13.     appendMessage(JSON.parse(e.data), false);  
    14. };  
    15.   
    16. //capture close event  
    17. socket.onclose = function(e) {  
    18.     console.log('socket disconnected');  
    19. };  

    在这段代码中,首先我们在开始聊天之前获取用户输入的用户名,然后创建一个WebSocket实例,然后注册相关的事件函数,在onmessage函数中,我们注意到调用了appendMessage函数,这个函数是负责向聊天内容显示区域追加一条消息,下面我们来看一下appendMessage和sendMessage函数:

    [javascript] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. //append message to chatting-messages area  
    2. function appendMessage(data, isSelf) {  
    3.     var message = '<img class="icon" src="img/icon.png">'  
    4.                 + '<div class="message">'  
    5.                 + ' <span class="message-name">' + data.name + '</span>'  
    6.                 + ' <div class="message-content">' + data.message + '</div>'  
    7.                 + '</div>';  
    8.     var messageBody = $('<div class="message-body"></div>');  
    9.     if (isSelf) {  
    10.         messageBody.addClass('message-self');  
    11.     }  
    12.     messageBody.html(message);  
    13.   
    14.     $('#chatting-messages').append(messageBody);  
    15. }  
    16.   
    17. //collect user's input and send the message to server  
    18. function sendMessage() {  
    19.     var editArea = $('#chatting-input'),  
    20.         message = editArea.text().trim();  
    21.   
    22.     if (!message) return;  
    23.   
    24.     var messageObject = {name: window.username, message: message};  
    25.   
    26.     window.socket.send(JSON.stringify(messageObject));  
    27.     appendMessage(messageObject, true);  
    28.   
    29.     editArea.html('');  
    30. }  

    在appendMessage函数中,指定两个参数,第一个是要追加显示的数据,包含用户名和消息内容,第二个参数是鉴别是否是客户端自己发送的消息,如果是自己发送的,则会将消息显示在窗口的右侧。下面的sendMessage函数中,我们收集用户输入的数据,处理成JSON字符串,然后通过WebSocket发送至服务器端,同时将消息追加到显示区域。

    到这里,我们的客户端逻辑就完成了,是不是非常简单?其实我们使用WebSocket只需要处理好几个相关的函数,便可与服务器端无缝地进行实时双向通信。

    WebSocket的讲解到这里就结束了,在实际的应用开发中,客户端只需要配合服务器端,在数据和事件处理方面做到统一,便可构建一个实时高效的双向通信系统,实现真正意义上Web消息推送。

    原文链接:http://blog.csdn.net/liuhe688/article/details/50496780

    其他相关websocket知识:

     WebSocket断开连接  https://www.cnblogs.com/fangsmile/articles/13370698.html 这个文章里面描述的关于断开连接的应该比较详细些,把可能的坑说了下

    我也说下我使用中遇到的问题,我作为客户端使用websocket.close()去主动断开连接时,如果断开成功的会客户端监听的onclose也会触发的,但是发现会过一段时间才会触发我的onclose回调,回来查问题发现服务端收到客户端close请求后没有主动断开tcp连接。

    另外我有需要是要区分一下是我客户端主动断开的请求,在发起close时添加了参数ws.close(4000,'client close'),服务端接收close请求后再断开tcp连接时附带了客户端发送的code和message,这样客户端的onclose也就能接收到自定义的信息了。

  • 相关阅读:
    APP 弱网测试可能会出现的bug
    Monkey 稳定性测试
    设计模式 策略模式
    设计模式 单例模式
    Linux常用命令(三)文件权限管理
    Linux常用命令(二)文件目录管理命令
    Linux常用命令(一)
    WSL安装yum报错:E: Unable to locate package yum
    使用LxRunOffline迁移WSL
    关于PyQt5 setPalette 设置背景不生效问题
  • 原文地址:https://www.cnblogs.com/fangsmile/p/6383688.html
Copyright © 2011-2022 走看看