WebSocket消息推送接收-(微仿滴滴打車業務場景)-(node.js-Vue.js)

年終結尾最後一次更新,在不寫一次估計年前就沒機會再寫了(保證一月一次)。突然想起之前一個朋友問起實時訂單推送如何實現。在年尾沒啥事自己也比較感興趣,簡單實現了一下。

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.結尾

做到這就結束了。以上有什麼錯誤或更好的處理方式,請留言我及時更正,避免誤人子弟!

同時年尾最後幾天了。祝大家明年更好,工作順心。新年快樂!

發佈了39 篇原創文章 · 獲贊 14 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章