1、 当接到项目的时候我最先尝试的是原生的STOMP协议,在浏览器上完美的实现,当时感觉人生一片美好,Soeasy啊有木有!某一天心血来潮把手机插上看了一眼,看到控制台疯狂报错的一瞬间我慌了,然后就是各种的排查,和找解决的方法,并且使用官方提供的uni.connectSocket()方法来创建socket连接在APP端死活不给面子,然后无奈放弃。
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js" charset="utf-8"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js" charset="utf-8"></script>
<script>
var stompClient = null;
//加载完浏览器后 调用connect(),打开双通道
$(function(){
//打开双通道
connect()
})
//强制关闭浏览器 调用websocket.close(),进行正常关闭
window.onunload = function() {
disconnect()
}
var auth="Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInJvbGUiOjEsImlhdCI6MTU4OTM1NzA1MCwiZXhwIjoxNTkwNTY2NjUwfQ.0KeeyGwQmOs42Iv_iYBbK2BBvi9BcH47RW2OGTwOH-g";
//打开双通道
function connect(){
// var socket = new WebSocket('ws:localhost:8080/admin/hello'); //连接SockJS的endpoint名称为"endpointAric"
// stompClient = Stomp.over(socket);//使用STMOP子协议的WebSocket客户端
var url = "ws:localhost:8080/admin/hello";
stompClient = Stomp.client(url);
//设置心跳
// client will send heartbeats every 20000ms
// stompClient.heartbeat.outgoing = 2000;
// // client does not want to receive heartbeats from the server
// stompClient.heartbeat.incoming = 2000;
stompClient.connect({"Authorization":auth},function(frame){//连接WebSocket服务端
console.log('Connected:' + frame);
//广播接收信息
stompTopic();
//私聊天监听消息
stompQueue();
},function (error) {
console.log('ConnectedError:' + error);
//发生错误,可以在此处进行重连
});
}
//关闭双通道
function disconnect(){
if(stompClient != null) {
stompClient.disconnect();
}
console.log("Disconnected");
}
//广播(一对多)
function stompTopic(){
//通过stompClient.subscribe订阅/topic/getResponse 目标(destination)发送的消息(广播接收信息)
stompClient.subscribe('/topic/getResponse',function(response){
// var message=JSON.parse(response.body);
console.log(response)
},{"Authorization":auth})
//订阅错误
stompClient.subscribe('/topic/errors',function(response){
// var message=JSON.parse(response.body);
console.log(response)
},{"Authorization":auth})
//订阅错误私发
stompClient.subscribe('/user/' + 1 + '/errors',function(response){
// var message=JSON.parse(response.body);
console.log(response)
},{"Authorization":auth})
}
//列队(一对一)
function stompQueue(){
//通过stompClient.subscribe订阅/topic/getResponse 目标(destination)发送的消息(队列接收信息)
stompClient.subscribe('/user/' + 1 + '/alone/getResponse',function(response){
// var message=JSON.parse(response.body);
//展示一对一的接收的内容接收
console.log(response);
},{"Authorization":"私发订阅"});
}
//群发
function sendMassMessage(){
var postValue={};
postValue.name="1";
postValue.chatValue="2";
stompClient.send("/massRequest",{"Authorization":"群发"},JSON.stringify(postValue));
}
//单独发
function sendAloneMessage(){
var postValue={};
stompClient.send("/aloneRequest",{"Authorization":"私发"},JSON.stringify(postValue));
}
</script>
2、查看了腾讯IM的官方网站有小程序、webIM、Android IM、iOS IM但就是没有uniapp的,这个难受哦!试试webIM吧,然后试啊试,这次我学聪明了直接上手机端调试,然而最后并没有成功,这时候才发现web IM SDK并不支持uniapp编译到APP端,该怎么办呢?腾讯这种大公司提供的SDK都不支持我该肿么办呢?
3、在我抱怨之际让我发现了新的希望,GoEasy——一个简单到只要有手就会用的socket框架,当然使用简单的代价就是不够灵活,且功能也不够健全,并且最最致命的的是聊天信息不能进行保存全都在人家的服务器上,中间全部都是客户端与GoEasy之间的通讯跟公司服务器完全没关系了,不带后端完的结果就是大家都没得玩。
4、然后尝试了原生安卓,这次经历太过曲折就不再展开说了,总之是通过这次尝试虽然最终没有真正的成功的用到的项目中,但是却让我了解了更多的前端APP开发相关的知识,同时也想明白了做事还是要靠自己,怕麻烦只会更麻烦。
5、经过上面的几番尝试,最终还是选择了**环信的小程序端IM的SDK**,到此终于建立连接成功了。你以为结束了?不不不,这只是开始。
6、当我把发送文字消息链路打通能够正常的接收发送文字的时候我以为这一切都结束了,但是当我拿出来展示的时候才发现自己写的有多lou。
7、 当我解决了IM,再向下做地图坐标推送的时候需要再创建一条新的socket连接,再次遇到了问题,自己创建的socket连接和环信的IM冲突了
,看文档,搜帖,问大佬,又卡了很长很长的时间,然后被逼无奈之下查看了环信的SDK源码,在登录回调中找到了问题所在,因为是两个socket连接,环信的socket会误把uniapp的socket的连接状态当做自己的然后导致后面执行的代码出现问题(按照正常的逻辑两条链接是毫无关系的,ip端口号都是不想管的,但是不知道为什么造成了冲突),所以我将使用uniapp创建的socket连接放到了环信创建的连接成功的回调后面该问题得以解决,但是紧接着新的问题又出现了。
8、环信IM与自定义socket连接监听件发生冲突
,使用uniapp官方提供的方法会全局监听搜有的socket连接,这个问题到没有困扰我很长时间,最后证实确实是官方的bug,且官方给出了解决的方案plus-websocket,终于通过plus-websocket解决环信和自定义的socket连接冲突的问题。
① 官方提供的创建socket连接的API问题分析:
② plus-websocket实现原理:
9、使用plus-websocket创建两条socket连接发消息走同一条通道的问题
,两个连接发送的消息都只会发送给其中某一个连接,具体的原因没有找到,但我通过修改源码中打开web页面的id来生成一个新的plus-websocket.js文件,相当于每一个连接单独引用了一个plus-websocket.js文件,从而导致打开的web页面不同最终导致的作用域不同,所以不会再互相影响。
至此终于可以愉快的玩耍了!
最后附上自定义socket连接心跳检测的代码,小白可以做参考,有什么不合理的地方请大佬多指正:
// 保存聊天历史记录
import {storage, $ws, toast, skip02} from '@/common/forScript/config.js'
import {loginInfo} from '@/common/forScript/res.js'
import Sockets from './socket2.js'
/* 获取本地存储的token */
function getToken() { if(storage.getDataSync('token') != false) { return storage.getDataSync('token') }else {return false}}
/* 获取当前时间 */
function getNowTime() {return (new Date()).getTime()}
const s = new Sockets()
let heartbeat = null // 心跳定时器
let num = 0 // 计算心跳次数
let connectionStatus = false // 连接状态
let ExportFun = {
// 初始化socket
initSocket: function(that) {
// 创建socket连接
s.connectSocket({
url: $ws+'admin/YsWebSocket?token=' + getToken() + '&type=2',
// url: 'ws://echo.websocket.org', // 测试地址
success(res) {
console.log('uniapp - 聊天记录 -socket连接已经打开!')
connectionStatus = true
// 连接成功调起心跳任务
ExportFun.onHeartBeat()
let info = that.$store.state.loginInfo
console.log(info)
loginInfo(that, info).then((data)=>{
console.log(data)
login(data.res, data.res01)
})
},
fail(err) {
console.log(err)
}
})
// 监听接收自定义socket,因为业务中没有用到所以注释了
// s.onSocketMessage(function (res) {
// console.log(res)
// });
// 监听是否创建连接成功,没有走该方法所以成功后的逻辑代码都写到了init中
s.onSocketOpen(function (res) {
// console.log('uniapp - 聊天记录 -socket连接已经打开!')
// // socketLocation.initSocket()
// ExportFun.onHeartBeat()
});
// 监听连接是否关闭
ExportFun.onSocketClose()
s.onSocketError(function (res) {
if(connectionStatus) {return}
console.log('uniapp - 聊天记录 -WebSocket连接打开失败,请检查!');
if(storage.getDataSync('token') != false) {
console.log('uniapp - 聊天记录 -WebSocket连接失败,重新建立连接!');
let rconnectSocket = setTimeout(()=>{
// 重新创建连接
ExportFun.reconnection()
clearTimeout(rconnectSocket)
}, 10000)
}
})
},
// 進入页面检测链接是否正常
onShowHeartbeat: function() {
console.log('进入页面启动心跳检测')
if(heartbeat != null) {
clearTimeout(heartbeat)
}
ExportFun.onHeartBeat()
},
// 发送数据
sendSocketMessage: function(a) {
s.sendSocketMessage({
data: JSON.stringify(a),
success(res) {
num = 0
ExportFun.onHeartBeat()
},
fail(err) {
num++
if(num == 5) {
console.log('uniapp - 聊天记录 -心跳监测超时,主动断开连接!')
num = 0
// 重新创建连接
ExportFun.reconnection()
}
ExportFun.onHeartBeat()
}
});
/* 发送数据 结束 */
},
// 心跳检测
onHeartBeat: function() {
/**
* 第一次发消息成功如果定时器不存在则直接创建新的定时任务,
* 第二次发消息成功则先清除之前的定时任务再次发送新的定时任务
* */
if(storage.getDataSync('token') != false) {
if(heartbeat != null) {
console.log('uniapp - 聊天记录 -发送心跳', num)
clearTimeout(heartbeat)
// 发送消息成功10秒后重新调起心跳连接
heartbeat = setTimeout(()=>{
ExportFun.sendSocketMessage(0) // 发送心跳数据
}, 10000)
} else {
console.log('uniapp - 聊天记录 -发送心跳', num)
// 发送消息成功10秒后重新调起心跳连接
heartbeat = setTimeout(()=>{
ExportFun.sendSocketMessage(0) // 发送心跳数据
}, 10000)
}
} else {
// 重新创建连接
// ExportFun.reconnection()
}
},
// 关闭socket连接
closeSocket: function() {
s.closeSocket({
success: function(res) {
console.log('连接已关闭!')
},
fail: function(err) {
}
})
},
// 监听连接断开
onSocketClose: function() {
s.onSocketClose(function (res) {
console.log('uniapp - 聊天记录 -WebSocket 已关闭!')
// 更改连接状态
connectionStatus = false
// 重新创建连接
ExportFun.reconnection()
})
},
// 重新创建连接
reconnection: function() {
// 如果处于连接状态则不再重新创建
if(connectionStatus) {return}
// 清除心跳定时器
if(heartbeat != null) {clearTimeout(heartbeat)}
// 主动调起关闭连接的方法
ExportFun.closeSocket()
if(storage.getDataSync('token') != false) {
let a = setTimeout(()=>{
ExportFun.initSocket()
clearTimeout(a)
}, 10000)
}
}
}
function login(res, res01) {
/* 根据登陆人是否已经完善登陆信息决定跳转的页面 */
if(res.data.step == 1) { // 补充个人信息
if(res.data.roles == '3') { // 执行方 企业
skip02('../reg/reg02?regType=3&step='+res.data.step)
}
if(res.data.roles == '4') { // 执行方 司机
skip02('../reg/reg02?regType=4&step='+res.data.step)
}
return false
} else if(res.data.step == 2) { // 补充企业信息
if(res.data.roles == '3') { // 执行方 企业
skip02('../reg/reg03')
}
if(res.data.roles == '4') { // 执行方 司机
skip02('../reg/reg02?regType=4&step='+res.data.step)
}
return false
}
console.log(res.data.step)
/* 判断登陆人的身份进行页面跳转 */
if(!res.data.infoCheck && res.data.roles == 1) { // 需求方
console.log(res.data.infoCheck)
storage.setDataSync('infoCheck', '0') // 0=>false
skip02('../../id1/informationComplete/informationComplete')
return
}
skip02('../../home/home')
}
export default ExportFun
如果对您有帮助请点个赞呗!