WebSocket的简单介绍及应用

定时刷新的不足与改进

web开发中可能遇到这样的场景:网页里的某一块区域里写了一些内容,但这些内容不是固定的,即使看网页的人没有做任何操作,它们也会随时间不断变化。股票行情、活动或游戏的榜单都是比较常见的例子。

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

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

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

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

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

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

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

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

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

GET ws://websocket.example.com/ HTTP/1.1
Host: websocket.example.com
Upgrade: websocket
Connection: Upgrade
Origin: http://example.com
Sec-WebSocket-Key:pAloKxsGSHtpIHrJdWLvzQ==
Sec-WebSocket-Version:13

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

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

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

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

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

整个过程像这样:

简单的应用示例

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

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

服务器端

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

var request = require("request");
var dateFormat = require("dateformat");
var WebSocket = require("ws"),
 WebSocketServer = WebSocket.Server,
 wss = new WebSocketServer({
  port: 8080,
  path: "/guest"
 });

// 收到来自客户端的连接请求后,开始给客户端推消息
wss.on("connection", function(ws) {
 ws.on("message", function(message) {
  console.log("received: %s", message);
 });
 sendGuestInfo(ws);
});

function sendGuestInfo(ws) {
 request("http://uinames.com/api?region=china",
  function(error, response, body) {
   if (!error && response.statusCode === 200) {
    var jsonObject = JSON.parse(body),
     guest = jsonObject.name + jsonObject.surname,
     guestInfo = {
      guest: guest,
      time: dateFormat(new Date(), "HH:MM:ss")
     };

    if (ws.readyState === WebSocket.OPEN) {

     // 发,送
     ws.send(JSON.stringify(guestInfo));

     // 用随机来“装”得更像不定时推送一些
     setTimeout(function() {
      sendGuestInfo(ws);
     }, (Math.random() * 5 + 3) * 1000);
    }
   }
  });
}

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

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

客户端

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

var socket = new WebSocket("ws://localhost:8080/guest");

socket.onopen = function(openEvent) {
 console.log("WebSocket conntected.");
};

socket.onmessage = function(messageEvent) {
 var data = messageEvent.data,
  dataObject = JSON.parse(data);
 console.log("Guest at " + dataObject.time + ": " + dataObject.guest);
};

socket.onerror = function(errorEvent) {
 console.log("WebSocket error: ", errorEvent);
};

socket.onclose = function(closeEvent) {
 console.log("WebSocket closed.");
};

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

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

效果及分析

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

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

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

服务器就可以收到:

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

其他可用的数据类型

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

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

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

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

var fs = require("fs");

wss.on("connection", function(ws) {
 ws.on("message", function(message) {
  fs.writeFile("upload.png", message, "binary", function(error) {
   if (!error) {
    console.log("File saved.");
   }
  });
 });
});

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

socket.binaryType = "arraybuffer";

其他WebSocket服务器端

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

<?php
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;

require __DIR__ . '/vendor/autoload.php';

class GuestServer implements MessageComponentInterface {

 public function onOpen(ConnectionInterface $conn) {
  $conn->send('The server is listening to you now.');
 }

 public function onMessage(ConnectionInterface $conn, $msg) {
  $conn->send($this->generateGuestInfo());
 }

 public function onClose(ConnectionInterface $conn) {
 }

 public function onError(ConnectionInterface $conn, \Exception $e) {
  $conn->close();
 }

 private function generateGuestInfo() {
  $jsonString = file_get_contents('http://uinames.com/api?region=china');
  $jsonObject = json_decode($jsonString, true);
  $guest = $jsonObject['name'] . $jsonObject['surname'];
  $guestInfo = array(
   'guest' => $guest,
   'time' => date('H:i:s', time()),
  );

  return json_encode($guestInfo);
 }
}

$app = new Ratchet\App('localhost', 8080);
$app->route('/guest', new GuestServer(), array('*'));
$app->run();
?>

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

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

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

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

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

