mirror of
https://github.com/WuKongIM/WuKongIMAndroidSDK
synced 2025-05-28 04:02:35 +00:00
fix:优化连接
This commit is contained in:
parent
61dd373418
commit
16c6ca8d57
@ -3,3 +3,42 @@ description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# 连接管理机制
|
||||
|
||||
## 连接状态流转
|
||||
连接状态在 [WKConnection.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java) 中管理,遵循以下规则:
|
||||
|
||||
### 有效的状态转换
|
||||
- 失败状态 -> 连接中/成功
|
||||
- 连接中 -> 成功/失败/无网络
|
||||
- 成功 -> 同步消息/被踢/失败
|
||||
- 同步消息 -> 成功/失败
|
||||
- 无网络 -> 连接中/失败
|
||||
|
||||
### 关键同步点
|
||||
所有状态转换都需要通过 `connectionLock` 同步锁保护:
|
||||
```java
|
||||
synchronized (connectionLock) {
|
||||
// 状态转换操作
|
||||
}
|
||||
```
|
||||
|
||||
## 重连机制
|
||||
重连策略采用指数退避算法:
|
||||
- 基础延迟:1秒
|
||||
- 最大延迟:32秒
|
||||
- 最大重试次数:5次
|
||||
|
||||
## 连接关闭处理
|
||||
连接关闭流程:
|
||||
1. 使用 `isClosing` 原子变量防止重复关闭
|
||||
2. 设置连接标记防止重连
|
||||
3. 清理连接资源
|
||||
4. 触发超时强制关闭
|
||||
|
||||
## 异常处理
|
||||
主要在 [ConnectionClient.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/ConnectionClient.java) 中处理:
|
||||
- 连接异常
|
||||
- 连接超时
|
||||
- 心跳超时
|
||||
- 空闲超时
|
||||
|
@ -3,3 +3,36 @@ description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# 消息处理机制
|
||||
|
||||
## 消息发送流程
|
||||
在 [WKConnection.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java) 中实现:
|
||||
|
||||
### 发送前检查
|
||||
- 检查连接状态
|
||||
- 验证消息完整性
|
||||
- 处理附件(图片、视频等)
|
||||
|
||||
### 消息队列管理
|
||||
使用 `sendingMsgHashMap` 管理发送中的消息:
|
||||
- 消息重发机制
|
||||
- 发送超时处理
|
||||
- 发送状态追踪
|
||||
|
||||
### 消息同步
|
||||
两种同步模式:
|
||||
- WRITE模式:完整消息同步
|
||||
- READ模式:仅同步会话列表
|
||||
|
||||
## 消息接收处理
|
||||
主要在 ConnectionClient 的 onData 方法中处理:
|
||||
- 消息解析
|
||||
- 重复消息过滤
|
||||
- 消息排序
|
||||
- 存储和回调
|
||||
|
||||
## 特殊消息处理
|
||||
- 心跳消息(ping/pong)
|
||||
- 连接确认消息
|
||||
- 踢下线消息
|
||||
- 同步完成消息
|
||||
|
@ -3,3 +3,38 @@ description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# WuKongIM Android SDK 项目结构
|
||||
|
||||
WuKongIM Android SDK 是一个即时通讯SDK,主要包含以下核心组件:
|
||||
|
||||
## 核心连接管理
|
||||
主要连接管理类在 [WKConnection.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java) 中实现,负责:
|
||||
- Socket连接的建立和维护
|
||||
- 消息的发送和接收
|
||||
- 重连机制
|
||||
- 心跳管理
|
||||
|
||||
## 连接状态处理
|
||||
连接状态定义在 [WKConnectStatus.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/type/WKConnectStatus.java) 中,包括:
|
||||
- 连接成功(1)
|
||||
- 连接失败(0)
|
||||
- 被踢下线(2)
|
||||
- 同步消息中(3)
|
||||
- 连接中(4)
|
||||
- 无网络(5)
|
||||
- 同步完成(6)
|
||||
|
||||
## 客户端连接处理
|
||||
[ConnectionClient.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/ConnectionClient.java) 处理具体的连接事件:
|
||||
- 连接建立回调
|
||||
- 数据接收
|
||||
- 连接断开处理
|
||||
- 超时处理
|
||||
- 异常处理
|
||||
|
||||
## 应用配置
|
||||
[WKIMApplication.java](mdc:wkim/src/main/java/com/xinbida/wukongim/WKIMApplication.java) 管理SDK全局配置:
|
||||
- 协议版本
|
||||
- 同步消息模式
|
||||
- 网络状态
|
||||
- 数据库配置
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,5 +13,4 @@
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/repository
|
||||
.cursor
|
||||
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
2a8fa0fa06d069610eed09005b343994
|
@ -0,0 +1 @@
|
||||
34a3a10b015c40e16a76fe75cb974d675a9cbda6
|
@ -0,0 +1 @@
|
||||
eb0a4ee07731a3ef38f606b2978d16169a6f6388e3f33fa3a10c82f3b0c17bb0
|
@ -0,0 +1 @@
|
||||
d1a0aa32976653fa04696f6d42edbe8152b59fdaf0135350cdefa14d571212571e85412c8f026a46e43069aadad3603358b63e262635a3716c948dab7b056750
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
195544eb25003c80879e6390c389f4b5
|
@ -0,0 +1 @@
|
||||
d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8
|
@ -0,0 +1 @@
|
||||
2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28
|
@ -0,0 +1 @@
|
||||
67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d
|
@ -0,0 +1,140 @@
|
||||
{
|
||||
"formatVersion": "1.1",
|
||||
"component": {
|
||||
"group": "com.xinbida.wukongim",
|
||||
"module": "WKIMLib_local",
|
||||
"version": "1.0.7",
|
||||
"attributes": {
|
||||
"org.gradle.status": "release"
|
||||
}
|
||||
},
|
||||
"createdBy": {
|
||||
"gradle": {
|
||||
"version": "8.9"
|
||||
}
|
||||
},
|
||||
"variants": [
|
||||
{
|
||||
"name": "releaseApiElements-published",
|
||||
"attributes": {
|
||||
"org.gradle.category": "library",
|
||||
"org.gradle.usage": "java-api",
|
||||
"org.jetbrains.kotlin.platform.type": "androidJvm"
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"name": "WKIMLib_local-1.0.7.aar",
|
||||
"url": "WKIMLib_local-1.0.7.aar",
|
||||
"size": 619369,
|
||||
"sha512": "67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d",
|
||||
"sha256": "2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28",
|
||||
"sha1": "d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8",
|
||||
"md5": "195544eb25003c80879e6390c389f4b5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "releaseRuntimeElements-published",
|
||||
"attributes": {
|
||||
"org.gradle.category": "library",
|
||||
"org.gradle.usage": "java-runtime",
|
||||
"org.jetbrains.kotlin.platform.type": "androidJvm"
|
||||
},
|
||||
"dependencies": [
|
||||
{
|
||||
"group": "org.jetbrains.kotlin",
|
||||
"module": "kotlin-bom",
|
||||
"version": {
|
||||
"requires": "1.9.22"
|
||||
},
|
||||
"attributes": {
|
||||
"org.gradle.category": "platform"
|
||||
},
|
||||
"endorseStrictVersions": true
|
||||
},
|
||||
{
|
||||
"group": "org.jetbrains.kotlin",
|
||||
"module": "kotlin-stdlib",
|
||||
"version": {
|
||||
"requires": "1.9.22"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "org.jetbrains.kotlin",
|
||||
"module": "kotlin-stdlib-jdk8",
|
||||
"version": {
|
||||
"requires": "1.9.22"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "com.android.support",
|
||||
"module": "multidex",
|
||||
"version": {
|
||||
"requires": "1.0.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "net.zetetic",
|
||||
"module": "android-database-sqlcipher",
|
||||
"version": {
|
||||
"requires": "4.5.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "androidx.sqlite",
|
||||
"module": "sqlite-ktx",
|
||||
"version": {
|
||||
"requires": "2.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "org.whispersystems",
|
||||
"module": "curve25519-android",
|
||||
"version": {
|
||||
"requires": "0.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "org.whispersystems",
|
||||
"module": "signal-protocol-android",
|
||||
"version": {
|
||||
"requires": "2.8.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"name": "WKIMLib_local-1.0.7.aar",
|
||||
"url": "WKIMLib_local-1.0.7.aar",
|
||||
"size": 619369,
|
||||
"sha512": "67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d",
|
||||
"sha256": "2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28",
|
||||
"sha1": "d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8",
|
||||
"md5": "195544eb25003c80879e6390c389f4b5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "releaseSourcesElements-published",
|
||||
"attributes": {
|
||||
"org.gradle.category": "documentation",
|
||||
"org.gradle.dependency.bundling": "external",
|
||||
"org.gradle.docstype": "sources",
|
||||
"org.gradle.libraryelements": "jar",
|
||||
"org.gradle.usage": "java-runtime",
|
||||
"org.jetbrains.kotlin.platform.type": "androidJvm"
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"name": "WKIMLib_local-1.0.7-sources.jar",
|
||||
"url": "WKIMLib_local-1.0.7-sources.jar",
|
||||
"size": 328517,
|
||||
"sha512": "d1a0aa32976653fa04696f6d42edbe8152b59fdaf0135350cdefa14d571212571e85412c8f026a46e43069aadad3603358b63e262635a3716c948dab7b056750",
|
||||
"sha256": "eb0a4ee07731a3ef38f606b2978d16169a6f6388e3f33fa3a10c82f3b0c17bb0",
|
||||
"sha1": "34a3a10b015c40e16a76fe75cb974d675a9cbda6",
|
||||
"md5": "2a8fa0fa06d069610eed09005b343994"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
18f875f433f4b46c3a8f82d2102b4efd
|
@ -0,0 +1 @@
|
||||
5e3e6e61712a9bee911ec2191da0faef05f93430
|
@ -0,0 +1 @@
|
||||
b5c54156b3a952972c75cfed1f5dd52419426a975df9a863efa8eb36f1feae12
|
@ -0,0 +1 @@
|
||||
0b3a1ba410c00e2566f0052b66121737164e4edc5defe64fd2aa7fd6e02ba05ccd9b2e0e13b603911d53a34ec9c10ee51fcd4c05719e19440c5da726484b45e3
|
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<!-- This module was also published with a richer model, Gradle metadata, -->
|
||||
<!-- which should be used instead. Do not delete the following line which -->
|
||||
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
|
||||
<!-- that they should prefer consuming it instead. -->
|
||||
<!-- do_not_remove: published-with-gradle-metadata -->
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.xinbida.wukongim</groupId>
|
||||
<artifactId>WKIMLib_local</artifactId>
|
||||
<version>1.0.7</version>
|
||||
<packaging>aar</packaging>
|
||||
<name>WKIMLib</name>
|
||||
<description>WuKong IM Android Library</description>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-bom</artifactId>
|
||||
<version>1.9.22</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>1.9.22</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>1.9.22</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.android.support</groupId>
|
||||
<artifactId>multidex</artifactId>
|
||||
<version>1.0.3</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.zetetic</groupId>
|
||||
<artifactId>android-database-sqlcipher</artifactId>
|
||||
<version>4.5.4</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>androidx.sqlite</groupId>
|
||||
<artifactId>sqlite-ktx</artifactId>
|
||||
<version>2.5.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.whispersystems</groupId>
|
||||
<artifactId>curve25519-android</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.whispersystems</groupId>
|
||||
<artifactId>signal-protocol-android</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1 @@
|
||||
ddc59ca3508344a630f0330f9c75801e
|
@ -0,0 +1 @@
|
||||
b351753e161fb1fde554f02adc966908b5d835b6
|
@ -0,0 +1 @@
|
||||
67367cbfa088cc2f2c57a66720e040865b70aa718b6f9487eeae174688b311f4
|
@ -0,0 +1 @@
|
||||
0ca8950dcf1398e81df06fb2c44015e10dac0d0ccc4d8db4e1e5eda302ee79c2727460d576f855c31bf8db5e669cdbb5d32897dce308009d069bc9e6d85126d9
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>com.xinbida.wukongim</groupId>
|
||||
<artifactId>WKIMLib_local</artifactId>
|
||||
<versioning>
|
||||
<latest>1.0.7</latest>
|
||||
<release>1.0.7</release>
|
||||
<versions>
|
||||
<version>1.0.7</version>
|
||||
</versions>
|
||||
<lastUpdated>20250524090841</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
@ -0,0 +1 @@
|
||||
2867162497a0539e3e3b0a1716f9feaa
|
@ -0,0 +1 @@
|
||||
e431ad1afdad3aff298476f1949e6dcb73bf52d2
|
@ -0,0 +1 @@
|
||||
aa7f10d602787f0faf2fc1a55c04fd089c55313b1d22c30cf1aa24c24ab9a216
|
@ -0,0 +1 @@
|
||||
3c7de45f238ee5d29b5c331b4c90ef396a87cc7a36550eeba7aa475e86f77da8d20e3373d177d46845e8e8d26c1ec106d2855ee1298c0763788a2583325b638f
|
@ -40,6 +40,8 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 5/21/21 11:25 AM
|
||||
@ -102,20 +104,42 @@ public class MessageHandler {
|
||||
|
||||
private volatile List<WKSyncMsg> receivedMsgList;
|
||||
private final Object receivedMsgListLock = new Object();
|
||||
private final Object cacheLock = new Object();
|
||||
private final ReentrantLock cacheLock = new ReentrantLock(true); // 使用公平锁
|
||||
private static final long LOCK_TIMEOUT = 2000; // 2秒超时
|
||||
private byte[] cacheData = null;
|
||||
private int available_len;
|
||||
|
||||
public void clearCacheData() {
|
||||
synchronized (cacheLock) {
|
||||
cacheData = null;
|
||||
available_len = 0;
|
||||
boolean locked = false;
|
||||
try {
|
||||
// 尝试获取锁,最多等待3秒
|
||||
locked = cacheLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
if (locked) {
|
||||
cacheData = null;
|
||||
available_len = 0;
|
||||
} else {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,clearCacheData失败");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "clearCacheData等待锁被中断: " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
if (locked) {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
synchronized void handlerOnlineBytes(INonBlockingConnection iNonBlockingConnection) {
|
||||
synchronized (cacheLock) {
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = cacheLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,handlerOnlineBytes失败");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取可用数据长度
|
||||
available_len = iNonBlockingConnection.available();
|
||||
@ -160,12 +184,26 @@ public class MessageHandler {
|
||||
WKLoggerUtils.getInstance().e(TAG, "onData 中发生意外错误: " + e.getMessage());
|
||||
clearCacheData();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "handlerOnlineBytes等待锁被中断: " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
if (locked) {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void cutBytes(byte[] available_bytes,
|
||||
IReceivedMsgListener mIReceivedMsgListener) {
|
||||
synchronized (cacheLock) {
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = cacheLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,cutBytes失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cacheData == null || cacheData.length == 0) cacheData = available_bytes;
|
||||
else {
|
||||
//如果上次还存在未解析完的消息将新数据追加到缓存数据中
|
||||
@ -265,6 +303,13 @@ public class MessageHandler {
|
||||
}
|
||||
}
|
||||
saveReceiveMsg();
|
||||
} catch (InterruptedException e) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "cutBytes等待锁被中断: " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
if (locked) {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 5/21/21 10:51 AM
|
||||
@ -146,9 +147,9 @@ public class WKConnection {
|
||||
}
|
||||
};
|
||||
|
||||
// 添加一个专门用于同步connection访问的锁对象
|
||||
public final Object connectionLock = new Object();
|
||||
|
||||
// 替换原有的 Object 锁
|
||||
public final ReentrantLock connectionLock = new ReentrantLock(true); // 使用公平锁
|
||||
private static final long LOCK_TIMEOUT = 3000; // 3秒超时
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private static final long CONNECTION_CLOSE_TIMEOUT = 5000; // 5 seconds timeout
|
||||
|
||||
@ -548,15 +549,20 @@ public class WKConnection {
|
||||
|
||||
//处理登录消息状态
|
||||
private void handleLoginStatus(short status) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "Connection state transition: " + connectStatus + " -> " + status);
|
||||
|
||||
synchronized (connectionLock) {
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = tryLockWithTimeout();
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,handleLoginStatus失败");
|
||||
return;
|
||||
}
|
||||
|
||||
WKLoggerUtils.getInstance().e(TAG, "Connection state transition: " + connectStatus + " -> " + status);
|
||||
String reason = WKConnectReason.ConnectSuccess;
|
||||
if (status == WKConnectStatus.kicked) {
|
||||
reason = WKConnectReason.ReasonAuthFail;
|
||||
}
|
||||
|
||||
// Validate state transition
|
||||
if (!isValidStateTransition(connectStatus, status)) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "Invalid state transition attempted: " + connectStatus + " -> " + status);
|
||||
return;
|
||||
@ -566,22 +572,22 @@ public class WKConnection {
|
||||
WKIM.getInstance().getConnectionManager().setConnectionStatus(status, reason);
|
||||
|
||||
if (status == WKConnectStatus.success) {
|
||||
// Reset reconnection counters since we have a successful connection
|
||||
connCount = 0;
|
||||
isReConnecting = false;
|
||||
|
||||
// Set to syncing state
|
||||
connectStatus = WKConnectStatus.syncMsg;
|
||||
WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.syncMsg, WKConnectReason.SyncMsg);
|
||||
|
||||
// Start timers and heartbeat before syncing
|
||||
startAll();
|
||||
|
||||
// Handle message syncing based on mode
|
||||
if (WKIMApplication.getInstance().getSyncMsgMode() == WKSyncMsgMode.WRITE) {
|
||||
WKIM.getInstance().getMsgManager().setSyncOfflineMsg((isEnd, list) -> {
|
||||
if (isEnd) {
|
||||
synchronized (connectionLock) {
|
||||
boolean innerLocked = false;
|
||||
try {
|
||||
innerLocked = tryLockWithTimeout();
|
||||
if (!innerLocked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,setSyncOfflineMsg回调处理失败");
|
||||
return;
|
||||
}
|
||||
if (connection != null && !isClosing.get()) {
|
||||
connectStatus = WKConnectStatus.success;
|
||||
MessageHandler.getInstance().saveReceiveMsg();
|
||||
@ -590,12 +596,22 @@ public class WKConnection {
|
||||
resendMsg();
|
||||
WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.success, WKConnectReason.ConnectSuccess);
|
||||
}
|
||||
} finally {
|
||||
if (innerLocked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
WKIM.getInstance().getConversationManager().setSyncConversationListener(syncChat -> {
|
||||
synchronized (connectionLock) {
|
||||
boolean innerLocked = false;
|
||||
try {
|
||||
innerLocked = tryLockWithTimeout();
|
||||
if (!innerLocked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,setSyncConversationListener回调处理失败");
|
||||
return;
|
||||
}
|
||||
if (connection != null && !isClosing.get()) {
|
||||
connectStatus = WKConnectStatus.success;
|
||||
WKIMApplication.getInstance().isCanConnect = true;
|
||||
@ -603,6 +619,10 @@ public class WKConnection {
|
||||
resendMsg();
|
||||
WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.success, WKConnectReason.ConnectSuccess);
|
||||
}
|
||||
} finally {
|
||||
if (innerLocked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -612,13 +632,16 @@ public class WKConnection {
|
||||
WKIMApplication.getInstance().isCanConnect = false;
|
||||
stopAll();
|
||||
} else {
|
||||
// Only attempt reconnection if we're allowed to connect
|
||||
if (WKIMApplication.getInstance().isCanConnect) {
|
||||
reconnection();
|
||||
}
|
||||
WKLoggerUtils.getInstance().e(TAG, "Login status: " + status);
|
||||
stopAll();
|
||||
}
|
||||
} finally {
|
||||
if (locked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,36 +678,46 @@ public class WKConnection {
|
||||
|
||||
public void sendMessage(WKBaseMsg mBaseMsg) {
|
||||
if (mBaseMsg == null) {
|
||||
WKLoggerUtils.getInstance().w(TAG ,"sendMessage called with null mBaseMsg.");
|
||||
WKLoggerUtils.getInstance().w(TAG, "sendMessage called with null mBaseMsg.");
|
||||
return;
|
||||
}
|
||||
if (mBaseMsg.packetType != WKMsgType.CONNECT) {
|
||||
if (connectStatus == WKConnectStatus.syncMsg) {
|
||||
WKLoggerUtils.getInstance().i(TAG ," sendMessage: In syncMsg status, message not sent: " + mBaseMsg.packetType);
|
||||
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = tryLockWithTimeout();
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,sendMessage失败");
|
||||
return;
|
||||
}
|
||||
if (connectStatus != WKConnectStatus.success) {
|
||||
WKLoggerUtils.getInstance().w(TAG , " sendMessage: Not in success status (is " + connectStatus + "), attempting reconnection for: " + mBaseMsg.packetType);
|
||||
|
||||
if (mBaseMsg.packetType != WKMsgType.CONNECT) {
|
||||
if (connectStatus == WKConnectStatus.syncMsg) {
|
||||
WKLoggerUtils.getInstance().i(TAG, " sendMessage: In syncMsg status, message not sent: " + mBaseMsg.packetType);
|
||||
return;
|
||||
}
|
||||
if (connectStatus != WKConnectStatus.success) {
|
||||
WKLoggerUtils.getInstance().w(TAG, " sendMessage: Not in success status (is " + connectStatus + "), attempting reconnection for: " + mBaseMsg.packetType);
|
||||
reconnection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
INonBlockingConnection currentConnection = this.connection;
|
||||
if (currentConnection == null || !currentConnection.isOpen()) {
|
||||
WKLoggerUtils.getInstance().w(TAG, " sendMessage: Connection is null or not open, attempting reconnection for: " + mBaseMsg.packetType);
|
||||
reconnection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
INonBlockingConnection currentConnection;
|
||||
synchronized (connectionLock) {
|
||||
currentConnection = this.connection;
|
||||
}
|
||||
|
||||
if (currentConnection == null || !currentConnection.isOpen()) {
|
||||
WKLoggerUtils.getInstance().w(TAG ," sendMessage: Connection is null or not open, attempting reconnection for: " + mBaseMsg.packetType);
|
||||
reconnection();
|
||||
return;
|
||||
}
|
||||
// Pass the local reference to MessageHandler
|
||||
int status = MessageHandler.getInstance().sendMessage(currentConnection, mBaseMsg);
|
||||
if (status == 0) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "发消息失败 (status 0 from MessageHandler), attempting reconnection for: " + mBaseMsg.packetType);
|
||||
reconnection();
|
||||
int status = MessageHandler.getInstance().sendMessage(currentConnection, mBaseMsg);
|
||||
if (status == 0) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "发消息失败 (status 0 from MessageHandler), attempting reconnection for: " + mBaseMsg.packetType);
|
||||
reconnection();
|
||||
}
|
||||
} finally {
|
||||
if (locked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
private void removeSendingMsg() {
|
||||
@ -846,8 +879,18 @@ public class WKConnection {
|
||||
}
|
||||
|
||||
public boolean connectionIsNull() {
|
||||
synchronized (connectionLock) {
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = tryLockWithTimeout();
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,connectionIsNull检查失败");
|
||||
return true; // 保守起见,如果获取锁失败就认为连接为空
|
||||
}
|
||||
return connection == null || !connection.isOpen();
|
||||
} finally {
|
||||
if (locked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -883,7 +926,14 @@ public class WKConnection {
|
||||
}
|
||||
|
||||
public void stopAll() {
|
||||
synchronized (connectionLock) {
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = tryLockWithTimeout();
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,stopAll失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 先设置连接状态为失败
|
||||
WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.fail, "");
|
||||
// 清理连接相关资源
|
||||
@ -912,6 +962,7 @@ public class WKConnection {
|
||||
connAckTime = 0;
|
||||
lastMsgTime = 0;
|
||||
connCount = 0;
|
||||
|
||||
// 清空发送消息队列
|
||||
if (sendingMsgHashMap != null) {
|
||||
sendingMsgHashMap.clear();
|
||||
@ -923,28 +974,38 @@ public class WKConnection {
|
||||
shutdownExecutor();
|
||||
|
||||
System.gc();
|
||||
} finally {
|
||||
if (locked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeConnect() {
|
||||
final INonBlockingConnection connectionToCloseActual;
|
||||
|
||||
// 如果已经在关闭过程中,直接返回
|
||||
if (!isClosing.compareAndSet(false, true)) {
|
||||
WKLoggerUtils.getInstance().i(TAG , " Close operation already in progress");
|
||||
WKLoggerUtils.getInstance().i(TAG, " Close operation already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (connectionLock) {
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = tryLockWithTimeout();
|
||||
if (!locked) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁超时,closeConnect失败");
|
||||
isClosing.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (connection == null) {
|
||||
isClosing.set(false);
|
||||
WKLoggerUtils.getInstance().i(TAG , " closeConnect called but connection is already null.");
|
||||
WKLoggerUtils.getInstance().i(TAG, " closeConnect called but connection is already null.");
|
||||
return;
|
||||
}
|
||||
connectionToCloseActual = connection;
|
||||
String connId = connectionToCloseActual.getId();
|
||||
|
||||
// Mark connection for closure to prevent reconnection attempts
|
||||
try {
|
||||
connectionToCloseActual.setAttachment("closing_" + System.currentTimeMillis() + "_" + connId);
|
||||
} catch (Exception e) {
|
||||
@ -953,7 +1014,11 @@ public class WKConnection {
|
||||
|
||||
connection = null;
|
||||
connectionClient = null;
|
||||
WKLoggerUtils.getInstance().i(TAG , " Connection object nulled, preparing for async close of: " + connId);
|
||||
WKLoggerUtils.getInstance().i(TAG, " Connection object nulled, preparing for async close of: " + connId);
|
||||
} finally {
|
||||
if (locked) {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a timeout handler to force close after timeout
|
||||
@ -961,7 +1026,7 @@ public class WKConnection {
|
||||
try {
|
||||
if (connectionToCloseActual.isOpen()) {
|
||||
String connId = connectionToCloseActual.getId();
|
||||
WKLoggerUtils.getInstance().w(TAG , " Connection close timeout reached for: " + connId);
|
||||
WKLoggerUtils.getInstance().w(TAG, " Connection close timeout reached for: " + connId);
|
||||
connectionToCloseActual.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -979,13 +1044,13 @@ public class WKConnection {
|
||||
try {
|
||||
if (connectionToCloseActual.isOpen()) {
|
||||
String connId = connectionToCloseActual.getId();
|
||||
WKLoggerUtils.getInstance().i(TAG , " Attempting to close connection: " + connId);
|
||||
WKLoggerUtils.getInstance().i(TAG, " Attempting to close connection: " + connId);
|
||||
connectionToCloseActual.close();
|
||||
// Remove the timeout handler since we closed successfully
|
||||
mainHandler.removeCallbacks(timeoutRunnable);
|
||||
WKLoggerUtils.getInstance().i(TAG , " Successfully closed connection: " + connId);
|
||||
WKLoggerUtils.getInstance().i(TAG, " Successfully closed connection: " + connId);
|
||||
} else {
|
||||
WKLoggerUtils.getInstance().i(TAG , " Connection was already closed or not open when async close executed: " + connectionToCloseActual.getId());
|
||||
WKLoggerUtils.getInstance().i(TAG, " Connection was already closed or not open when async close executed: " + connectionToCloseActual.getId());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "IOException during async connection close for " + connectionToCloseActual.getId() + ": " + e.getMessage());
|
||||
@ -1008,4 +1073,14 @@ public class WKConnection {
|
||||
closeThread.setDaemon(true);
|
||||
closeThread.start();
|
||||
}
|
||||
|
||||
private boolean tryLockWithTimeout() {
|
||||
try {
|
||||
return connectionLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
WKLoggerUtils.getInstance().e(TAG, "获取锁被中断: " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user