WePY+WebSocket實現聊天小程序

socket通常也稱作“套接字”,用於描述IP地址和端口,是一個通信鏈的句柄。可以用來實現不同虛擬機或不同計算機之間的通信。網絡上的兩個程序通過一個雙線的通信連接實現數據的交換,這個連接的一端稱爲一個socket。關於這一點 屬於網絡協議部分內容,想深入瞭解此部分的請移步專欄:

笑談網絡協議


WebSocket是基於TCP的一種新的網絡協議,它實現了瀏覽器與服務器全雙工通信 —— 允許服務器主動發信息給客戶端。
和HTTP的Request請求不同,在實現websocket連接的過程中,瀏覽器需要發出websocket連接請求,然後服務器做出迴應,這個過程也就是常說的“握手”。

在websocket API中,瀏覽器和服務器只需要做一個握手的動作,然後瀏覽器和服務器之間就形成了一條快速通道。

websocket一般用在“客戶端和服務器端交互緊密並且極度頻繁”的場景下(比如:端對端的聊天和網絡遊戲)。打通兩者之間的數據通路,而不用定時一次次地發起普通http請求(輪詢)。

//啓動一個socket代碼(客戶端)
wx.connectSocket({
	//連接一個socket
	url:'wss://example.qq.com',
	data:{},
	header:{
		'content-type':'application/json'
	},
	protocols:['protocol1'],
	method:'GET'
})

表面上看,和普通請求很像,但它的不凡之處就在於:該請求成功連接一個socket以後,將會保持這個連接的狀態,而普通的get/post等請求則是隨着http的斷開而斷開。

這時候,可以調用wx.onSocketOpen這個API監聽websocket連接打開事件:

wx.onSocketOpen(function(res){
	console.log('WebSocket連接已打開!');
})

當一個socket打開以後,最重要的內容則是通過該socket發送一個需要的信息——這需要用到API:wx.sendSocketMessage;當然,這個“發送”必須在“打開”(的回調success)之後(WePY中是在then之後):

wx.onSocketOpen(function(res){
	wx.sendSocketMessage({
		data:msg
	})
})

(不過實際中並不這樣寫,在頁面Load中init“Open”,open中取receive,這個send反而是放在具體監聽的事件中調用)

既然發送出去了,就得接受服務器端的消息(不然怎麼“對話”啊~):在打開socket之後,可以調用wx.onSocketMessage API來接收服務器的消息事件

wx.onSocketMessage(function(res){
	console.log('收到服務器的消息:'+res.data)
})

而在消息的發送和接收過程中,因爲某些原因出現一些錯誤是不可避免的——比如客戶端設備無法打開socket、或者網絡掉線/延遲、或者服務端請求過多造成擁堵…
這時就需要“手動”提示開發者或用戶了:

wx.onSocketError(function(res){
	console.log('websocket連接打開失敗,請檢查系統及網絡!');
})

最後,我們完成了一個socket連接,用戶卻不用了,那就要及時斷開 —— 一個服務器接收和承載連接數是有限的,及時地斷開不需要的鏈接可以極大地減輕服務器的壓力,減少資源的浪費:

wx.onSocketClose(function(res){
	console.log('websocket連接已關閉!');
})

我們將上面的知識點總結一下:
實戰:簡單的socket聊天室小程序
後端:node.js

npm install -g ws

全局安裝websocket用到的npm包。

安裝完成後,在項目中新建一個server.js文件:

const WebsocketServer=require('ws').Server;
let wbsocketServer=new WebsocketServer({
	port:8081,
	autoAcceptConnections:true
})

let clients=[]
let connectNum=0
//監聽連接和消息
wbsocketServer.on('connection',(ws)=>{
	clients.push(ws);
	++connectNum;
	console.log('連接的數量:'+connectNum);

	ws.on('message',(message)=>{
		let objMessage=JSON.parse(message);
		console.log(objMessage.data);
		//可以做一些處理或者轉發其它客戶端
		
	})
	//隨機發送消息
	setInterval(()=>{
		if(connectNum!==0){
			setTimeout(()=>{
				//從連接池中獲取最新連接
				clients[clients.length-1].send(JSON.stringify({data:'來自服務器的消息'}))
			},Math.random()*1000*3)
		}else{
			console.log('無客戶端連接')
		}
	},10000)
	
	ws.on('close',()=>{
		console.log('有連接斷開');
		//刪除不需要的連接——一般是“最老的”一條數據
		clients.pop();
		--connectNum;
	})
})

