Browse Source

!69 断线重连优化

Merge pull request !69 from blue/v_2.0.0
master
blue 2 years ago
committed by Gitee
parent
commit
be49d99aae
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 2
      im-server/src/main/java/com/bx/imserver/netty/IMChannelHandler.java
  2. 5
      im-server/src/main/java/com/bx/imserver/netty/processor/HeartbeatProcessor.java
  3. 2
      im-ui/package.json
  4. 112
      im-uniapp/App.vue
  5. 3
      im-uniapp/common/request.js
  6. 9
      im-uniapp/common/wssocket.js
  7. 6
      im-uniapp/components/group-member-selector/group-member-selector.vue
  8. 2
      im-uniapp/pages/group/group-info.vue
  9. 10
      im-uniapp/static/icon/iconfont.css
  10. BIN
      im-uniapp/static/icon/iconfont.ttf
  11. 11
      im-uniapp/store/chatStore.js

2
im-server/src/main/java/com/bx/imserver/netty/IMChannelHandler.java

@ -74,7 +74,7 @@ public class IMChannelHandler extends SimpleChannelInboundHandler<IMSendInfo> {
RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate"); RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString()); String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString());
redisTemplate.delete(key); redisTemplate.delete(key);
log.info("断开连接,userId:{},终端类型:{}", userId, terminal); log.info("断开连接,userId:{},终端类型:{},{}", userId, terminal, ctx.channel().id().asLongText());
} }
} }

5
im-server/src/main/java/com/bx/imserver/netty/processor/HeartbeatProcessor.java

@ -30,7 +30,7 @@ public class HeartbeatProcessor extends AbstractMessageProcessor<IMHeartbeatInfo
IMSendInfo sendInfo = new IMSendInfo(); IMSendInfo sendInfo = new IMSendInfo();
sendInfo.setCmd(IMCmdType.HEART_BEAT.code()); sendInfo.setCmd(IMCmdType.HEART_BEAT.code());
ctx.channel().writeAndFlush(sendInfo); ctx.channel().writeAndFlush(sendInfo);
;
// 设置属性 // 设置属性
AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf(ChannelAttrKey.HEARTBEAT_TIMES); AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf(ChannelAttrKey.HEARTBEAT_TIMES);
Long heartbeatTimes = ctx.channel().attr(heartBeatAttr).get(); Long heartbeatTimes = ctx.channel().attr(heartBeatAttr).get();
@ -44,6 +44,9 @@ public class HeartbeatProcessor extends AbstractMessageProcessor<IMHeartbeatInfo
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString()); String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString());
redisTemplate.expire(key, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS); redisTemplate.expire(key, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
} }
AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
Long userId = ctx.channel().attr(userIdAttr).get();
log.info("心跳,userId:{},{}",userId,ctx.channel().id().asLongText());
} }
@Override @Override

2
im-ui/package.json

@ -13,7 +13,7 @@
"element-ui": "^2.15.10", "element-ui": "^2.15.10",
"js-audio-recorder": "^1.0.7", "js-audio-recorder": "^1.0.7",
"sass": "^1.47.0", "sass": "^1.47.0",
"sass-loader": "^7.3.1", "sass-loader": "^10.1.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-axios": "^3.5.0", "vue-axios": "^3.5.0",
"vue-router": "^3.3.3", "vue-router": "^3.3.3",

112
im-uniapp/App.vue

