Node.js实现数据推送


Posted in Javascript onApril 14, 2016

场景:后端更新数据推送到客户端(Java部分使用Tomcat服务器)。

后端推送数据的解决方案有很多,比如轮询、Comet、WebSocket。

1. 轮询对于后端来说开发成本最低,就是按照传统的方式处理Ajax请求并返回数据,在学校的时候实验室的项目一直都采用轮询,因为它最保险也最容易实现。但轮询带来的通信资源的浪费是无法忽视的,无论数据是否改变,都照常发送请求并响应,而且每次HTTP请求都带有很长的头部信息。

2. Comet的概念是长连接,客户端发送请求后,后端将连接保持下来,直到连接超时或后端返回数据时再重新建立连接,有效的将通信资源转移到了服务器上,实际消耗的是服务器资源。

3. WebSocket是HTML5提供的一种全双工通信技术,通过“握手”实现客户端与服务器之间的通信,实时性好,携带的头部也较小,目前支持的浏览器如下:

Node.js实现数据推送

理想的情况是采取WebSocket与Comet结合的方式,对IE8等浏览器采取Comet方式,做降级处理。但是这样一来,后端需要实现两种处理请求的逻辑,即WebSocket与Comet。所以,本文加入Node.js,之所以这样做,是将处理WebSocket(或Comet)的逻辑转移到Node.js部分,不给后端“添麻烦”,因为在实际情况下,前端开发人员推动后端开发人员并不容易。Node.js作为浏览器与Java业务逻辑层通信的中间层,连接客户端与Tomcat,通过Socket与Tomcat进行通信(是Socket,不是WebSocket,后端需要实现Socket接口。

在客户端,WebSocket与Comet通过Socket.io实现,Socket.io会针对不同的浏览器版本或者不同客户端选择合适的实现方式(WebSocket, long pull..),Socket.io的引入让处理WebSocket(或长连接)变的很容易。Socket.io

客户端引入socket.io:

<script src="static/js/socket.io.js"></script>
客户端JavaScript代码:

var socket = io.connect('127.0.0.1:8181');
 // 发送数据至服务器
socket.emit('fromWebClient', jsonData);
// 从服务器接收数据
 socket.on('pushToWebClient', function (data) {
  // do sth.
 });

Node.js服务器代码:

var http = require('http'),
   app = http.createServer().listen('8181'),
   io = require('socket.io').listen(app);
 io.sockets.on('connection', function (socketIO) {
   // 从客户端接收数据
   socketIO.on('fromWebClient', function (webClientData) {
     // do sth.
   });
   // 客户端断开连接
   socketIO.on('disconnect', function () {
     console.log('DISCONNECTED FROM CLIENT');
   });    
   // 向客户端发送数据
   socketIO.emit('pushToWebClient', jsonData);  
 });

建立好客户端同Node.js服务器的连接只是第一步,下面还需要建立Node.js服务器与Java业务逻辑层的联系。这时,Node.js服务器则作为客户端,向Tomcat发送TCP连接请求。连接成功后,Node.js服务器和Tomcat建立了一条全双工的通道,而且是唯一的一条,不论有多少个客户端请求,都从Node.js服务器转发至Tomcat;同样,Tomcat推送过来的数据,也经由Node.js服务器分发至各个客户端。

这里存在一个问题,就是在WebSocket连接与Socket连接都建立好之后,两次连接彼此之间是屏蔽的。Tomcat不知道是哪次WebSocket连接发送过来的数据,也不知道是哪个客户端发来的数据。当然,Node.js可以利用session id发送至Tomcat来标识是哪一个客户端,但本文采用的是另外一种办法。

客户端同Node.js建立WebSocket连接时,每个连接都会包含一个实例,这里称它为socketIO。每个socketIO都有一个id属性用来唯一标识这个连接,这里称它为socket_id。利用socket_id,在Node.js服务器建立一个映射表,存储每一个socketIO与socket_id的映射关系。Node.js服务器发送数据给Tomcat时带上这个socket_id,再由Java部分进行一系列处理以后封装好每个客户端需要的不同数据一并返回,返回的数据里要有与socket_id的对应关系。这样,Node.js服务器收到Tomcat发来的数据时,通过前面提到的映射表由不同的socketIO分发至不同的客户端。

Node.js服务器代码:

var http = require('http'),
   net = require('net'),
   app = http.createServer().listen('8181'),
   io = require('socket.io').listen(app),
   nodeServer = new net.Socket();
 // 连接到Tomcat
 nodeServer.connect(8007, '127.0.0.1', function() {
   console.log('CONNECTED');
 });
// 存储客户端的WebSocket连接实例
 var aSocket = {};
 // 同客户端建立连接
 io.sockets.on('connection', function (socketIO) {
  // 从客户端接收数据,然后发送至Tomcat
   socketIO.on('fromWebClient', function (webClientData) {    
    // 存储至映射表
     aSocket[socketIO.id] = socketIO;
    // 发送至Tomcat的数据中添加socket_id
    webClientData['sid'] = socketIO.id;    
    // 发送String类型的数据至Tomcat
    nodeServer.write(JSON.stringify(webClientData));    
   });
   // 客户端断开连接
   socketIO.on('disconnect', function () {
    console.log('DISCONNECTED FROM CLIENT');
   });  
});
 // 从Tomcat接收数据
 nodeServer.on('data', function (data) { 
   var jsonData = JSON.parse(data.toString());  
   // 分发数据至客户端
   for (var i in jsonData.list) {
     aSocket[jsonData.list[i]['sid']].emit('pushToWebClient', jsonData.list[i].data);
  }
 });

上面的代码省略了一些逻辑,比如Node.js服务器从Tomcat接收的数据分为两种,一种是推送过来的数据,另外一种是响应请求的数据,这里统一处理推送过来的数据。

在处理通信时,Node.js发送至Tomcat的数据是String格式,而从Tomcat接收的数据为Buffer对象(8进制),需要转化为String之后再转化为json发送至客户端。

本文只是给出一个这样两次连接的简单例子,具体的业务中需要加入许多东西。既然在项目中引入了Node.js,就需要前端承担更多的事情,比如对数据的处理、缓存、甚至加入很多业务逻辑。

Javascript 相关文章推荐
文本框获得焦点和失去焦点的判断代码
Mar 18 Javascript
jQuery中toggleClass()方法用法实例
Jan 05 Javascript
JQuery动态添加和删除表格行的方法
Mar 09 Javascript
AngularJS 实现按需异步加载实例代码
Oct 18 Javascript
JS显示日历和天气的方法
Mar 01 Javascript
JS实现自定义状态栏动画文字效果示例
Oct 12 Javascript
微信小程序中上传图片并进行压缩的实现代码
Aug 28 Javascript
react native基于FlatList下拉刷新上拉加载实现代码示例
Sep 30 Javascript
React中使用外部样式的3种方式(小结)
May 28 Javascript
微信小程序 腾讯地图SDK 获取当前地址实现解析
Aug 12 Javascript
vue.js 子组件无法获取父组件store值的解决方式
Nov 08 Javascript
JavaScript实现滚动加载更多
Dec 27 Javascript
node.js实现端口转发
Apr 14 #Javascript
即将发布的jQuery 3 有哪些新特性
Apr 14 #Javascript
谈一谈JS消息机制和事件机制的理解
Apr 14 #Javascript
Kindeditor在线文本编辑器如何过滤HTML
Apr 14 #Javascript
基于RequireJS和JQuery的模块化编程日常问题解析
Apr 14 #Javascript
[原创]JQuery 在表单提交之前修改 提交的值
Apr 14 #Javascript
javaScript数组迭代方法详解
Apr 14 #Javascript
You might like
PHP数据过滤的方法
2013/10/30 PHP
php计算两个文件相对路径的方法
2015/03/14 PHP
PHP多个图片压缩成ZIP的方法
2020/08/18 PHP
php微信公众号开发之简答题
2018/10/20 PHP
用javascript父窗口控制只弹出一个子窗口
2007/04/10 Javascript
JSON JQUERY模板实现说明
2010/07/03 Javascript
js获取当前地址 JS获取当前URL的示例代码
2014/02/26 Javascript
jQuery 判断图片是否加载完成方法汇总
2015/08/10 Javascript
asp知识整理笔记3(问答模式)
2015/09/27 Javascript
微信小程序 获取设备信息 API实例详解
2016/10/02 Javascript
微信小程序 wxapp地图 map详解
2016/10/31 Javascript
深入理解jQuery()方法的构建原理
2016/12/05 Javascript
webpack4实现不同的导出类型
2019/04/09 Javascript
在element-ui的select下拉框加上滚动加载
2019/04/18 Javascript
JS求1到任意数之间的所有质数的方法详解
2019/05/20 Javascript
Layui带搜索的下拉框的使用以及动态数据绑定方法
2019/09/28 Javascript
Python找出文件中使用率最高的汉字实例详解
2015/06/03 Python
解决python3 urllib中urlopen报错的问题
2017/03/25 Python
使用python遍历指定城市的一周气温
2017/03/31 Python
Python中read()、readline()和readlines()三者间的区别和用法
2017/07/30 Python
python爬虫的数据库连接问题【推荐】
2018/06/25 Python
使用python的pandas库读取csv文件保存至mysql数据库
2018/08/20 Python
HTML5 创建canvas元素示例代码
2014/06/04 HTML / CSS
HTML5标签使用方法详解
2015/11/27 HTML / CSS
英国最大的电子产品和家电零售企业:Currys PC World
2016/09/24 全球购物
法国低价在线宠物商店:bitiba.fr
2020/07/03 全球购物
师范应届生语文教师求职信
2013/10/29 职场文书
关于幼儿的自我评价
2013/12/18 职场文书
养生餐厅创业计划书范文
2014/03/26 职场文书
电话客服专员岗位职责
2014/06/28 职场文书
四川省传达学习贯彻党的群众路线教育实践活动总结大会精神新闻稿
2014/10/26 职场文书
2014年团队工作总结
2014/11/24 职场文书
网聊搭讪开场白
2015/05/28 职场文书
2016读书月活动心得体会
2016/01/14 职场文书
Spring Data JPA使用JPQL与原生SQL进行查询的操作
2021/06/15 Java/Android
世界十大儿童漫画书排名,法国国宝漫画排第五,第二是轰动日本连环
2022/03/18 欧美动漫