完後就可以用nodemon server.js命令啓動服務器。

小程序中開發時一定要勾選“未校驗合法域名…”這一項

客戶端開發——WePY

npm install wepy-cli -g

wepy init standard chat
--創建了一個chat項目,完成基本配置後,進入該目錄

npm i

--生成、監控小程序
wepy build -watch

在app.wpy文件的config配置中新增一個chat頁面,並且開啓promisify,並且在pages文件夾下創建chat.wpy文件。修改如下所示:

//開啓promisify
constructor{
	super();
	this.use('requestfix');
	this.use('promisify');
}
pages:[
	'pages/chat'
],

同一般的微信小程序的<block></block>,我們可以用一個數組存儲對話,而使用<repeat></repeat>循環顯示聊天內容。
chat.wpy -> template

<template>
	<view class="page">
		<view class="chats">
			<repeat for="{{chats}}" item="item">
				<view style="font-size:20rpx;color:#ababab">{{item.item}}</view>
				<view style="font-size:25rpx;padding-bottom:20rpx;">{{item.text}}</view>
			</repeat>
		</view>
		<view class="chatInput">
			<input placeholder="請輸入聊天內容" bindinput="userSay" />
		</view>
		<button @tap="sendMessage" size="mini" class="btn">發送消息</button>
	</view>
</template>

chat.wpy -> style

<style lang="less">
	.page{
		position:fixed;
		height:100vh;
		width:100vw;
		background:#e8e9d2;
	}
	.chats{
		text-align:center;
		margin:10vh 10vh 10vw 10vw;
		height:65vh;
		width:80vw;
		background-color:aliceblue;
		overflow:auto;
	}
	.chatInput{
		background:aliceblue;
		height:40rpx;
		font-size:20rpx;
		padding:10rpx;
		width:70vw;
		margin-left:15vw;
		border-radius:20rpx;
		margin-bottom:3vh;
	}
	.btn{
		width:70vw;
		mnargin-left:15vw;
	}
</style>

chat.wpy -> js/script

<script>
	import wepy from 'wepy'
	//監聽是否打開的狀態量
	let socketOpen=false
	export default class chat extends wepy.page{   //wepy中的固定格式
		data={
			say:'',
			chats:[
				{time:'聊天開始',text:''}
			]
		}
		methods={
			//用戶輸入相關
			userSay(e){
				this.say=e.detail.value
				this.$apply()
			},
			//發送對話
			sendMessage(){
				let time=new Date()
				this.chats=this.chats.concat([time:time.toLocaleTimeString(),text:'我說:'+this.say])
				this.handleSendMessage()
				this.$apply()
			}
		}
		//啓動一個socket
		startSocket(){
			wepy.connectSocket({
				url:'ws://127.0.0.1:8081'
			})
		}
		wssInit(){
			const that=this
			this.startSocket()
			//連接失敗顯示
			wepy.onSocketError(function(res){
				socketOpen=false
				console.log('websocket連接打開失敗,請檢查!',res)
				setTimeout(()=>{
					that.startSocket()
				},2000)
			})
			//監聽連接成功
			wepy.onSocketOpen(function(res){
				socketOpen=true
				console.log('websocket連接已打開')
				//接收服務器的消息
				that.receiveMessage()
			})
		}
		receiveMessage(){
			const that=this
			if(socketOpen){
				console.log('讀取socket服務器...')
				wepy.onSocketMessage(function(res){
					let time=new Date()
					let resData=JSON.parse(res.data)
					if(resData.data){
						that.chats=that.chats.concat([{time:time.toLocaleTimeString(),text:'服務器說:'+resData.data}])
						that.$apply()
					}
				})
			}else{
				//未打開狀態需要延時重新連接
				console.log('服務器沒有連接上')
				setTimeout(()=>{
					that.receiveMessage()
				},2000)
			}
		}
		//發送消息
		handleSendMessage(){
			const that=this
			wepy.sendSocketMessage({
				data:JSON.stringify({data:that.say})
			})
		}
		events={}

		onLoad(){
			const that=this
			setTimeout(()=>{
				that.wssInit()
			},1000)
		}
	}
</script>

wepy是微信小程序中的一個框架,它集合了vue的優勢。
故而上述js代碼中操作data數據時直接this.xxx而不是this.data.xxx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章