postgres+socket.io+nodejs实时地图应用实践

nodejs一直以异步io著称,其语言特性尤其擅长于在realtime应用中,如聊天室等。在进行实时应用开发时,必不可少的需要用到 socket.io库,可以说,nodejs+socket.io在实时应用中具有较好的表现能力。 本文既然选择以实时地图应用做个小例子,那么选择经典的PostgreSQL/PostGIS作为地图的数据库。希望实现的是模拟数据库数据插入了新的GPS坐标,而一旦数据发生改变,立刻将插入的GPS坐标广播到服务端,服务端广播到所有的客户端地图上,进行定位展示。早期作者使用的是redis的广播/订阅机制,最近发现Pg数据库的listen/notify也具备这种消息传递机制。 本文主要的socke.io广播/订阅参考官网,Pg的listen/notify自行谷歌,作者仅简述一下自己如何考虑应用的。

一 服务器端

		var fs = require('fs');
    var http = require('http');
    var socket = require('socket.io');
	var pg = require('pg');
	var util=require('util');
	
	var constr=util.format('%s://%s:%s@%s:%s/%s', 'postgres','postgres','123456','192.168.43.125',5432,'Test');
	var server = http.createServer(function(req, res) {
		res.writeHead(200, { 'Content-type': 'text/html'});
		res.end(fs.readFileSync(__dirname + '/index.html'));
	}).listen(8081, function() {
		console.log('Listening at: http://localhost:8081');
	});

	var pgClient = new pg.Client(constr);//数据库连接
	var socketio=socket.listen(server);//socketio
	socketio.on('connection', function (socketclient) {
		console.log('已连接socket:');
		//socketclient.broadcast.emit('GPSCoor', data.payload);//广播给别人
	    //socketclient.emit('GPSCoor', data.payload);//广播给自己
	
	});
	var sql = 'LISTEN gps'; //监听数据库的gps消息
	var query = pgClient.query(sql);//开始数据库消息监听
        //数据库一旦获取通知,将通知消息通过socket.io发送到各个客户端展示。
	pgClient.on('notification', function (data) {
		console.log(data.payload);
		//socketio.sockets.emit('GPSCoor', data.payload); //与下面的等价
	    socketio.emit('GPSCoor', data.payload);//广播给所有的客户端
			
	});
	pgClient.connect();
	

二 数据库端

建立一个测试表如下:

create table t_gps(
          id serial not null,
          geom geometry(Point,4326),
          constraint t_gps_pkey primary key (id)
);
--建立索引
create index t_gps_geom_idx on t_gps using gist(geom);

对表的增删改建立一个触发器,触发器中发送变化数据出去:

CREATE OR REPLACE FUNCTION process_t_gps() RETURNS TRIGGER AS $body$
	DECLARE
		rec record;
    BEGIN
        IF (TG_OP = 'DELETE') THEN
        	--插入的GPS都是4326的经纬度,我们将在3857的谷歌底图上显示数据,发送转换后的3857出去
            select TG_OP TG_OP,OLD.id,ST_AsText(ST_Transform(OLD.geom,3857)) geom into rec;
			perform pg_notify('gps',row_to_json(rec)::text);
            RETURN OLD;
        ELSIF (TG_OP = 'UPDATE') THEN 
            select TG_OP TG_OP,NEW.id,ST_AsText(ST_Transform(NEW.geom,3857)) geom into rec;
			perform pg_notify('gps',row_to_json(rec)::text);
            RETURN NEW;
        ELSIF (TG_OP = 'INSERT') THEN
        	select TG_OP TG_OP,NEW.id,ST_AsText(ST_Transform(NEW.geom,3857)) geom into rec;
			perform pg_notify('gps',row_to_json(rec)::text);
            RETURN NEW;
        END IF;
        RETURN NULL;
    END;
$body$ LANGUAGE plpgsql;

CREATE TRIGGER T_GPS_TRIGGER
AFTER INSERT OR UPDATE OR DELETE ON T_GPS
    FOR EACH ROW EXECUTE PROCEDURE process_t_gps();

三 客户端

