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 相关文章推荐
js/jQuery对象互转(快速操作dom元素)
Feb 04 Javascript
Jquery获得控件值的三种方法总结
Feb 13 Javascript
document.addEventListener使用介绍
Mar 07 Javascript
js跨域访问示例(客户端/服务端)
May 19 Javascript
Javascript 数组排序详解
Oct 22 Javascript
JavaScript列表框listbox全选和反选的实现方法
Mar 18 Javascript
JS实现两表格里数据来回转移的方法
May 28 Javascript
AngularJS 限定$scope的范围实例详解
Jun 23 Javascript
VUE axios发送跨域请求需要注意的问题
Jul 06 Javascript
微信web端后退强制刷新功能的实现代码
Mar 04 Javascript
微信小程序实现点击按钮后修改颜色
Dec 05 Javascript
js实现扫雷源代码
Nov 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 错误处理经验分享
2011/10/11 PHP
php中0,null,empty,空,false,字符串关系的详细介绍
2013/06/20 PHP
ThinkPHP采用GET方式获取中文参数查询无结果的解决方法
2014/06/26 PHP
百度地图API使用方法详解
2015/08/25 PHP
Yii基于数组和对象的Model查询技巧实例详解
2015/12/28 PHP
PHP会员找回密码功能的简单实现
2016/09/05 PHP
PHP 实现手机端APP支付宝支付功能
2018/06/07 PHP
判断是否输入完毕再激活提交按钮
2006/06/26 Javascript
JavaScript ECMA-262-3 深入解析.第三章.this
2011/09/28 Javascript
javascript实现简单的二级联动
2015/03/19 Javascript
AngularJS基础学习笔记之指令
2015/05/10 Javascript
node.js require() 源码解读
2015/12/13 Javascript
JS实现鼠标滑过显示边框的菜单效果
2016/09/21 Javascript
基于jQuery代码实现圆形菜单展开收缩效果
2017/02/13 Javascript
Ajax验证用户名或昵称是否已被注册
2017/04/05 Javascript
ReactJS实现表单的单选多选和反选的示例
2017/10/13 Javascript
ES6中字符串string常用的新增方法小结
2017/11/07 Javascript
详解NODEJS的http实现
2018/01/04 NodeJs
easyUI 实现的后台分页与前台显示功能示例
2020/06/01 Javascript
Python中进程和线程的区别详解
2017/10/29 Python
Python及Django框架生成二维码的方法分析
2018/01/31 Python
python之cv2与图像的载入、显示和保存实例
2018/12/05 Python
Python判断有效的数独算法示例
2019/02/23 Python
python微信撤回监测代码
2019/04/29 Python
django 信号调度机制详解
2019/07/19 Python
CSS3文本换行word-wrap解决英文文本超过固定宽度不换行
2013/10/10 HTML / CSS
英国第二大营养品供应商:Vitabiotics
2016/10/01 全球购物
函数指针的定义是什么
2016/08/14 面试题
仓管岗位职责范本
2014/02/08 职场文书
职务说明书范文
2014/05/07 职场文书
环保口号大全
2014/06/12 职场文书
法语专业求职信
2014/07/20 职场文书
自动化专业大学生职业生涯规划范文:爱拚才会赢
2014/09/12 职场文书
学校财务管理制度
2015/08/04 职场文书
2019升学宴主持词范本5篇
2019/10/09 职场文书
使用CSS实现小三角边框原理解析
2021/11/07 HTML / CSS