@ -9,7 +9,8 @@
export default { export default {
data() { data() {
return { return {
audioTip: null audioTip: null,
reconnecting: false //
} }
}, },
methods: { methods: {
@ -28,6 +29,14 @@
wsApi.init(); wsApi.init();
wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken); wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken);
wsApi.onConnect(() => { wsApi.onConnect(() => {
//
if(this.reconnecting){
this.reconnecting = false;
uni.showToast({
title: "已重新连接",
icon: 'none'
})
}
// 线 // 线
this.pullPrivateOfflineMessage(store.state.chatStore.privateMsgMaxId); this.pullPrivateOfflineMessage(store.state.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(store.state.chatStore.groupMsgMaxId); this.pullGroupOfflineMessage(store.state.chatStore.groupMsgMaxId);
@ -49,35 +58,30 @@
} }
}); });
wsApi.onClose((res) => { wsApi.onClose((res) => {
console.log("ws断开",res); console.log("ws断开", res);
// 1000 // 3099
if (res.code != 1000) { if (res.code != 3099) {
// //
uni.showToast({ this.reconnectWs();
title: '连接已断开,尝试重新连接...',
icon: 'none',
})
let loginInfo = uni.getStorageSync("loginInfo")
wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken);
} }
}) })
}, },
pullPrivateOfflineMessage(minId) { pullPrivateOfflineMessage(minId) {
store.commit("loadingPrivateMsg",true) store.commit("loadingPrivateMsg", true)
http({ http({
url: "/message/private/pullOfflineMessage?minId=" + minId, url: "/message/private/pullOfflineMessage?minId=" + minId,
method: 'GET' method: 'GET'
}).catch(()=>{ }).catch(() => {
store.commit("loadingPrivateMsg",false) store.commit("loadingPrivateMsg", false)
}) })
}, },
pullGroupOfflineMessage(minId) { pullGroupOfflineMessage(minId) {
store.commit("loadingGroupMsg",true) store.commit("loadingGroupMsg", true)
http({ http({
url: "/message/group/pullOfflineMessage?minId=" + minId, url: "/message/group/pullOfflineMessage?minId=" + minId,
method: 'GET' method: 'GET'
}).catch(()=>{ }).catch(() => {
store.commit("loadingGroupMsg",false) store.commit("loadingGroupMsg", false)
}) })
}, },
handlePrivateMessage(msg) { handlePrivateMessage(msg) {
@ -96,7 +100,9 @@
} }
// , // ,
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { if (msg.type == enums.MESSAGE_TYPE.RECEIPT) {
store.commit("readedMessage", { friendId: msg.sendId }) store.commit("readedMessage", {
friendId: msg.sendId
})
return; return;
} }
// //
@ -112,17 +118,17 @@
// //
if (msgType.isRtcPrivate(msg.type)) { if (msgType.isRtcPrivate(msg.type)) {
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// //
return; return;
// #endif // #endif
// //
let delayTime = 100; let delayTime = 100;
if(msg.type == enums.MESSAGE_TYPE.RTC_CALL_VOICE if (msg.type == enums.MESSAGE_TYPE.RTC_CALL_VOICE ||
|| msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO){ msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO) {
let mode = msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO? "video":"voice"; let mode = msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO ? "video" : "voice";
let pages = getCurrentPages(); let pages = getCurrentPages();
let curPage = pages[pages.length-1].route; let curPage = pages[pages.length - 1].route;
if(curPage != "pages/chat/chat-private-video"){ if (curPage != "pages/chat/chat-private-video") {
const friendInfo = encodeURIComponent(JSON.stringify(friend)); const friendInfo = encodeURIComponent(JSON.stringify(friend));
uni.navigateTo({ uni.navigateTo({
url: `/pages/chat/chat-private-video?mode=${mode}&friend=${friendInfo}&isHost=false` url: `/pages/chat/chat-private-video?mode=${mode}&friend=${friendInfo}&isHost=false`
@ -131,8 +137,8 @@
} }
} }
setTimeout(() => { setTimeout(() => {
uni.$emit('WS_RTC_PRIVATE',msg); uni.$emit('WS_RTC_PRIVATE', msg);
},delayTime) }, delayTime)
return; return;
} }
let chatInfo = { let chatInfo = {
@ -152,7 +158,7 @@
handleGroupMessage(msg) { handleGroupMessage(msg) {
// //
if (msg.type == enums.MESSAGE_TYPE.LOADING) { if (msg.type == enums.MESSAGE_TYPE.LOADING) {
store.commit("loadingGroupMsg",JSON.parse(msg.content)) store.commit("loadingGroupMsg", JSON.parse(msg.content))
return; return;
} }
// //
@ -189,15 +195,15 @@
// //
if (msgType.isRtcGroup(msg.type)) { if (msgType.isRtcGroup(msg.type)) {
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// //
return; return;
// #endif // #endif
// //
let delayTime = 100; let delayTime = 100;
if(msg.type == enums.MESSAGE_TYPE.RTC_GROUP_SETUP){ if (msg.type == enums.MESSAGE_TYPE.RTC_GROUP_SETUP) {
let pages = getCurrentPages(); let pages = getCurrentPages();
let curPage = pages[pages.length-1].route; let curPage = pages[pages.length - 1].route;
if(curPage != "pages/chat/chat-group-video"){ if (curPage != "pages/chat/chat-group-video") {
const userInfos = encodeURIComponent(msg.content); const userInfos = encodeURIComponent(msg.content);
const inviterId = msg.sendId; const inviterId = msg.sendId;
const groupId = msg.groupId const groupId = msg.groupId
@ -210,8 +216,8 @@
} }
// chat-group-video // chat-group-video
setTimeout(() => { setTimeout(() => {
uni.$emit('WS_RTC_GROUP',msg); uni.$emit('WS_RTC_GROUP', msg);
},delayTime) }, delayTime)
return; return;
} }
@ -275,11 +281,37 @@
// this.audioTip.src = "/static/audio/tip.wav"; // this.audioTip.src = "/static/audio/tip.wav";
// this.audioTip.play(); // this.audioTip.play();
}, },
isExpired(loginInfo){ isExpired(loginInfo) {
if(!loginInfo || !loginInfo.expireTime){ if (!loginInfo || !loginInfo.expireTime) {
return true; return true;
} }
return loginInfo.expireTime < new Date().getTime(); return loginInfo.expireTime < new Date().getTime();
},
reconnectWs() {
//
this.reconnecting = true;
// token
this.reloadUserInfo().then((userInfo) => {
uni.showToast({
title: '连接已断开,尝试重新连接...',
icon: 'none',
})
store.commit("setUserInfo", userInfo);
//
let loginInfo = uni.getStorageSync("loginInfo")
wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken);
}).catch(() => {
// 5s
setTimeout(()=>{
this.reconnectWs();
},5000)
})
},
reloadUserInfo() {
return http({
url: '/user/self',
method: 'GET'
})
} }
}, },
onLaunch() { onLaunch() {
@ -293,12 +325,12 @@
uni.switchTab({ uni.switchTab({
url: "/pages/chat/chat" url: "/pages/chat/chat"
}) })
} else{ } else {
// //
// #ifdef H5 // #ifdef H5
uni.navigateTo({ uni.navigateTo({
url: "/pages/login/login" url: "/pages/login/login"
}) })
// #endif // #endif
} }
} }

3
im-uniapp/common/request.js

@ -61,7 +61,8 @@ const request = (options) => {
}, },
fail(error) { fail(error) {
uni.showToast({ uni.showToast({
title: "网络似乎有点不给力,请稍后重试", title: "网络似乎有点不给力哟",
icon: "none",
duration: 1500 duration: 1500
}) })
return reject(error) return reject(error)

9
im-uniapp/common/wssocket.js

@ -6,6 +6,7 @@ let connectCallBack = null;
let isConnect = false; //连接标识 避免重复连接 let isConnect = false; //连接标识 避免重复连接
let rec = null; let rec = null;
let isInit = false; let isInit = false;
let lastConnectTime = new Date(); // 最后一次连接时间
let init = () => { let init = () => {
// 防止重复初始化 // 防止重复初始化
@ -64,6 +65,7 @@ let connect = (url, token) => {
if (isConnect) { if (isConnect) {
return; return;
} }
lastConnectTime = new Date();
uni.connectSocket({ uni.connectSocket({
url: wsurl, url: wsurl,
success: (res) => { success: (res) => {
@ -86,10 +88,13 @@ let reconnect = (wsurl, accessToken) => {
//如果已经连上就不在重连了 //如果已经连上就不在重连了
return; return;
} }
// 延迟10秒重连 避免过多次过频繁请求重连
let timeDiff = new Date().getTime() - lastConnectTime.getTime()
let delay = timeDiff < 10000 ? 10000 - timeDiff : 0;
rec && clearTimeout(rec); rec && clearTimeout(rec);
rec = setTimeout(function() { // 延迟15秒重连 避免过多次过频繁请求重连 rec = setTimeout(function() {
connect(wsurl, accessToken); connect(wsurl, accessToken);
}, 15000); }, delay);
}; };
//设置关闭连接 //设置关闭连接

6
im-uniapp/components/group-member-selector/group-member-selector.vue

@ -4,7 +4,7 @@
<view class="top-bar"> <view class="top-bar">
<view class="top-tip">选择成员</view> <view class="top-tip">选择成员</view>
<button class="top-btn" type="warn" size="mini" @click="onClean()">清空 </button> <button class="top-btn" type="warn" size="mini" @click="onClean()">清空 </button>
<button class="top--btn" type="primary" size="mini" @click="onOk()">确定({{checkedIds.length}}) <button class="top-btn" type="primary" size="mini" @click="onOk()">确定({{checkedIds.length}})
</button> </button>
</view> </view>
<scroll-view v-show="checkedIds.length>0" scroll-x="true" scroll-left="120"> <scroll-view v-show="checkedIds.length>0" scroll-x="true" scroll-left="120">
@ -120,7 +120,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
height: 70rpx; height: 70rpx;
padding: 10rpx; padding: 10rpx 30rpx;
.top-tip { .top-tip {
flex: 1; flex: 1;
@ -135,7 +135,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
height: 90rpx; height: 90rpx;
padding: 0 30rpx;
.user-item { .user-item {
padding: 3rpx; padding: 3rpx;
} }

2
im-uniapp/pages/group/group-info.vue

@ -112,6 +112,7 @@
url:"/pages/group/group" url:"/pages/group/group"
}); });
this.$store.commit("removeGroup", this.groupId); this.$store.commit("removeGroup", this.groupId);
this.$store.commit("removeGroupChat",this.groupId);
},100) },100)
} }
}) })
@ -141,6 +142,7 @@
url:"/pages/group/group" url:"/pages/group/group"
}); });
this.$store.commit("removeGroup", this.groupId); this.$store.commit("removeGroup", this.groupId);
this.$store.commit("removeGroupChat",this.groupId);
},100) },100)
} }
}) })

