我們項目之前前后端使用的是Stomp + SockJs實現的在線直播的實時聊天,現在需要搬到app上,所以要在uni-app上面也實現一次,結果就很自然的出問題了。下面整理一下在uniapp整合WebSocket中遇到的bug。
1、第一次嘗試
先像web開發一樣去寫:先引入stomjs和sockjs包,然后用 new SockJS(url) 的方式去實例化SockJs,然后通過創建StompClient去連接訂閱。
H5時連接沒有任何問題,但換到uni-app的時候問題就來了,sockjs.js里面用了一點操作DOM元素的方法,比如document.getElementsByTagName
,uni-app是解析不了的,原生的微信小程序好像也解析不了。
2、第二次嘗試
這次放棄了使用外部的js,采用原生的uni.connectSocket()的方式創建連接。
SocketTask = uni.connectSocket({
url: 'http://***:8081/socket', //url: 'ws://localhost:8081/socket',
data: 'data', header: { 'content-type': 'application/json' }, method: 'post', success: function(res) { console.log('WebSocket連接創建', res) }, fail: function(err) { uni.showToast({ title: '網絡異常!', }) console.log(err) }, })
代碼中顯示的就很清楚了,uni-app連接websocket的時候要是用ws或者wss,相當於http和https,不然就會報無效的url這種異常,但是這個時候還是會拋異常:Error during WebSocket handshake: Unexpected response code: 200
其中遇到的問題就是使用原生websocket怎么也連不上的問題,后來就查看服務端是如何處理sockjs的。然后我就拉取了后端代碼,去后端代碼里最好找到了問題的關鍵,先貼一下服務端配置:
@Override //用來注冊Endpoint,“/event-websocket”即為客戶端嘗試建立連接的地址。
public void registerStompEndpoints(StompEndpointRegistry registry) { registry .addEndpoint("/event-websocket") .setAllowedOrigins("*") .withSockJS(); }
可以看到服務端是開啟了sockjs模式的,將此處的withSockJS()去除,就可以通過原生方式連接了。因為sockjs在瀏覽器不支持wobsoket請求是會自動切換為http請求輪訓方式。
但是去除withSockJS(),那么web就無法連接了,怎么辦呢?可以注冊建立2個鏈接地址,一個給web使用sockjs模式,另一個給app使用,不使用sockjs模式。
@Override //用來注冊Endpoint,“/event-websocket”即為客戶端嘗試建立連接的地址。
public void registerStompEndpoints(StompEndpointRegistry registry) { registry .addEndpoint("/event-websocket") .setAllowedOrigins("*") .withSockJS(); registry .addEndpoint("/event-websocket-app") .setAllowedOrigins("*"); }
3、第3次嘗試
經過上述處理,以為萬事大吉了,但是結果在安卓app上是可以正常建立鏈接的,但是在ios app上卻不行,百思不得解,最后解決方式是將域名模式改成ip模式就可以了
// 連接后台
initSocket () { let that = this let _unitoken = uni.getStorageSync('token') let _token = _unitoken ? _unitoken.substring(7) : ''
// 當未登錄或登錄失效時后台根據_randomUserId模擬匿名用戶
let _randomUserId = (-generateRandom(100000)) let _eventId = this.params.eventId let sockUrl = wsdomain + `event-websocket-app?eventId=${_eventId}&token=${_token}&userId=${_randomUserId}` let socket = new UniWebSocket(sockUrl) this.stompClient = Stomp.over(socket) this.stompClient.connect({}, (res) => { // 訂閱服務端提供的某個topic
this.stompClient.subscribe(`/topic/event/${_eventId}`, (frame) => { that.addBarage(JSON.parse(frame.body)) }) that.sentFirst() }, (err) => { console.log('失敗:' + err) if (this.infoCount > 5) { let _speaker = this.event.currentAgendaSpeaker || {} let _tip = { headimgurl: _speaker.icon, name: _speaker.name, text: '溫馨提示:您的連接已斷開,將無法收到聊天信息,請退出直播間重新進入。' } this.infoFlow.push(_tip) this.infoCount = 0 } if (!this.timer) this.resetWebSocket() else clearInterval(this.timer) }) this.stompClient.debug = null },
wsdomain由原域名形式換成ip形式就可以了。
wss://dev.modb.pro/ws/
ws://服務器ip:8080/