WebSocket消息推送接收-(微仿滴滴打车业务场景)-(node.js-Vue.js)

30 篇文章 0 订阅
8 篇文章 0 订阅

年终结尾最后一次更新,在不写一次估计年前就没机会再写了(保证一月一次)。突然想起之前一个朋友问起实时订单推送如何实现。在年尾没啥事自己也比较感兴趣,简单实现了一下。

1.开始

直接梭。安装websocket依赖库。

//安装 websocket依赖
npm install websocket 
//安装 hashMap依赖
npm install hashmap

2.实现服务端

此处代码是简单实现一下业务场景而已,其中使用很多for循环,真实业务场景千万不要这么使用,可自行找其他代替方案。如Redis或MongoDB都可以很完美实现。(有兴趣或看到的朋友,千万别复制粘贴一把梭了。一定要看注释。在结合自己的想法去优化,取长补短。个人觉得有更好的方式去实现不妨留言探讨一下。谢谢!共同进步!)

const http = require('http');
const WebSocketServer = require("websocket").server
const hashMap = require('hashmap');

const httpServer = http.createServer((request, response) => {
    console.log('[' + new Date + '] Received request for ' + request.url);
    response.writable(404);
    response.end();
})

const wsServer = new WebSocketServer({
    httpServer,
    autoAcceptConnections: true
})

const driverMap = new hashMap();//使用hashMap,保持键唯一,查询快
const passengerMap = new hashMap();//使用hashMap,保持键唯一,查询快
const driverContent = [];

wsServer.on('connect', function (connection) {
    connection.on('message', message => {
        console.log(message);
        if (message.type == "utf8") {
            //转换一下JSON格式。
            var ConnectMessage = JSON.parse(message.utf8Data)
            if (ConnectMessage.type == "driver") {
                console.log("司机连接socket!");
                //将司机连接头信息装进hashMap形成一个队列,以openID为Key保证唯一。此为测试用,实际线上环境使用redis。
                //(如:使用hashMap在线上环境遍历时导致服务器挂壁,独自负责哈~!)
                driverMap.set(ConnectMessage.openId, connection);
                //此处将司机信息保存起来。此信息也可以用redis记录,因为3.2版本之后redis也支持地理操作(如距离查询等等)。
                //当然使用mongodb也是可以的。redis优先级最高,因为可以缓存,查询起来起码会快很多。
                driverContent.push(ConnectMessage)
                //司机连接成功给一个提示!
                connection.send(JSON.stringify({ code: 200, status: true, msg: "连接成功!等待系统为您指派任务!" }));
            }
            if (ConnectMessage.type == "passenger") {
                console.log("乘客连接socket!");
                //将乘客连接头信息装进hashMap形成一个队列。
                passengerMap.set(ConnectMessage.openId, connection);
                //这里是代替redis或mongodb 经纬度距离查询操作。
                for (var i = 0; i < driverContent.length; i++) {
                    //我这里实现的比较简单,正常是拿到客户经纬度去redis或mongodb查询匹配的距离,如多个结果直接拿最近距离的结果。
                    var dist = GetDistance(driverContent[i].lng, driverContent[i].lat, ConnectMessage.lng, ConnectMessage.lat)
                    if (dist < 20) {
                        console.log(dist);
                        console.log("订单匹配成功");
                        driverMap.forEach(function (value, key) {
                            if (driverContent[i].openId == key) {
                                //此处当订单派发成功后,司机端给一个提示,是否接单,如接单就将订单持久化放入数据库,如不接单将订单回归socket订单池。
                                value.send(JSON.stringify({ code: 200, status: true, data: ConnectMessage, msg: "接单成功!" }));
                                //订单发布成功后,通知客户端
                                connection.send(JSON.stringify({ code: 200, status: true, data: driverContent, msg: "附近司机成功接揽您的订单!" }));
                            }
                        }, driverMap)
                    }
                }
            }
        }
    }).on("close", (reasonCode, description) => {
        console.log('[' + new Date() + '] Peer ' + connection.remoteAddress + ' disconnected.')//关闭
        // 由于司机  连接socket 就加入的序列,所有用户退出socket需要移除用户序列
        driverMap.forEach(function (value, key) {
            // 判断关闭socket的连接 是否与序列中的连接一样。如果一样移除
            if (connection === value) {
                driverMap.remove(key)
                //删除司机信息。
                for (var i = 1; i < driverContent.length; i++) {
                    if (driverContent[i].openId == key) {
                        driverContent.splice(driverContent[i], 1)
                    }
                }
            }
        }, driverMap)

        //乘客
        passengerMap.forEach(function (value, key) {
            if (connection === value) {
                passengerMap.remove(key)
            }
        }, passengerMap)
    })
})