10
im-uniapp/static/icon/iconfont.css

@ -1,6 +1,6 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4272106 */ font-family: "iconfont"; /* Project id 4272106 */
src: url('iconfont.ttf?t=1711870080646') format('truetype'); src: url('iconfont.ttf?t=1719727774055') format('truetype');
} }
.iconfont { .iconfont {
@ -11,6 +11,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-receipt:before {
content: "\e601";
}
.icon-pause:before { .icon-pause:before {
content: "\e669"; content: "\e669";
} }
@ -35,10 +39,6 @@
content: "\e685"; content: "\e685";
} }
.icon-receipt:before {
content: "\e61a";
}
.icon-ok:before { .icon-ok:before {
content: "\e65a"; content: "\e65a";
} }

BIN
im-uniapp/static/icon/iconfont.ttf

Binary file not shown.

11
im-uniapp/store/chatStore.js

@ -133,6 +133,15 @@ export default {
} }
} }
}, },
removeGroupChat(state, groupId) {
let chats = this.getters.findChats();
for (let idx in chats) {
if (chats[idx].type == 'GROUP' &&
chats[idx].targetId == groupId) {
this.commit("removeChat", idx);
}
}
},
moveTop(state, idx) { moveTop(state, idx) {
let chats = this.getters.findChats(); let chats = this.getters.findChats();
let chat = chats[idx]; let chat = chats[idx];
@ -294,6 +303,8 @@ export default {
}); });
// 将消息一次性装载回来 // 将消息一次性装载回来
state.chats = cacheChats; state.chats = cacheChats;
// 断线重连后不能使用缓存模式,否则会导致聊天窗口的消息不刷新
cacheChats = state.chats;
this.commit("saveToStorage"); this.commit("saveToStorage");
}, },
saveToStorage(state) { saveToStorage(state) {

Loading…
Cancel
Save