<html>
<head>
	<meta charset='utf-8'>
    <title>实时地图应用</title>
    <link rel="stylesheet" href="http://openlayers.org/en/v3.18.2/css/ol.css" type="text/css">
    <script src="http://openlayers.org/en/v3.18.2/build/ol.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
			var wktform=new ol.format.WKT();//wkt解析
			var gpsSource=new ol.source.Vector();
			function init(){
				var gpsLayer=new ol.layer.Vector({
					source:gpsSource,
					style:new ol.style.Style({
						image: new ol.style.Icon(({
							anchor: [0.5, 1],
							src: 'http://openlayers.org/en/v3.18.2/examples/data/icon.png'
						}))
					})
				});
				var map = new ol.Map({
					layers : [
						new ol.layer.Tile({
							title : '街道图',
							visible : true,
							source : new ol.source.XYZ({
								url : 'http://www.google.cn/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m3!1e0!2sm!3i342009817!3m9!2szh-CN!3sCN!5e18!12m1!1e47!12m3!1e37!2m1!1ssmartmaps!4e0&token=32965'
							})
						}),
						gpsLayer
					],
					target : 'map',
					controls : ol.control.defaults({
						attributionOptions : 
						({
							collapsible : false
						})
					}),
					view : new ol.View({
						center : [0, 0],
						zoom : 2
					})
				});
				var iosocket = io.connect();
				//接受服务端消息
				iosocket.on('GPSCoor', function(data) {
					data=JSON.parse(data);
					switch(data.tg_op){
						case 'INSERT':
							var feature=new ol.Feature({
								geometry:wktform.readGeometry(data.geom)
							});
							feature.setId(data.id);
							gpsSource.addFeature(feature);//地图新增点
							break;
						case 'UPDATE':
							var geom=wktform.readGeometry(data.geom);
							var feature=gpsSource.getFeatureById(data.id);
							if(feature)
								feature.setGeometry(geom);//修改已有点
							break;
						case 'DELETE':
							var feature=gpsSource.getFeatureById(data.id);
							if(feature)
								gpsSource.removeFeature(feature);//删除点
							break;
					}
				});
			}
			
           
    </script>
</head>
<body onload="init()">
    <div id="map"></div>
</body>
</html>

客户端接收到消息后,改变当前地图上的图标gps坐标位置。

四 测试与结果

连开三个客户端连接如下: 初始化三个客户端.png

服务器端socket连接成功.png

4.1 数据库新增GPS坐标

insert into t_gps(geom) values (st_geomfromtext('Point(0 0)',4326));
insert into t_gps(geom) values (st_geomfromtext('Point(118 32)',4326));
insert into t_gps(geom) values (st_geomfromtext('Point(-118 -32)',4326));

页面自动响应效果如下:

服务器端监听到的数据库消息.png

服务器端socket到客户端的效果.png

4.2 数据库修改GPS坐标

查看下当前的数据如下:

Test=# select id,st_astext(geom) from t_gps;
 id |    st_astext    
----+-----------------
 24 | POINT(0 0)
 25 | POINT(118 32)
 26 | POINT(-118 -32)
(3 rows)

将id=25的坐标改成 150,40:

Test=# update t_gps set geom=st_geomfromtext('Point(150 40)',4326) where id=25;
UPDATE 1

服务器端打印如下:

显示一条更新语句.png

更新效果.png

4.3 数据库删除GPS坐标

Test=# delete from t_gps where id=25;
DELETE 1

显示一条删除.png

删除效果.png

所有以上操作,只是数据的增删改指令,服务器和客户端都是自动响应的。

结论:本文实现了,数据库一旦广播了消息,服务器端监听,并继续以sockeio广播到客户端。全部过程,只是数据库发送了一个坐标消息无任何其他操作。pg的notify和listen消息机制,真实应用一般比如写在触发器中,触发器监听是否有数据采集终端将新坐标写入或者更新,然后在触发器中notify消息,这样,前端实时响应。可以做到将终端应用位置无任何操作的一波流发送到全部客户端实时展示。

转载于:https://my.oschina.net/freegis/blog/761713

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值