httpServer.listen(666, () => {
    console.log((new Date()) + ' Server is listening on port 666');
})

//代替redis 和 Mongodb中的 经纬度地理计算。此方法为其他博客找的,距离查询跟redis mongodb 有很大出入,跟手机APP定位的距离也有出入。
function GetDistance(lat1, lng1, lat2, lng2) {
    var radLat1 = lat1 * Math.PI / 180.0;
    var radLat2 = lat2 * Math.PI / 180.0;
    var a = radLat1 - radLat2;
    var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
    var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
        Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
    s = s * 6378.137;// EARTH_RADIUS;
    s = Math.round(s * 10000) / 10000;
    return s;
}

3.实现客户端(包括客户端,司机端,只有简单JS实现,具体UI实现个人有想法可以自己梭一把。)

司机端:

<template>
	<div>
		<a-button @click="close">断开</a-button>
	</div>
</template>

<script>
	export default{
		name:"socketTest",
		data(){
			return{
				socket:""
			}
		},
		created(){
			this.initWebSocket()
		},
		mounted(){},
		methods:{
			initWebSocket(){
				var url = "ws:192.168.16.90:666";
				this.socket = new WebSocket(url);
				this.socket.onopen = this.onOpen;
				this.socket.onmessage = this.getMessage;
				this.socket.onerror = this.error
				this.socket.onclose = this.close
			},
			onOpen(e){
				console.log("socket连接成功!",e);
				var data = {
					openId:"driver_2d14d3f0d84a11e9b5c7a1dd69b6481f",
					type:"driver",
					name:'chenbd',
					lng:"22.5595796300", 
					lat:"113.8894646400"
					
				}
				this.send(JSON.stringify(data));
			},
			error(err){
				console.log("socket连接失败",err);
			},
			getMessage(message){
				console.log("消息接收成功!")
				console.log(message.data)
				console.log(JSON.parse(message.data))
			},
			send(params){
				this.socket.send(params)
			},
			close(){
				console.log("socket关闭!")
			},
		},
		components:{}
	}
</script>

<style lang="scss" scoped>
</style>

客户端:

<template>
	<div>
		
	</div>
</template>

<script>
	export default{
		name:"socketTest2",
		data(){
			return{
				socket:""
			}
		},
		created(){
			this.initWebSocket()
		},
		mounted(){
			
		},
		methods:{
			initWebSocket(){
				var url = "ws:192.168.16.90:666";
				this.socket = new WebSocket(url);
				this.socket.onopen = this.onOpen;
				this.socket.onmessage = this.getMessage;
				this.socket.onerror = this.error
				this.socket.onclose = this.close
			},
			onOpen(e){
				console.log("socket连接成功!",e);
				var data = {
					openId:"passenger_2d14d3f0d84a11e9b5c7a1dd69b6481f",
					type:"passenger",
					name:'波波先生',
					lng:"22.6980270000", 
					lat:"113.9989310000",
					address:"我要去深圳北站,我需要30个小时后赶到!"
				}
				this.send(JSON.stringify(data));
			},
			error(err){
				console.log("socket连接失败",err);
			},
			getMessage(message){
				console.log("消息接收成功!")
				console.log(message.data)
				console.log(JSON.parse(message.data))
			},
			send(params){
				this.socket.send(params)
			},
			close(){
				console.log("socket关闭!")
			},
		},
		components:{
			
		}
	}
</script>

<style lang="scss" scoped>
</style>

4.实现效果图。

1.先让司机端连线。打开页面,自动连接socket服务器。等待系统自动匹配订单

2.客户端发布订单消息!打开页面自动发布订单消息,等待系统交付司机订单消息,等待通知!

 

5.结尾

做到这就结束了。以上有什么错误或更好的处理方式,请留言我及时更正,避免误人子弟!

同时年尾最后几天了。祝大家明年更好,工作顺心。新年快乐!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值