结语

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 微信小程序websocket实现即时聊天功能

    今天给大家分享一下本人做小程序使用websocket的一点小经验,希望对大家有所帮助. 使用之前肯定首先要了解一下websocket是什么,简单来讲websocket就是客户端与服务器之间专门建立的一条特殊通道,请求只需要请求一次,而且还可以从通道实时获取服务器数据,非常适合应用到实时应用上. 因为这里本人是分享小程序,所以就不去深究websocket的底层和协议了,感兴趣的朋友可以去看下websocket协议 建议大家在做之前先看看微信小程序官方提供的api关于websocket的文档,因为微

  • 微信小程序使用websocket通讯的demo,含前后端代码,亲测可用

    0.概述websocket (1) 个人总结:后台设置了websocket地址,服务器开启后等待有人去连接它. 一个客户端一打开就去连接websocket地址,同时传递某些识别参数.这样一来后台和客户端连接成功了,然后后台就可以发消息给客户端了,(客户端也可以再回话给后台). (2) socket叫套接字,应用程序用socket向网络发出请求或者应答网络请求. (3) 官方解释的socket 建立连接四步骤: 服务器端开启socket,然后accep方法处于监听状态,等待客户端的连接. 客户端开

  • WebSocket整合SSM(Spring,Struts2,Maven)的实现示例

    一.WebSocket与HTTP长轮询 WebSocket属于HTML5 规范的一部分,提供的一种在单个 TCP 连接上进行全双工通讯的协议.允许服务端主动向客户端推送数据.在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输.(Tomcat 8以上版本支持) HTTP 协议是一种无状态的.无连接的.单向的应用层协议.它采用了请求/响应模型.通信请求只能由客户端发起,服务端对请求做出应答处理.这种通信模型有一个弊端:HTTP

  • WebSocket实现数据库更新时前端页面刷新

    本文实例为大家分享了WebSocket实现数据库更新时前端页面刷新,供大家参考,具体内容如下 后台代码: WebSocketConfig: package com.x.common.websocket; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.sock

  • WebSocket的简单介绍及应用

    定时刷新的不足与改进 web开发中可能遇到这样的场景:网页里的某一块区域里写了一些内容,但这些内容不是固定的,即使看网页的人没有做任何操作,它们也会随时间不断变化.股票行情.活动或游戏的榜单都是比较常见的例子. 对此,一般的做法是用setTimeout()或setInverval()定时执行任务,任务内容是Ajax访问一次服务器,并在成功拿到返回数据后去更新页面. 这种定时刷新的做法会有这样一些感觉不足的地方: 频繁的定时网络请求对浏览器(客户端)和服务器来说都是一种负担,尤其是当网页里有多个定

  • SpringBoot+WebSocket搭建简单的多人聊天系统

    前言 今天闲来无事,就来了解一下WebSocket协议.来简单了解一下吧. WebSocket是什么 首先了解一下WebSocket是什么?WebSocket是一种在单个TCP连接上进行全双工通信的协议.这是一种比较官方的说法,简单点来说就是,在一次TCP连接中,通信的双方可以相互通信.比如A和B在打电话,A说话的时候,B也可以说话来进行信息的交互,这就叫做全双工通信.对应的是单工通信,和半双工通信,单工通信就是只能由A向B通信,比如电脑和打印机.半双工通信是可以AB可以互相通信,但是同一时间只

  • 如何用JS WebSocket实现简单聊天

    短轮询(Polling) 短轮询的实现思路就是浏览器端每隔几秒钟向服务器端发送 HTTP 请求,服务端在收到请求后,不论是否有数据更新,都直接进行响应.在服务端响应完成,就会关闭这个 TCP 连接,代码实现也最简单,就是利用 XHR, 通过 setInterval 定时向后端发送请求,以获取最新的数据. setInterval(function() { fetch(url).then((res) => { // success code }) }, 3000); 优点:实现简单. 缺点:会造成数

  • Atom-IDE 的使用方法简单介绍

    Atom-IDE 的使用方法简单介绍 今日,GitHub 宣布与 Facebook 合作推出了 Atom-IDE -- 它包括一系列将类 IDE 功能带到 Atom 的可选工具包. 初次发布的版本包括更智能.感知上下文的自动完成:导航功能,如大纲视图和 goto-definition,以及其他有用的功能:还包括错误.警告提醒和格式化文档功能. 查看 Atom 博客以了解更多. Atom-IDE 包括适用于 C#, Flow, Java, JavaScript, PHP, 和 TypeScript

  • Eclipse代码格式化设置简单介绍

    自用项目中统一Eclipse格式化Java.JavaScript.JSP.HTML代码设置 1.Window->Preferences //Java 格式化 2.Java->Code Style->Formatter->New->Edit->Line Wrapping 3.Maximum Line width = 180 4.Set line width for preview window = 180 //JavaScript 格式化 5.JavaScript->

  • JSP状态管理的简单介绍

    JSP状态管理的简单介绍 一 http协议的无状态性 无状态性是指:当浏览器发送请求给服务器时,服务器响应客户端请求. 但是当同一个浏览器再次发送请求给服务器的时候,服务器并不知道它就是刚才那个浏览器. 简单地说,就是服务器不会去记得你,所以就是无状态协议. 二 保存用户状态的两大机制 Session Cookie 三 Cookie概述 Cookie:中文名称为"小甜饼",是Web服务器保存在客户端的一系列文本信息. 典型应用一:判断注册用户是否已经登录网站. 典型应用二:"

  • 权限控制之粗粒度与细粒度概念及实现简单介绍

    本文简单介绍了权限控制之粗粒度与细粒度概念及实现,下面看看具体内容. 1.   什么是粗粒度和细粒度权限 粗粒度权限管理,对资源类型的权限管理.资源类型比如:菜单.url连接.用户添加页面.用户信息.类方法.页面中按钮.. 粗粒度权限管理比如:超级管理员可以访问户添加页面.用户信息等全部页面. 部门管理员可以访问用户信息页面包括 页面中所有按钮. 细粒度权限管理,对资源实例的权限管理.资源实例就资源类型的具体化,比如:用户id为001的修改连接,1110班的用户信息.行政部的员工. 细粒度权限管

  • json简单介绍

    JSON简单介绍: 所谓的JSON是JavaScript Object Notation的缩写,意思是javascript对象表示法. 下面对JSON做一下总结: (1).JSON指的是javascript对象表示法,是JavaScript Object Notation的缩写. (2).JSON是轻量级的文本数据交换格式. (3).JSON是独立于平台和语言的. (4).JSON是具有自我描述性,非常易于理解. 特别说明:JSON是一种用来规范字符串数据格式的语法. 代码实例: 实例一: {"

  • 关于RequireJS的简单介绍即使用方法

    RequireJS介绍 RequireJS 是一个JavaScript模块加载器.它非常适合在浏览器中使用.使用RequireJS加载模块化脚本将提高代码的加载速度和质量. 兼容性 浏览器(browser) 是否兼容 IE 6+ 兼容 ✔ Firefox 2+ 兼容 ✔ Safari 3.2+ 兼容 ✔ Chrome 3+ 兼容 ✔ Opera 10+ 兼容 ✔ 优点 实现js文件的异步加载,避免网页失去响应 管理模块之间的依赖性,便于代码的编写和维护 快速上手 step 1 引入require

  • node.js中http模块和url模块的简单介绍

    前言 本文主要给大家介绍了关于node.js中http模块与url模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一.http模块的简单介绍 node.js当中的http内置模块可以用于创建http服务器与http客户端. 1.引包 const http = require('http'); 2.创建http服务器 var server = http.createServer((req,res)=>{ }); 使用http的.createServer()方法可以

随机推荐