鸿蒙工具学习五十一:WebSocket错误码-1深度排查与解决方案
摘要:本文深入探讨了HarmonyOS WebSocket开发中错误码-1的解决方案。该错误通常由连接状态异常、网络环境问题或系统限制引起。文章提供了完整的排查流程,包括网络连通性测试、增强型WebSocket管理器实现、连接状态管理最佳实践等。通过详细的代码示例展示了如何构建健壮的WebSocket通信能力,包括自动重连策略、消息确认机制和网络质量自适应传输等高级技巧。同时针对HarmonyOS
引言:HarmonyOS WebSocket开发中的常见挑战
在HarmonyOS应用开发中,WebSocket作为实现实时双向通信的核心技术,广泛应用于即时通讯、实时数据推送、在线协作等场景。然而,开发者在实际使用过程中经常会遇到WebSocket发送消息失败,返回错误码-1的问题。这个看似简单的错误码背后,往往隐藏着复杂的网络环境和系统交互问题。
错误码-1通常对应"Websocket Unknown Other Error",表明发生了未明确分类的其他错误。本文将从问题现象、根本原因、解决方案、代码实践等多个维度,深入剖析HarmonyOS WebSocket错误码-1的排查与解决,帮助开发者构建稳定可靠的WebSocket通信能力。
一、问题现象与日志分析
1.1 典型错误表现
当WebSocket发送消息失败时,开发者通常会看到以下错误信息:
// 错误返回示例
sendMessage subscribeDevice got an error!
Err.code is: -1,
Err.message is: Websocket Unknown Other Error
1.2 系统日志深度解析
通过分析系统日志,我们可以发现更多细节信息:
09-26 14:46:56.819 9308 9456 I C015B0/***/NETSTACK: [websocket_exec.cpp:1003] OnClose 1011 The link is down
09-26 14:46:56.824 9308 9308 I C015B0/***/NETSTACK: [module_template.h:46] js invoke WebSocketSend
09-26 14:46:56.824 9308 9308 I C015B0/***/NETSTACK: [send_context.cpp:38] SendContext data is String
09-26 14:46:56.825 9308 9325 E C015B0/***/NETSTACK: [websocket_exec.cpp:809] user data is nullptr
09-26 14:46:56.825 9308 9308 D A0000F/***/Utils: *** sendMessage the subscribeDevice result is: undefined
关键日志分析:
-
OnClose 1011 The link is down:WebSocket连接已关闭,状态码1011表示连接异常中断 -
user data is nullptr:用户数据为空指针,表明WebSocket实例可能已失效 -
时间戳显示:连接关闭后立即尝试发送消息,导致发送失败
二、根本原因深度剖析
2.1 核心原因:连接状态异常
错误码-1的根本原因在于WebSocket连接处于不可用状态时尝试发送消息。具体可分为以下几种情况:
2.1.1 连接未建立或已断开
-
WebSocket未成功连接到服务器
-
连接建立后因网络问题断开
-
服务器主动关闭连接
-
心跳检测超时导致连接断开
2.1.2 连接状态管理不当
-
未正确监听连接状态变化
-
在
onclose事件后仍尝试发送消息 -
连接重连机制不完善
2.1.3 资源释放问题
-
WebSocket实例被提前释放
-
内存管理不当导致空指针
-
多线程环境下的资源竞争
2.2 网络环境因素
2.2.1 服务器可达性问题
-
服务器IP地址或端口不可达
-
防火墙或安全组策略限制
-
DNS解析失败
2.2.2 网络协议兼容性
-
WebSocket协议版本不匹配
-
SSL/TLS证书问题
-
HTTP代理配置错误
2.2.3 网络质量影响
-
网络延迟过高
-
数据包丢失严重
-
移动网络切换导致连接中断
2.3 HarmonyOS系统限制
2.3.1 权限配置问题
// module.json5权限配置
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "需要网络权限建立WebSocket连接"
}
]
}
}
2.3.2 后台运行限制
-
应用进入后台后网络连接可能被限制
-
省电模式下的网络策略
-
系统资源回收机制
三、完整解决方案与排查流程
3.1 系统化排查流程图
graph TD
A[WebSocket发送失败错误码-1] --> B{检查连接状态}
B -->|连接正常| C[检查消息数据格式]
B -->|连接异常| D[执行网络连通性测试]
D --> E[Ping服务器测试]
E -->|成功| F[端口连通性测试]
E -->|失败| G[检查网络配置]
F -->|成功| H[HTTP连通性测试]
F -->|失败| I[检查防火墙/安全组]
H -->|成功| J[WebSocket协议测试]
H -->|失败| K[检查服务器状态]
J -->|成功| L[检查客户端代码]
J -->|失败| M[检查WebSocket服务配置]
L --> N[实现健壮的连接管理]
N --> O[问题解决]
3.2 网络连通性测试套件
3.2.1 服务器可达性测试
import { network } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
class NetworkDiagnostic {
// Ping测试
async pingServer(host: string): Promise<PingResult> {
try {
const netHandle = network.createNetHandle();
const result = await netHandle.hasDefaultNet();
if (!result) {
return {
success: false,
message: '无可用网络连接',
latency: 0,
packetLoss: 100
};
}
// 模拟ping测试(实际需要系统级支持)
const testResult = await this.simulatePing(host);
return testResult;
} catch (error) {
return {
success: false,
message: `Ping测试失败: ${(error as BusinessError).message}`,
latency: 0,
packetLoss: 100
};
}
}
private async simulatePing(host: string): Promise<PingResult> {
// 实际开发中应使用系统提供的网络诊断工具
// 这里简化实现
return {
success: true,
message: 'Ping测试成功',
latency: 32, // 模拟延迟
packetLoss: 0
};
}
// 端口连通性测试
async testPort(host: string, port: number): Promise<PortTestResult> {
try {
// 使用Socket测试端口连通性
const socket = await this.createTestSocket(host, port);
const isConnected = await this.testSocketConnection(socket);
return {
success: isConnected,
host: host,
port: port,
message: isConnected ? '端口开放' : '端口关闭或不可达'
};
} catch (error) {
return {
success: false,
host: host,
port: port,
message: `端口测试异常: ${(error as BusinessError).message}`
};
}
}
// HTTP连通性测试
async testHttpConnection(url: string): Promise<HttpTestResult> {
try {
const http = await import('@kit.NetworkKit');
const httpRequest = http.createHttp();
const options: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000
};
const response = await httpRequest.request(url, options);
return {
success: response.responseCode === 200,
statusCode: response.responseCode,
message: `HTTP状态码: ${response.responseCode}`,
headers: response.header
};
} catch (error) {
return {
success: false,
statusCode: 0,
message: `HTTP测试失败: ${(error as BusinessError).message}`,
headers: {}
};
}
}
}
interface PingResult {
success: boolean;
message: string;
latency: number;
packetLoss: number;
}
interface PortTestResult {
success: boolean;
host: string;
port: number;
message: string;
}
interface HttpTestResult {
success: boolean;
statusCode: number;
message: string;
headers: Record<string, string>;
}
3.2.2 WebSocket协议测试
class WebSocketTester {
private ws: WebSocket | null = null;
private testResults: TestResult[] = [];
async runFullTest(wsUrl: string): Promise<FullTestReport> {
this.testResults = [];
// 1. 基础连接测试
const connectionTest = await this.testConnection(wsUrl);
this.testResults.push(connectionTest);
if (!connectionTest.success) {
return this.generateReport();
}
// 2. 消息发送测试
const sendTest = await this.testMessageSending();
this.testResults.push(sendTest);
// 3. 消息接收测试
const receiveTest = await this.testMessageReceiving();
this.testResults.push(receiveTest);
// 4. 连接稳定性测试
const stabilityTest = await this.testConnectionStability();
this.testResults.push(stabilityTest);
// 5. 清理资源
this.cleanup();
return this.generateReport();
}
private async testConnection(wsUrl: string): Promise<TestResult> {
return new Promise((resolve) => {
try {
this.ws = new WebSocket(wsUrl);
const timeout = setTimeout(() => {
resolve({
testName: '连接建立',
success: false,
message: '连接超时',
details: '在10秒内未建立连接'
});
}, 10000);
this.ws.onopen = () => {
clearTimeout(timeout);
resolve({
testName: '连接建立',
success: true,
message: 'WebSocket连接成功建立',
details: `连接到: ${wsUrl}`
});
};
this.ws.onerror = (error) => {
clearTimeout(timeout);
resolve({
testName: '连接建立',
success: false,
message: '连接错误',
details: `错误信息: ${error}`
});
};
} catch (error) {
resolve({
testName: '连接建立',
success: false,
message: '连接异常',
details: `异常信息: ${error}`
});
}
});
}
private generateReport(): FullTestReport {
const totalTests = this.testResults.length;
const passedTests = this.testResults.filter(r => r.success).length;
const successRate = totalTests > 0 ? (passedTests / totalTests) * 100 : 0;
return {
timestamp: new Date().toISOString(),
totalTests: totalTests,
passedTests: passedTests,
successRate: successRate,
results: this.testResults,
summary: this.generateSummary()
};
}
private generateSummary(): string {
const failedTests = this.testResults.filter(r => !r.success);
if (failedTests.length === 0) {
return '所有测试通过,WebSocket连接正常';
}
return `发现${failedTests.length}个问题:\n` +
failedTests.map(t => `- ${t.testName}: ${t.message}`).join('\n');
}
private cleanup(): void {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}
interface TestResult {
testName: string;
success: boolean;
message: string;
details: string;
}
interface FullTestReport {
timestamp: string;
totalTests: number;
passedTests: number;
successRate: number;
results: TestResult[];
summary: string;
}
3.3 客户端代码健壮性改进
3.3.1 增强型WebSocket管理器
import { BusinessError } from '@kit.BasicServicesKit';
type WebSocketState = 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED' | 'ERROR';
class RobustWebSocketManager {
private ws: WebSocket | null = null;
private state: WebSocketState = 'CLOSED';
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectDelay = 1000;
private messageQueue: Array<{
data: string | ArrayBuffer,
resolve: (value: boolean) => void,
reject: (error: Error) => void
}> = [];
private heartbeatInterval: number | null = null;
private lastHeartbeatTime: number = 0;
private heartbeatTimeout = 30000; // 30秒
constructor(
private url: string,
private protocols?: string | string[]
) {}
// 建立连接
async connect(): Promise<boolean> {
return new Promise((resolve, reject) => {
if (this.state === 'OPEN' || this.state === 'CONNECTING') {
resolve(true);
return;
}
try {
this.state = 'CONNECTING';
this.ws = new WebSocket(this.url, this.protocols);
// 设置超时
const connectTimeout = setTimeout(() => {
this.handleConnectionTimeout();
reject(new Error('连接超时'));
}, 10000);
this.ws.onopen = () => {
clearTimeout(connectTimeout);
this.state = 'OPEN';
this.reconnectAttempts = 0;
this.startHeartbeat();
this.processMessageQueue();
console.log('WebSocket连接成功');
resolve(true);
};
this.ws.onclose = (event: CloseEvent) => {
this.state = 'CLOSED';
this.stopHeartbeat();
console.log(`WebSocket连接关闭,代码: ${event.code}, 原因: ${event.reason}`);
// 如果不是正常关闭,尝试重连
if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
};
this.ws.onerror = (error: Event) => {
this.state = 'ERROR';
console.error('WebSocket错误:', error);
reject(new Error('连接错误'));
};
this.ws.onmessage = (event: MessageEvent) => {
this.lastHeartbeatTime = Date.now();
this.handleMessage(event.data);
};
} catch (error) {
this.state = 'ERROR';
console.error('创建WebSocket失败:', error);
reject(error);
}
});
}
// 发送消息(带状态检查)
async send(data: string | ArrayBuffer): Promise<boolean> {
return new Promise((resolve, reject) => {
// 检查连接状态
if (this.state !== 'OPEN' || !this.ws) {
// 将消息加入队列,等待重连后发送
this.messageQueue.push({ data, resolve, reject });
// 如果连接已断开,尝试重连
if (this.state === 'CLOSED' || this.state === 'ERROR') {
this.connect().catch(() => {
reject(new Error('连接不可用,且重连失败'));
});
} else {
reject(new Error('WebSocket连接未就绪'));
}
return;
}
// 检查WebSocket就绪状态
if (this.ws.readyState !== WebSocket.OPEN) {
reject(new Error(`WebSocket就绪状态异常: ${this.ws.readyState}`));
return;
}
try {
this.ws.send(data);
resolve(true);
} catch (error) {
console.error('发送消息失败:', error);
// 发送失败,将消息加入队列并尝试重连
this.messageQueue.push({ data, resolve, reject });
this.handleSendError(error as BusinessError);
reject(new Error(`发送失败: ${(error as BusinessError).message}`));
}
});
}
// 处理发送错误
private handleSendError(error: BusinessError): void {
console.error('WebSocket发送错误:', error);
// 根据错误码采取不同策略
switch (error.code) {
case -1: // Unknown Other Error
this.state = 'ERROR';
this.scheduleReconnect();
break;
case 1006: // Connection closed abnormally
this.state = 'CLOSED';
this.scheduleReconnect();
break;
default:
// 其他错误,记录日志
console.warn(`未处理的WebSocket错误码: ${error.code}`);
}
}
// 处理连接超时
private handleConnectionTimeout(): void {
console.error('WebSocket连接超时');
this.state = 'ERROR';
if (this.ws) {
this.ws.close();
this.ws = null;
}
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
}
// 安排重连
private scheduleReconnect(): void {
this.reconnectAttempts++;
if (this.reconnectAttempts > this.maxReconnectAttempts) {
console.error('已达到最大重连次数,停止重连');
return;
}
// 指数退避策略
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
console.log(`将在${delay}ms后尝试第${this.reconnectAttempts}次重连`);
setTimeout(() => {
this.connect().catch((error) => {
console.error(`第${this.reconnectAttempts}次重连失败:`, error);
});
}, delay);
}
// 启动心跳检测
private startHeartbeat(): void {
this.lastHeartbeatTime = Date.now();
this.heartbeatInterval = setInterval(() => {
const now = Date.now();
// 检查是否超过心跳超时时间
if (now - this.lastHeartbeatTime > this.heartbeatTimeout) {
console.error('心跳超时,连接可能已断开');
this.handleHeartbeatTimeout();
return;
}
// 发送心跳消息
if (this.state === 'OPEN' && this.ws) {
try {
this.ws.send(JSON.stringify({ type: 'heartbeat', timestamp: now }));
} catch (error) {
console.error('发送心跳失败:', error);
}
}
}, 10000) as unknown as number; // 每10秒发送一次心跳
}
// 停止心跳检测
private stopHeartbeat(): void {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
}
// 处理心跳超时
private handleHeartbeatTimeout(): void {
this.state = 'ERROR';
this.stopHeartbeat();
if (this.ws) {
this.ws.close();
this.ws = null;
}
// 尝试重连
this.scheduleReconnect();
}
// 处理接收到的消息
private handleMessage(data: any): void {
try {
const message = JSON.parse(data);
// 处理心跳响应
if (message.type === 'heartbeat_ack') {
this.lastHeartbeatTime = Date.now();
return;
}
// 触发消息事件
this.emit('message', message);
} catch (error) {
console.warn('解析消息失败:', error);
// 非JSON消息,直接传递
this.emit('raw_message', data);
}
}
// 处理消息队列
private processMessageQueue(): void {
while (this.messageQueue.length > 0 && this.state === 'OPEN' && this.ws) {
const item = this.messageQueue.shift();
if (item) {
try {
this.ws.send(item.data);
item.resolve(true);
} catch (error) {
console.error('处理队列消息失败:', error);
item.reject(new Error('发送队列消息失败'));
}
}
}
}
// 事件发射器
private listeners: Record<string, Array<Function>> = {};
on(event: string, callback: Function): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
}
private emit(event: string, ...args: any[]): void {
if (this.listeners[event]) {
this.listeners[event].forEach(callback => {
try {
callback(...args);
} catch (error) {
console.error(`事件${event}处理错误:`, error);
}
});
}
}
// 关闭连接
close(code?: number, reason?: string): void {
this.stopHeartbeat();
if (this.ws) {
this.ws.close(code || 1000, reason);
this.ws = null;
}
this.state = 'CLOSED';
// 清空消息队列并拒绝所有待处理的消息
this.messageQueue.forEach(item => {
item.reject(new Error('连接已关闭'));
});
this.messageQueue = [];
}
// 获取当前状态
getState(): WebSocketState {
return this.state;
}
// 获取重连次数
getReconnectAttempts(): number {
return this.reconnectAttempts;
}
}
3.3.2 WebSocket工厂与连接池
class WebSocketFactory {
private static instances: Map<string, RobustWebSocketManager> = new Map();
private static defaultConfig: WebSocketConfig = {
maxReconnectAttempts: 5,
reconnectDelay: 1000,
heartbeatInterval: 10000,
heartbeatTimeout: 30000
};
// 获取或创建WebSocket实例
static getInstance(
url: string,
protocols?: string | string[],
config?: Partial<WebSocketConfig>
): RobustWebSocketManager {
const key = this.generateKey(url, protocols);
if (!this.instances.has(key)) {
const mergedConfig = { ...this.defaultConfig, ...config };
const wsManager = new RobustWebSocketManager(url, protocols);
// 应用配置
Object.assign(wsManager, {
maxReconnectAttempts: mergedConfig.maxReconnectAttempts,
reconnectDelay: mergedConfig.reconnectDelay,
heartbeatTimeout: mergedConfig.heartbeatTimeout
});
this.instances.set(key, wsManager);
}
return this.instances.get(key)!;
}
// 关闭并移除实例
static closeInstance(url: string, protocols?: string | string[]): void {
const key = this.generateKey(url, protocols);
const instance = this.instances.get(key);
if (instance) {
instance.close();
this.instances.delete(key);
}
}
// 关闭所有实例
static closeAll(): void {
this.instances.forEach(instance => {
instance.close();
});
this.instances.clear();
}
// 生成实例键
private static generateKey(url: string, protocols?: string | string[]): string {
const protocolStr = Array.isArray(protocols)
? protocols.join(',')
: protocols || '';
return `${url}|${protocolStr}`;
}
// 获取所有实例状态
static getAllStatus(): InstanceStatus[] {
const statuses: InstanceStatus[] = [];
this.instances.forEach((instance, key) => {
const [url, protocols] = key.split('|');
statuses.push({
url,
protocols: protocols || 'default',
state: instance.getState(),
reconnectAttempts: instance.getReconnectAttempts()
});
});
return statuses;
}
}
interface WebSocketConfig {
maxReconnectAttempts: number;
reconnectDelay: number;
heartbeatInterval: number;
heartbeatTimeout: number;
}
interface InstanceStatus {
url: string;
protocols: string;
state: WebSocketState;
reconnectAttempts: number;
}
// 使用示例
@Component
struct WebSocketDemo {
private wsManager: RobustWebSocketManager | null = null;
@State connectionStatus: string = '未连接';
@State messages: string[] = [];
@State isConnected: boolean = false;
aboutToAppear(): void {
// 获取WebSocket实例
this.wsManager = WebSocketFactory.getInstance(
'wss://example.com/ws',
'protocol-v1',
{
maxReconnectAttempts: 3,
reconnectDelay: 2000
}
);
// 监听事件
this.wsManager.on('message', (message: any) => {
this.messages.push(`收到消息: ${JSON.stringify(message)}`);
});
this.wsManager.on('raw_message', (data: any) => {
this.messages.push(`收到原始消息: ${data}`);
});
}
// 连接WebSocket
async connectWebSocket(): Promise<void> {
if (!this.wsManager) return;
try {
this.connectionStatus = '连接中...';
const success = await this.wsManager.connect();
if (success) {
this.connectionStatus = '已连接';
this.isConnected = true;
} else {
this.connectionStatus = '连接失败';
}
} catch (error) {
this.connectionStatus = `连接错误: ${(error as Error).message}`;
console.error('连接失败:', error);
}
}
// 发送消息
async sendMessage(): Promise<void> {
if (!this.wsManager || !this.isConnected) {
console.error('WebSocket未连接');
return;
}
const message = {
type: 'chat',
content: 'Hello WebSocket!',
timestamp: Date.now()
};
try {
const success = await this.wsManager.send(JSON.stringify(message));
if (success) {
this.messages.push('消息发送成功');
} else {
this.messages.push('消息发送失败');
}
} catch (error) {
this.messages.push(`发送错误: ${(error as Error).message}`);
console.error('发送失败:', error);
}
}
// 断开连接
disconnect(): void {
if (this.wsManager) {
this.wsManager.close();
this.connectionStatus = '已断开';
this.isConnected = false;
}
}
aboutToDisappear(): void {
// 清理资源
this.disconnect();
WebSocketFactory.closeInstance('wss://example.com/ws', 'protocol-v1');
}
build() {
Column({ space: 20 }) {
// 连接状态显示
Text(`连接状态: ${this.connectionStatus}`)
.fontSize(18)
.fontColor(this.isConnected ? Color.Green : Color.Red)
// 控制按钮
Row({ space: 10 }) {
Button('连接')
.width(100)
.height(40)
.enabled(!this.isConnected)
.onClick(() => this.connectWebSocket())
Button('发送')
.width(100)
.height(40)
.enabled(this.isConnected)
.onClick(() => this.sendMessage())
Button('断开')
.width(100)
.height(40)
.enabled(this.isConnected)
.onClick(() => this.disconnect())
}
// 消息列表
List({ space: 10 }) {
ForEach(this.messages, (message: string, index: number) => {
ListItem() {
Text(message)
.fontSize(14)
.padding(10)
.backgroundColor(Color.Gray.copy(0.1))
.borderRadius(8)
}
}, (message: string) => message)
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.padding(20)
}
}
四、最佳实践与高级技巧
4.1 连接状态管理最佳实践
4.1.1 状态机设计模式
class WebSocketStateMachine {
private currentState: WebSocketState = 'CLOSED';
private transitions: Map<WebSocketState, WebSocketState[]> = new Map();
constructor() {
// 定义状态转移规则
this.transitions.set('CLOSED', ['CONNECTING']);
this.transitions.set('CONNECTING', ['OPEN', 'ERROR', 'CLOSED']);
this.transitions.set('OPEN', ['CLOSING', 'ERROR', 'CLOSED']);
this.transitions.set('CLOSING', ['CLOSED', 'ERROR']);
this.transitions.set('ERROR', ['CONNECTING', 'CLOSED']);
}
// 状态转移
transitionTo(newState: WebSocketState): boolean {
const allowedTransitions = this.transitions.get(this.currentState);
if (allowedTransitions && allowedTransitions.includes(newState)) {
console.log(`状态转移: ${this.currentState} -> ${newState}`);
this.currentState = newState;
return true;
}
console.error(`非法状态转移: ${this.currentState} -> ${newState}`);
return false;
}
// 获取当前状态
getCurrentState(): WebSocketState {
return this.currentState;
}
// 检查是否允许发送消息
canSendMessage(): boolean {
return this.currentState === 'OPEN';
}
// 检查是否允许重连
canReconnect(): boolean {
return this.currentState === 'CLOSED' || this.currentState === 'ERROR';
}
}
4.1.2 自动重连策略
class AutoReconnectStrategy {
private attempts = 0;
private maxAttempts: number;
private baseDelay: number;
private maxDelay: number;
private jitter: boolean;
constructor(config: ReconnectConfig) {
this.maxAttempts = config.maxAttempts;
this.baseDelay = config.baseDelay;
this.maxDelay = config.maxDelay;
this.jitter = config.jitter || false;
}
// 计算下一次重连延迟
getNextDelay(): number {
if (this.attempts >= this.maxAttempts) {
return -1; // 停止重连
}
this.attempts++;
// 指数退避
let delay = this.baseDelay * Math.pow(2, this.attempts - 1);
// 限制最大延迟
delay = Math.min(delay, this.maxDelay);
// 添加随机抖动避免惊群效应
if (this.jitter) {
const jitterAmount = delay * 0.1; // 10%的抖动
delay += Math.random() * jitterAmount * 2 - jitterAmount;
}
return Math.max(0, delay);
}
// 重置重连计数器
reset(): void {
this.attempts = 0;
}
// 获取当前重连次数
getAttempts(): number {
return this.attempts;
}
// 检查是否应该继续重连
shouldContinue(): boolean {
return this.attempts < this.maxAttempts;
}
}
interface ReconnectConfig {
maxAttempts: number;
baseDelay: number;
maxDelay: number;
jitter?: boolean;
}
4.2 消息处理与序列化
4.2.1 消息协议设计
interface WebSocketMessage {
id: string; // 消息ID
type: MessageType; // 消息类型
payload: any; // 消息负载
timestamp: number; // 时间戳
sequence?: number; // 序列号(可选)
}
type MessageType =
| 'heartbeat' // 心跳
| 'heartbeat_ack' // 心跳确认
| 'chat' // 聊天消息
| 'notification' // 通知
| 'command' // 命令
| 'error' // 错误
| 'ack' // 确认
| 'request' // 请求
| 'response'; // 响应
class MessageSerializer {
// 序列化消息
static serialize(message: WebSocketMessage): string {
return JSON.stringify({
...message,
// 添加版本信息
version: '1.0',
// 添加校验和
checksum: this.calculateChecksum(message)
});
}
// 反序列化消息
static deserialize(data: string): WebSocketMessage {
const parsed = JSON.parse(data);
// 验证校验和
const expectedChecksum = parsed.checksum;
delete parsed.checksum;
const actualChecksum = this.calculateChecksum(parsed);
if (expectedChecksum !== actualChecksum) {
throw new Error('消息校验失败');
}
return parsed;
}
// 计算校验和
private static calculateChecksum(obj: any): string {
const str = JSON.stringify(obj);
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // 转换为32位整数
}
return hash.toString(16);
}
// 创建心跳消息
static createHeartbeat(): string {
const message: WebSocketMessage = {
id: this.generateId(),
type: 'heartbeat',
payload: { timestamp: Date.now() },
timestamp: Date.now()
};
return this.serialize(message);
}
// 生成消息ID
private static generateId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
4.2.2 消息确认与重发机制
class MessageAcknowledgment {
private pendingMessages: Map<string, PendingMessage> = new Map();
private ackTimeout = 30000; // 30秒超时
private maxRetries = 3;
// 发送消息并等待确认
async sendWithAck(
ws: RobustWebSocketManager,
message: WebSocketMessage
): Promise<boolean> {
return new Promise((resolve, reject) => {
const messageId = message.id;
// 设置超时定时器
const timeoutId = setTimeout(() => {
this.handleTimeout(messageId);
reject(new Error(`消息确认超时: ${messageId}`));
}, this.ackTimeout);
// 保存待确认消息
this.pendingMessages.set(messageId, {
message,
retries: 0,
timeoutId,
resolve,
reject,
sentAt: Date.now()
});
// 发送消息
this.sendMessage(ws, message).catch(error => {
clearTimeout(timeoutId);
this.pendingMessages.delete(messageId);
reject(error);
});
});
}
// 处理确认消息
handleAck(messageId: string): void {
const pending = this.pendingMessages.get(messageId);
if (pending) {
clearTimeout(pending.timeoutId);
pending.resolve(true);
this.pendingMessages.delete(messageId);
}
}
// 处理超时
private handleTimeout(messageId: string): void {
const pending = this.pendingMessages.get(messageId);
if (!pending) return;
if (pending.retries < this.maxRetries) {
// 重试
pending.retries++;
console.log(`重试消息 ${messageId}, 第${pending.retries}次`);
// 重新设置超时
pending.timeoutId = setTimeout(() => {
this.handleTimeout(messageId);
}, this.ackTimeout);
// 重新发送
// 注意:实际实现中需要重新获取WebSocket实例
} else {
// 达到最大重试次数,失败
pending.reject(new Error(`消息发送失败,已达到最大重试次数: ${messageId}`));
this.pendingMessages.delete(messageId);
}
}
private async sendMessage(
ws: RobustWebSocketManager,
message: WebSocketMessage
): Promise<void> {
const serialized = MessageSerializer.serialize(message);
await ws.send(serialized);
}
}
interface PendingMessage {
message: WebSocketMessage;
retries: number;
timeoutId: number;
resolve: (value: boolean) => void;
reject: (error: Error) => void;
sentAt: number;
}
4.3 性能监控与优化
4.3.1 WebSocket性能监控器
class WebSocketPerformanceMonitor {
private metrics: PerformanceMetrics = {
connectionTime: 0,
messagesSent: 0,
messagesReceived: 0,
bytesSent: 0,
bytesReceived: 0,
errors: 0,
reconnects: 0,
latencySamples: []
};
private startTime: number = 0;
private lastMessageTime: number = 0;
// 开始监控
startMonitoring(): void {
this.startTime = Date.now();
this.metrics = {
connectionTime: 0,
messagesSent: 0,
messagesReceived: 0,
bytesSent: 0,
bytesReceived: 0,
errors: 0,
reconnects: 0,
latencySamples: []
};
}
// 记录连接建立
recordConnection(): void {
this.metrics.connectionTime = Date.now() - this.startTime;
}
// 记录消息发送
recordMessageSent(size: number): void {
this.metrics.messagesSent++;
this.metrics.bytesSent += size;
this.lastMessageTime = Date.now();
}
// 记录消息接收
recordMessageReceived(size: number): void {
this.metrics.messagesReceived++;
this.metrics.bytesReceived += size;
// 计算延迟
if (this.lastMessageTime > 0) {
const latency = Date.now() - this.lastMessageTime;
this.metrics.latencySamples.push(latency);
// 保持最近100个样本
if (this.metrics.latencySamples.length > 100) {
this.metrics.latencySamples.shift();
}
}
}
// 记录错误
recordError(): void {
this.metrics.errors++;
}
// 记录重连
recordReconnect(): void {
this.metrics.reconnects++;
}
// 获取性能报告
getPerformanceReport(): PerformanceReport {
const now = Date.now();
const uptime = now - this.startTime;
// 计算平均延迟
const avgLatency = this.metrics.latencySamples.length > 0
? this.metrics.latencySamples.reduce((a, b) => a + b, 0) / this.metrics.latencySamples.length
: 0;
// 计算消息速率
const messagesPerMinute = uptime > 0
? (this.metrics.messagesSent + this.metrics.messagesReceived) / (uptime / 60000)
: 0;
// 计算数据速率
const bytesPerSecond = uptime > 0
? (this.metrics.bytesSent + this.metrics.bytesReceived) / (uptime / 1000)
: 0;
// 计算错误率
const errorRate = this.metrics.messagesSent > 0
? (this.metrics.errors / this.metrics.messagesSent) * 100
: 0;
return {
uptime,
connectionTime: this.metrics.connectionTime,
messagesSent: this.metrics.messagesSent,
messagesReceived: this.metrics.messagesReceived,
bytesSent: this.metrics.bytesSent,
bytesReceived: this.metrics.bytesReceived,
errors: this.metrics.errors,
reconnects: this.metrics.reconnects,
avgLatency,
messagesPerMinute,
bytesPerSecond,
errorRate: `${errorRate.toFixed(2)}%`,
healthScore: this.calculateHealthScore()
};
}
// 计算健康分数
private calculateHealthScore(): number {
let score = 100;
// 错误扣分
score -= Math.min(this.metrics.errors * 5, 50);
// 重连扣分
score -= Math.min(this.metrics.reconnects * 10, 30);
// 高延迟扣分
const highLatencyCount = this.metrics.latencySamples.filter(l => l > 1000).length;
score -= Math.min(highLatencyCount * 2, 20);
return Math.max(0, score);
}
}
interface PerformanceMetrics {
connectionTime: number;
messagesSent: number;
messagesReceived: number;
bytesSent: number;
bytesReceived: number;
errors: number;
reconnects: number;
latencySamples: number[];
}
interface PerformanceReport {
uptime: number;
connectionTime: number;
messagesSent: number;
messagesReceived: number;
bytesSent: number;
bytesReceived: number;
errors: number;
reconnects: number;
avgLatency: number;
messagesPerMinute: number;
bytesPerSecond: number;
errorRate: string;
healthScore: number;
}
4.3.2 自适应网络质量检测
class NetworkQualityDetector {
private samples: NetworkSample[] = [];
private maxSamples = 50;
private currentQuality: NetworkQuality = 'UNKNOWN';
private qualityChangeCallbacks: Array<(quality: NetworkQuality) => void> = [];
// 添加网络样本
addSample(sample: NetworkSample): void {
this.samples.push(sample);
// 保持样本数量
if (this.samples.length > this.maxSamples) {
this.samples.shift();
}
// 更新网络质量评估
const previousQuality = this.currentQuality;
this.updateQuality();
// 质量变化时触发回调
if (previousQuality !== this.currentQuality) {
this.triggerQualityChange(this.currentQuality);
}
}
// 更新网络质量评估
private updateQuality(): void {
if (this.samples.length < 5) {
this.currentQuality = 'UNKNOWN';
return;
}
// 获取最近10个样本(如果样本数足够)
const recentSamples = this.samples.slice(-10);
// 计算平均延迟
const avgLatency = recentSamples.reduce((sum, s) => sum + s.latency, 0) / recentSamples.length;
// 计算丢包率
const lostPackets = recentSamples.filter(s => s.packetLoss > 0).length;
const packetLossRate = (lostPackets / recentSamples.length) * 100;
// 计算抖动
const jitter = this.calculateJitter(recentSamples);
// 计算带宽(基于样本中的带宽估算)
const avgBandwidth = recentSamples.reduce((sum, s) => sum + (s.bandwidth || 0), 0) / recentSamples.length;
// 评估网络质量
this.currentQuality = this.evaluateQuality(avgLatency, packetLossRate, jitter, avgBandwidth);
}
// 计算抖动(使用平均绝对差值)
private calculateJitter(samples: NetworkSample[]): number {
if (samples.length < 2) return 0;
let totalJitter = 0;
let count = 0;
for (let i = 1; i < samples.length; i++) {
const latencyDiff = Math.abs(samples[i].latency - samples[i-1].latency);
totalJitter += latencyDiff;
count++;
}
return count > 0 ? totalJitter / count : 0;
}
// 评估网络质量
private evaluateQuality(
latency: number,
packetLoss: number,
jitter: number,
bandwidth: number
): NetworkQuality {
// 质量评分系统
let score = 100;
// 延迟扣分
if (latency > 500) score -= 40;
else if (latency > 200) score -= 20;
else if (latency > 100) score -= 10;
else if (latency > 50) score -= 5;
// 丢包扣分
if (packetLoss > 20) score -= 40;
else if (packetLoss > 10) score -= 20;
else if (packetLoss > 5) score -= 10;
else if (packetLoss > 1) score -= 5;
// 抖动扣分
if (jitter > 100) score -= 20;
else if (jitter > 50) score -= 10;
else if (jitter > 30) score -= 5;
// 带宽加分
if (bandwidth > 10000) score += 10; // 10Mbps以上
else if (bandwidth > 5000) score += 5; // 5-10Mbps
// 根据评分确定质量等级
if (score >= 90) return 'EXCELLENT';
else if (score >= 75) return 'GOOD';
else if (score >= 60) return 'FAIR';
else if (score >= 40) return 'POOR';
else return 'BAD';
}
// 获取当前网络质量
getCurrentQuality(): NetworkQuality {
return this.currentQuality;
}
// 获取详细质量报告
getQualityReport(): NetworkQualityReport {
if (this.samples.length === 0) {
return {
quality: 'UNKNOWN',
latency: 0,
packetLoss: 0,
jitter: 0,
bandwidth: 0,
sampleCount: 0,
recommendation: '无足够样本数据'
};
}
const recentSamples = this.samples.slice(-10);
const avgLatency = recentSamples.reduce((sum, s) => sum + s.latency, 0) / recentSamples.length;
const lostPackets = recentSamples.filter(s => s.packetLoss > 0).length;
const packetLossRate = (lostPackets / recentSamples.length) * 100;
const jitter = this.calculateJitter(recentSamples);
const avgBandwidth = recentSamples.reduce((sum, s) => sum + (s.bandwidth || 0), 0) / recentSamples.length;
return {
quality: this.currentQuality,
latency: avgLatency,
packetLoss: packetLossRate,
jitter: jitter,
bandwidth: avgBandwidth,
sampleCount: this.samples.length,
recommendation: this.getRecommendation()
};
}
// 获取网络优化建议
private getRecommendation(): string {
switch (this.currentQuality) {
case 'EXCELLENT':
return '网络质量优秀,可维持当前配置';
case 'GOOD':
return '网络质量良好,可优化大文件传输策略';
case 'FAIR':
return '网络质量一般,建议启用压缩和优化重传策略';
case 'POOR':
return '网络质量较差,建议降低数据频率,启用强压缩';
case 'BAD':
return '网络质量差,建议切换到备用网络或使用离线模式';
default:
return '网络质量未知,建议继续收集数据';
}
}
// 注册质量变化回调
onQualityChange(callback: (quality: NetworkQuality) => void): void {
this.qualityChangeCallbacks.push(callback);
}
// 触发质量变化回调
private triggerQualityChange(quality: NetworkQuality): void {
this.qualityChangeCallbacks.forEach(callback => {
try {
callback(quality);
} catch (error) {
console.error('质量变化回调执行失败:', error);
}
});
}
// 清除所有样本数据
clearSamples(): void {
this.samples = [];
this.currentQuality = 'UNKNOWN';
}
// 获取网络质量对应的传输策略
getTransmissionStrategy(): TransmissionStrategy {
switch (this.currentQuality) {
case 'EXCELLENT':
return {
compression: 'none',
chunkSize: 65536, // 64KB
retryCount: 1,
timeout: 10000,
heartbeatInterval: 30000
};
case 'GOOD':
return {
compression: 'gzip',
chunkSize: 32768, // 32KB
retryCount: 2,
timeout: 15000,
heartbeatInterval: 20000
};
case 'FAIR':
return {
compression: 'gzip',
chunkSize: 16384, // 16KB
retryCount: 3,
timeout: 20000,
heartbeatInterval: 15000
};
case 'POOR':
return {
compression: 'high',
chunkSize: 8192, // 8KB
retryCount: 5,
timeout: 30000,
heartbeatInterval: 10000
};
case 'BAD':
return {
compression: 'maximum',
chunkSize: 4096, // 4KB
retryCount: 0, // 不重试,立即失败
timeout: 5000,
heartbeatInterval: 5000
};
default:
return {
compression: 'gzip',
chunkSize: 16384,
retryCount: 2,
timeout: 15000,
heartbeatInterval: 20000
};
}
}
}
interface NetworkSample {
latency: number; // 延迟(毫秒)
packetLoss: number; // 丢包率(0-100)
bandwidth?: number; // 带宽(Kbps)
timestamp: number; // 时间戳
}
type NetworkQuality = 'UNKNOWN' | 'EXCELLENT' | 'GOOD' | 'FAIR' | 'POOR' | 'BAD';
interface NetworkQualityReport {
quality: NetworkQuality;
latency: number;
packetLoss: number;
jitter: number;
bandwidth: number;
sampleCount: number;
recommendation: string;
}
interface TransmissionStrategy {
compression: 'none' | 'gzip' | 'high' | 'maximum';
chunkSize: number;
retryCount: number;
timeout: number;
heartbeatInterval: number;
}
4.4 集成网络质量检测的WebSocket管理器
class AdaptiveWebSocketManager extends RobustWebSocketManager {
private qualityDetector: NetworkQualityDetector;
private currentStrategy: TransmissionStrategy;
private messageBuffer: Array<{data: any, callback: (success: boolean) => void}> = [];
private isProcessingBuffer = false;
constructor(url: string, protocols?: string | string[]) {
super(url, protocols);
this.qualityDetector = new NetworkQualityDetector();
this.currentStrategy = this.qualityDetector.getTransmissionStrategy();
// 监听网络质量变化
this.qualityDetector.onQualityChange((quality) => {
console.log(`网络质量变化: ${quality}`);
this.onNetworkQualityChange(quality);
});
}
// 发送消息(自适应版本)
async sendAdaptive(data: any): Promise<boolean> {
return new Promise((resolve) => {
// 根据当前网络质量调整发送策略
if (this.qualityDetector.getCurrentQuality() === 'BAD') {
// 网络质量差,缓冲消息
this.bufferMessage(data, resolve);
return;
}
// 网络质量可接受,直接发送
this.sendWithStrategy(data, resolve);
});
}
// 缓冲消息
private bufferMessage(data: any, callback: (success: boolean) => void): void {
this.messageBuffer.push({ data, callback });
// 如果缓冲区未在处理,则启动处理
if (!this.isProcessingBuffer) {
this.processBuffer();
}
}
// 处理缓冲消息
private async processBuffer(): Promise<void> {
if (this.isProcessingBuffer || this.messageBuffer.length === 0) {
return;
}
this.isProcessingBuffer = true;
while (this.messageBuffer.length > 0) {
const quality = this.qualityDetector.getCurrentQuality();
// 只有网络质量不是BAD时才发送缓冲消息
if (quality === 'BAD') {
console.log('网络质量差,暂停发送缓冲消息');
break;
}
const item = this.messageBuffer.shift();
if (item) {
try {
const success = await this.sendWithStrategy(item.data, item.callback);
if (!success) {
// 发送失败,重新放回缓冲区开头
this.messageBuffer.unshift(item);
await this.delay(1000); // 等待1秒后重试
}
} catch (error) {
console.error('处理缓冲消息失败:', error);
}
}
// 控制发送速率,避免网络拥塞
await this.delay(this.getBufferProcessingDelay());
}
this.isProcessingBuffer = false;
}
// 根据策略发送消息
private async sendWithStrategy(data: any, callback?: (success: boolean) => void): Promise<boolean> {
const strategy = this.currentStrategy;
const compressedData = this.compressData(data, strategy.compression);
// 分块发送(如果数据较大)
if (compressedData.length > strategy.chunkSize) {
return this.sendChunked(compressedData, strategy.chunkSize, callback);
}
// 普通发送
try {
const success = await this.sendWithRetry(compressedData, strategy.retryCount, strategy.timeout);
callback?.(success);
return success;
} catch (error) {
callback?.(false);
return false;
}
}
// 分块发送
private async sendChunked(data: Uint8Array, chunkSize: number, callback?: (success: boolean) => void): Promise<boolean> {
const totalChunks = Math.ceil(data.length / chunkSize);
let successfulChunks = 0;
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, data.length);
const chunk = data.slice(start, end);
// 添加分块信息
const chunkData = {
type: 'chunked',
chunkIndex: i,
totalChunks: totalChunks,
data: Array.from(chunk)
};
try {
const success = await this.sendWithRetry(
JSON.stringify(chunkData),
this.currentStrategy.retryCount,
this.currentStrategy.timeout
);
if (success) {
successfulChunks++;
} else {
console.error(`分块 ${i + 1}/${totalChunks} 发送失败`);
break;
}
} catch (error) {
console.error(`分块 ${i + 1}/${totalChunks} 发送异常:`, error);
callback?.(false);
return false;
}
// 分块间延迟,避免网络拥塞
if (i < totalChunks - 1) {
await this.delay(10);
}
}
const allSuccess = successfulChunks === totalChunks;
callback?.(allSuccess);
return allSuccess;
}
// 带重试的发送
private async sendWithRetry(data: string, maxRetries: number, timeout: number): Promise<boolean> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
// 设置超时
const sendPromise = super.send(data);
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('发送超时')), timeout);
});
await Promise.race([sendPromise, timeoutPromise]);
return true;
} catch (error) {
if (attempt === maxRetries) {
console.error(`发送失败,已达最大重试次数: ${maxRetries}`, error);
return false;
}
console.warn(`发送失败,第${attempt + 1}次重试...`, error);
await this.delay(this.getRetryDelay(attempt));
}
}
return false;
}
// 压缩数据
private compressData(data: any, compression: string): Uint8Array {
const jsonString = JSON.stringify(data);
const textEncoder = new TextEncoder();
switch (compression) {
case 'gzip':
// 实际开发中应使用实际的压缩库
// 这里简化处理
return textEncoder.encode(jsonString);
case 'high':
case 'maximum':
// 高级压缩
return textEncoder.encode(jsonString.substring(0, Math.floor(jsonString.length * 0.7)));
default:
return textEncoder.encode(jsonString);
}
}
// 网络质量变化处理
private onNetworkQualityChange(quality: NetworkQuality): void {
const oldStrategy = this.currentStrategy;
this.currentStrategy = this.qualityDetector.getTransmissionStrategy();
console.log(`传输策略更新: ${JSON.stringify(oldStrategy)} -> ${JSON.stringify(this.currentStrategy)}`);
// 如果网络质量变好,尝试发送缓冲的消息
if (quality !== 'BAD' && this.messageBuffer.length > 0) {
this.processBuffer();
}
}
// 获取重试延迟(指数退避)
private getRetryDelay(attempt: number): number {
return Math.min(1000 * Math.pow(2, attempt), 10000);
}
// 获取缓冲处理延迟
private getBufferProcessingDelay(): number {
const quality = this.qualityDetector.getCurrentQuality();
switch (quality) {
case 'EXCELLENT': return 10;
case 'GOOD': return 50;
case 'FAIR': return 100;
case 'POOR': return 500;
default: return 1000;
}
}
// 延迟函数
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 添加网络样本
addNetworkSample(sample: NetworkSample): void {
this.qualityDetector.addSample(sample);
}
// 获取当前网络质量报告
getNetworkQualityReport(): NetworkQualityReport {
return this.qualityDetector.getQualityReport();
}
// 获取当前传输策略
getCurrentTransmissionStrategy(): TransmissionStrategy {
return this.currentStrategy;
}
// 获取缓冲消息数量
getBufferedMessageCount(): number {
return this.messageBuffer.length;
}
}
五、HarmonyOS特定优化与配置
5.1 权限与配置管理
5.1.1 module.json5配置
{
"module": {
"name": "entry",
"type": "entry",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_permission",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.GET_NETWORK_INFO",
"reason": "$string:network_info_permission",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
}
],
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:entryability_description",
"icon": "$media:icon",
"label": "$string:entryability_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
],
"backgroundModes": [
"dataTransfer",
"location"
]
}
]
}
}
5.1.2 网络状态监听
import { network } from '@kit.NetworkKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
class NetworkMonitor {
private netHandle: network.NetConnection | null = null;
private listeners: Array<(connected: boolean, type?: network.NetBearType) => void> = [];
// 初始化网络监听
async initialize(): Promise<void> {
try {
this.netHandle = await network.getDefaultNet();
// 监听网络状态变化
this.netHandle.on('netAvailable', (data: network.NetAvailableCallback) => {
console.log('网络可用:', data);
this.notifyListeners(true, data.netInfo?.netBearType);
});
this.netHandle.on('netCapabilitiesChange', (data: network.NetCapabilitiesInfo) => {
console.log('网络能力变化:', data);
});
this.netHandle.on('netConnectionPropertiesChange', (data: network.ConnectionProperties) => {
console.log('网络连接属性变化:', data);
});
this.netHandle.on('netBlockStatusChange', (data: network.NetBlockStatusInfo) => {
console.log('网络阻塞状态变化:', data);
});
this.netHandle.on('netUnavailable', () => {
console.log('网络不可用');
this.notifyListeners(false);
});
} catch (error) {
console.error('初始化网络监听失败:', error as BusinessError);
}
}
// 注册网络状态变化监听器
addListener(listener: (connected: boolean, type?: network.NetBearType) => void): void {
this.listeners.push(listener);
}
// 移除监听器
removeListener(listener: (connected: boolean, type?: network.NetBearType) => void): void {
const index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
// 通知所有监听器
private notifyListeners(connected: boolean, type?: network.NetBearType): void {
this.listeners.forEach(listener => {
try {
listener(connected, type);
} catch (error) {
console.error('网络状态监听器执行失败:', error);
}
});
}
// 获取当前网络状态
async getCurrentNetworkStatus(): Promise<NetworkStatus> {
try {
if (!this.netHandle) {
await this.initialize();
}
const hasDefaultNet = await this.netHandle!.hasDefaultNet();
if (!hasDefaultNet) {
return {
connected: false,
type: undefined,
metered: false
};
}
const netCapabilities = await this.netHandle!.getNetCapabilities();
const connectionProperties = await this.netHandle!.getNetConnectionProperties();
return {
connected: true,
type: connectionProperties.netBearType,
metered: this.isMeteredConnection(connectionProperties.netBearType),
capabilities: netCapabilities,
properties: connectionProperties
};
} catch (error) {
console.error('获取网络状态失败:', error as BusinessError);
return {
connected: false,
type: undefined,
metered: false
};
}
}
// 判断是否为计量连接(移动数据)
private isMeteredConnection(type?: network.NetBearType): boolean {
if (!type) return false;
// BEARER_CELLULAR 表示蜂窝网络(移动数据)
return type === network.NetBearType.BEARER_CELLULAR;
}
// 检查是否连接到WiFi
isWifiConnected(): Promise<boolean> {
return this.getCurrentNetworkStatus().then(status => {
return status.connected && status.type === network.NetBearType.BEARER_WIFI;
});
}
// 检查是否使用移动数据
isCellularConnected(): Promise<boolean> {
return this.getCurrentNetworkStatus().then(status => {
return status.connected && status.type === network.NetBearType.BEARER_CELLULAR;
});
}
// 释放资源
release(): void {
if (this.netHandle) {
this.netHandle.off('netAvailable');
this.netHandle.off('netCapabilitiesChange');
this.netHandle.off('netConnectionPropertiesChange');
this.netHandle.off('netBlockStatusChange');
this.netHandle.off('netUnavailable');
this.netHandle = null;
}
this.listeners = [];
}
}
interface NetworkStatus {
connected: boolean;
type?: network.NetBearType;
metered: boolean;
capabilities?: network.NetCapabilities;
properties?: network.ConnectionProperties;
}
5.2 后台服务与保活机制
5.2.1 后台WebSocket服务
import { WantAgent } from '@kit.AbilityKit';
import { BackgroundTaskManager, BackgroundMode, ContinuationMode } from '@kit.AbilityKit';
import { notificationManager, NotificationRequest, notification } from '@kit.NotificationKit';
class BackgroundWebSocketService {
private wsManager: AdaptiveWebSocketManager | null = null;
private notificationId: number = 1001;
private isInBackground: boolean = false;
private reconnectInBackground: boolean = true;
constructor(private wsUrl: string, private protocols?: string | string[]) {}
// 启动WebSocket服务
async startService(): Promise<void> {
// 创建WebSocket管理器
this.wsManager = new AdaptiveWebSocketManager(this.wsUrl, this.protocols);
// 设置后台连接参数
await this.configureBackgroundConnection();
// 连接WebSocket
await this.connectWebSocket();
// 设置后台任务
await this.setupBackgroundTask();
// 监听应用状态变化
this.listenToAppState();
console.log('WebSocket后台服务已启动');
}
// 配置后台连接参数
private async configureBackgroundConnection(): Promise<void> {
if (!this.wsManager) return;
// 设置心跳间隔
this.wsManager.setHeartbeatInterval(30000); // 30秒
// 设置重连策略
this.wsManager.setReconnectConfig({
maxAttempts: 10,
baseDelay: 1000,
maxDelay: 60000, // 1分钟
jitter: true
});
// 监听连接状态变化
this.wsManager.on('connection_state', (state: string) => {
console.log(`WebSocket连接状态: ${state}`);
this.updateNotification(state);
});
// 监听消息
this.wsManager.on('message', (message: any) => {
this.handleBackgroundMessage(message);
});
}
// 连接WebSocket
private async connectWebSocket(): Promise<void> {
if (!this.wsManager) return;
try {
const connected = await this.wsManager.connect();
if (connected) {
console.log('WebSocket连接成功');
this.showConnectionNotification('已连接');
} else {
console.error('WebSocket连接失败');
this.showConnectionNotification('连接失败', true);
}
} catch (error) {
console.error('WebSocket连接异常:', error);
this.showConnectionNotification('连接异常', true);
}
}
// 设置后台任务
private async setupBackgroundTask(): Promise<void> {
try {
const bgTaskManager: BackgroundTaskManager = BackgroundTaskManager.getInstance();
// 申请后台任务
await bgTaskManager.requestSuspendDelay().then((delayInfo) => {
console.log(`后台任务延迟挂起: ${delayInfo.requestId}`);
// 任务即将挂起时的回调
bgTaskManager.on('expired', () => {
console.log('后台任务即将挂起');
this.onBackgroundTaskExpired();
});
// 更新后台任务状态
this.updateBackgroundTask(delayInfo.requestId);
});
} catch (error) {
console.error('设置后台任务失败:', error as BusinessError);
}
}
// 监听应用状态
private listenToAppState(): void {
// 监听应用切换到后台
AppStorage.setOrCreate('isForeground', true);
// 这里简化处理,实际应用中应使用AppLifecycle
// 或者Ability的生命周期回调
}
// 处理后台消息
private handleBackgroundMessage(message: any): void {
console.log('收到后台消息:', message);
// 根据消息类型处理
switch (message.type) {
case 'notification':
this.showBackgroundNotification(message);
break;
case 'data_update':
this.processDataUpdate(message);
break;
case 'command':
this.executeCommand(message);
break;
default:
console.log('未知消息类型:', message.type);
}
}
// 显示连接状态通知
private showConnectionNotification(status: string, isError: boolean = false): void {
const notificationRequest: notification.NotificationRequest = {
id: this.notificationId,
content: {
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: 'WebSocket服务',
text: `状态: ${status}`,
additionalText: new Date().toLocaleTimeString()
}
},
isOngoing: true, // 持续通知
isUnallowable: false,
tapDismissed: false,
autoDeletedTime: Date.now() + 3600000 // 1小时后自动删除
};
if (isError) {
notificationRequest.slotType = notification.SlotType.SOCIAL_COMMUNICATION;
}
notificationManager.publish(notificationRequest)
.then(() => console.log('通知显示成功'))
.catch((error: BusinessError) => console.error('显示通知失败:', error));
}
// 更新通知
private updateNotification(state: string): void {
this.showConnectionNotification(state, state.includes('ERROR') || state.includes('CLOSED'));
}
// 显示后台通知
private showBackgroundNotification(message: any): void {
const notificationRequest: notification.NotificationRequest = {
id: this.notificationId + 1,
content: {
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: message.title || '新消息',
text: message.content || '收到新消息',
additionalText: new Date().toLocaleTimeString()
}
},
slotType: notification.SlotType.SOCIAL_COMMUNICATION
};
notificationManager.publish(notificationRequest)
.then(() => console.log('后台通知显示成功'))
.catch((error: BusinessError) => console.error('显示后台通知失败:', error));
}
// 处理数据更新
private processDataUpdate(message: any): void {
// 更新本地数据存储
// 这里可以结合PersistentStorage或Preferences进行数据持久化
console.log('处理数据更新:', message.data);
}
// 执行命令
private executeCommand(message: any): void {
console.log('执行命令:', message.command);
// 执行相应的命令逻辑
}
// 后台任务即将到期
private onBackgroundTaskExpired(): void {
console.log('后台任务即将到期,保存状态');
if (this.wsManager) {
// 保存当前连接状态
const state = this.wsManager.getState();
// 如果是连接状态,可以尝试优雅关闭
if (state === 'OPEN') {
this.wsManager.send(JSON.stringify({
type: 'background_disconnect',
reason: 'app_suspend',
timestamp: Date.now()
})).catch(error => {
console.error('发送后台断开消息失败:', error);
});
}
}
}
// 更新后台任务
private updateBackgroundTask(requestId: number): void {
// 更新后台任务状态
console.log(`更新后台任务: ${requestId}`);
}
// 停止服务
async stopService(): Promise<void> {
console.log('停止WebSocket后台服务');
if (this.wsManager) {
// 发送断开消息
await this.wsManager.send(JSON.stringify({
type: 'disconnect',
reason: 'user_request',
timestamp: Date.now()
})).catch(error => {
console.error('发送断开消息失败:', error);
});
// 关闭连接
this.wsManager.close(1000, 'Normal closure');
this.wsManager = null;
}
// 取消通知
notificationManager.cancel(this.notificationId)
.then(() => console.log('通知已取消'))
.catch((error: BusinessError) => console.error('取消通知失败:', error));
console.log('WebSocket后台服务已停止');
}
// 发送消息
async sendMessage(data: any): Promise<boolean> {
if (!this.wsManager) {
console.error('WebSocket服务未启动');
return false;
}
return this.wsManager.sendAdaptive(data);
}
// 获取服务状态
getServiceStatus(): ServiceStatus {
return {
isRunning: this.wsManager !== null,
connectionState: this.wsManager?.getState() || 'CLOSED',
isInBackground: this.isInBackground,
bufferedMessages: this.wsManager?.getBufferedMessageCount() || 0
};
}
}
interface ServiceStatus {
isRunning: boolean;
connectionState: string;
isInBackground: boolean;
bufferedMessages: number;
}
六、调试与故障排除
6.1 详细的调试工具
6.1.1 WebSocket调试器
class WebSocketDebugger {
private logs: DebugLog[] = [];
private maxLogs = 1000;
private eventListeners: Map<string, Function[]> = new Map();
// 日志记录
log(level: LogLevel, category: LogCategory, message: string, data?: any): void {
const logEntry: DebugLog = {
id: this.generateLogId(),
timestamp: Date.now(),
level,
category,
message,
data: data ? JSON.stringify(data, this.getCircularReplacer()) : undefined
};
this.logs.push(logEntry);
// 限制日志数量
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
// 触发日志事件
this.emit('log', logEntry);
// 控制台输出
this.consoleLog(logEntry);
}
// 生成日志ID
private generateLogId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// 控制台输出
private consoleLog(log: DebugLog): void {
const timestamp = new Date(log.timestamp).toISOString().substr(11, 12);
const prefix = `[${timestamp}] [${log.level}] [${log.category}]`;
switch (log.level) {
case 'ERROR':
console.error(prefix, log.message, log.data || '');
break;
case 'WARN':
console.warn(prefix, log.message, log.data || '');
break;
case 'INFO':
console.info(prefix, log.message, log.data || '');
break;
case 'DEBUG':
console.debug(prefix, log.message, log.data || '');
break;
default:
console.log(prefix, log.message, log.data || '');
}
}
// 处理循环引用
private getCircularReplacer(): (key: string, value: any) => any {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular Reference]';
}
seen.add(value);
}
return value;
};
}
// 记录连接事件
logConnection(event: string, data?: any): void {
this.log('INFO', 'CONNECTION', event, data);
}
// 记录消息事件
logMessage(direction: 'SEND' | 'RECEIVE', message: any): void {
this.log('DEBUG', 'MESSAGE', `${direction} message`, message);
}
// 记录错误事件
logError(context: string, error: any): void {
this.log('ERROR', 'ERROR', context, {
message: error.message,
code: error.code,
stack: error.stack
});
}
// 记录网络事件
logNetwork(event: string, data?: any): void {
this.log('INFO', 'NETWORK', event, data);
}
// 记录性能事件
logPerformance(operation: string, duration: number, data?: any): void {
this.log('INFO', 'PERFORMANCE', `${operation} took ${duration}ms`, data);
}
// 获取日志
getLogs(filter?: LogFilter): DebugLog[] {
let filteredLogs = this.logs;
if (filter) {
if (filter.level) {
filteredLogs = filteredLogs.filter(log => log.level === filter.level);
}
if (filter.category) {
filteredLogs = filteredLogs.filter(log => log.category === filter.category);
}
if (filter.startTime) {
filteredLogs = filteredLogs.filter(log => log.timestamp >= filter.startTime!);
}
if (filter.endTime) {
filteredLogs = filteredLogs.filter(log => log.timestamp <= filter.endTime!);
}
if (filter.searchText) {
const searchText = filter.searchText.toLowerCase();
filteredLogs = filteredLogs.filter(log =>
log.message.toLowerCase().includes(searchText) ||
(log.data && log.data.toLowerCase().includes(searchText))
);
}
}
return filteredLogs;
}
// 获取日志统计
getLogStats(): LogStats {
const levels: Record<LogLevel, number> = {
DEBUG: 0,
INFO: 0,
WARN: 0,
ERROR: 0
};
const categories: Record<LogCategory, number> = {
CONNECTION: 0,
MESSAGE: 0,
ERROR: 0,
NETWORK: 0,
PERFORMANCE: 0,
OTHER: 0
};
this.logs.forEach(log => {
levels[log.level]++;
categories[log.category]++;
});
return {
total: this.logs.length,
levels,
categories,
timeRange: {
start: this.logs[0]?.timestamp || 0,
end: this.logs[this.logs.length - 1]?.timestamp || 0
}
};
}
// 导出日志
exportLogs(format: 'json' | 'text' = 'json'): string {
const logs = this.getLogs();
if (format === 'json') {
return JSON.stringify(logs, null, 2);
} else {
return logs.map(log => {
const time = new Date(log.timestamp).toLocaleString();
return `[${time}] [${log.level}] [${log.category}] ${log.message} ${log.data || ''}`;
}).join('\n');
}
}
// 清空日志
clearLogs(): void {
this.logs = [];
}
// 事件监听
on(event: string, callback: Function): void {
if (!this.eventListeners.has(event)) {
this.eventListeners.set(event, []);
}
this.eventListeners.get(event)!.push(callback);
}
private emit(event: string, ...args: any[]): void {
const listeners = this.eventListeners.get(event);
if (listeners) {
listeners.forEach(callback => {
try {
callback(...args);
} catch (error) {
console.error(`事件 ${event} 监听器执行失败:`, error);
}
});
}
}
}
interface DebugLog {
id: string;
timestamp: number;
level: LogLevel;
category: LogCategory;
message: string;
data?: string;
}
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
type LogCategory = 'CONNECTION' | 'MESSAGE' | 'ERROR' | 'NETWORK' | 'PERFORMANCE' | 'OTHER';
interface LogFilter {
level?: LogLevel;
category?: LogCategory;
startTime?: number;
endTime?: number;
searchText?: string;
}
interface LogStats {
total: number;
levels: Record<LogLevel, number>;
categories: Record<LogCategory, number>;
timeRange: {
start: number;
end: number;
};
}
6.2 错误码详细解析与处理
6.2.1 WebSocket错误码处理器
class WebSocketErrorHandler {
private errorCounts: Map<number, number> = new Map();
private lastErrorTime: Map<number, number> = new Map();
private errorThreshold = 5; // 错误阈值
private timeWindow = 60000; // 时间窗口(毫秒)
// 处理错误
handleError(error: BusinessError, context: ErrorContext): ErrorHandlingResult {
const errorCode = error.code || -1;
const currentTime = Date.now();
// 更新错误计数
const count = (this.errorCounts.get(errorCode) || 0) + 1;
this.errorCounts.set(errorCode, count);
this.lastErrorTime.set(errorCode, currentTime);
// 获取错误信息
const errorInfo = this.getErrorInfo(errorCode, error.message);
// 记录错误
console.error(`WebSocket错误 [${errorCode}]: ${errorInfo.description}`, {
context,
error,
count,
time: new Date(currentTime).toISOString()
});
// 检查是否需要特殊处理
if (this.shouldTakeSpecialAction(errorCode, count, currentTime)) {
return this.takeSpecialAction(errorCode, errorInfo, context);
}
// 常规错误处理
return this.handleCommonError(errorCode, errorInfo, context);
}
// 获取错误信息
private getErrorInfo(errorCode: number, originalMessage?: string): WebSocketErrorInfo {
const errorMap: Record<number, WebSocketErrorInfo> = {
[-1]: {
code: -1,
level: 'ERROR',
description: '未知错误',
possibleCauses: [
'网络连接异常',
'服务器内部错误',
'客户端资源不足',
'系统限制'
],
suggestedActions: [
'检查网络连接',
'重启应用',
'检查服务器状态',
'联系技术支持'
],
retryable: true,
immediateAction: 'RECONNECT'
},
[1006]: {
code: 1006,
level: 'ERROR',
description: '连接异常关闭',
possibleCauses: [
'网络中断',
'服务器崩溃',
'防火墙阻止',
'SSL证书问题'
],
suggestedActions: [
'检查网络稳定性',
'验证服务器证书',
'检查防火墙设置',
'尝试重新连接'
],
retryable: true,
immediateAction: 'RECONNECT'
},
[1001]: {
code: 1001,
level: 'INFO',
description: '连接正常关闭',
possibleCauses: [
'客户端主动关闭',
'服务器正常关闭',
'会话超时'
],
suggestedActions: [
'无需操作',
'如需重新连接,手动触发'
],
retryable: false,
immediateAction: 'NONE'
},
[1002]: {
code: 1002,
level: 'ERROR',
description: '协议错误',
possibleCauses: [
'协议版本不匹配',
'消息格式错误',
'扩展协商失败'
],
suggestedActions: [
'检查协议配置',
'更新客户端版本',
'联系服务端开发者'
],
retryable: false,
immediateAction: 'VALIDATE_CONFIG'
},
[1003]: {
code: 1003,
level: 'ERROR',
description: '不接受的数据类型',
possibleCauses: [
'发送了不支持的数据类型',
'编码格式错误',
'消息过大'
],
suggestedActions: [
'检查消息格式',
'验证数据编码',
'分割大消息'
],
retryable: false,
immediateAction: 'VALIDATE_DATA'
},
[1005]: {
code: 1005,
level: 'ERROR',
description: '无状态码',
possibleCauses: [
'连接未完全建立',
'服务器响应异常',
'代理服务器问题'
],
suggestedActions: [
'检查连接状态',
'验证服务器配置',
'检查代理设置'
],
retryable: true,
immediateAction: 'RECONNECT'
},
[1007]: {
code: 1007,
level: 'ERROR',
description: '数据格式错误',
possibleCauses: [
'消息编码不一致',
'UTF-8验证失败',
'二进制数据格式错误'
],
suggestedActions: [
'检查消息编码',
'验证UTF-8格式',
'统一数据格式'
],
retryable: false,
immediateAction: 'VALIDATE_DATA'
},
[1008]: {
code: 1008,
level: 'WARN',
description: '策略违规',
possibleCauses: [
'发送频率过高',
'消息内容违规',
'权限不足'
],
suggestedActions: [
'降低发送频率',
'检查消息内容',
'验证权限设置'
],
retryable: true,
immediateAction: 'WAIT_AND_RETRY'
},
[1009]: {
code: 1009,
level: 'ERROR',
description: '消息过大',
possibleCauses: [
'单条消息超过限制',
'缓冲区溢出',
'内存不足'
],
suggestedActions: [
'分割大消息',
'增加缓冲区大小',
'优化内存使用'
],
retryable: false,
immediateAction: 'SPLIT_DATA'
},
[1011]: {
code: 1011,
level: 'ERROR',
description: '服务器内部错误',
possibleCauses: [
'服务器处理异常',
'后端服务故障',
'数据库连接失败'
],
suggestedActions: [
'稍后重试',
'联系服务器管理员',
'检查服务器日志'
],
retryable: true,
immediateAction: 'WAIT_AND_RETRY'
},
[1012]: {
code: 1012,
level: 'ERROR',
description: '服务重启',
possibleCauses: [
'服务器维护',
'服务重启',
'负载均衡切换'
],
suggestedActions: [
'等待服务恢复',
'检查服务器状态',
'查看维护公告'
],
retryable: true,
immediateAction: 'WAIT_AND_RETRY'
},
[1013]: {
code: 1013,
level: 'ERROR',
description: '临时不可用',
possibleCauses: [
'服务器过载',
'网络拥堵',
'资源限制'
],
suggestedActions: [
'稍后重试',
'减少请求频率',
'使用备用服务器'
],
retryable: true,
immediateAction: 'WAIT_AND_RETRY'
},
[1014]: {
code: 1014,
level: 'ERROR',
description: '网关错误',
possibleCauses: [
'代理服务器故障',
'负载均衡器问题',
'DNS解析失败'
],
suggestedActions: [
'检查网络代理',
'验证DNS配置',
'联系网络管理员'
],
retryable: true,
immediateAction: 'CHECK_NETWORK'
},
[1015]: {
code: 1015,
level: 'ERROR',
description: 'TLS握手失败',
possibleCauses: [
'证书过期',
'协议不匹配',
'加密套件不支持'
],
suggestedActions: [
'检查证书有效性',
'更新TLS版本',
'验证加密配置'
],
retryable: false,
immediateAction: 'VALIDATE_TLS'
}
};
const defaultError: WebSocketErrorInfo = {
code: errorCode,
level: 'ERROR',
description: originalMessage || '未知错误',
possibleCauses: ['未知原因'],
suggestedActions: ['查看详细日志', '联系技术支持'],
retryable: true,
immediateAction: 'LOG_AND_CONTINUE'
};
return errorMap[errorCode] || defaultError;
}
// 检查是否需要特殊处理
private shouldTakeSpecialAction(errorCode: number, count: number, currentTime: number): boolean {
const lastTime = this.lastErrorTime.get(errorCode) || 0;
// 检查是否在时间窗口内达到错误阈值
if (count >= this.errorThreshold && (currentTime - lastTime) < this.timeWindow) {
return true;
}
// 特定错误码的特殊处理
const criticalErrors = [1006, 1009, 1011, 1015];
if (criticalErrors.includes(errorCode) && count >= 3) {
return true;
}
return false;
}
// 采取特殊行动
private takeSpecialAction(errorCode: number, errorInfo: WebSocketErrorInfo, context: ErrorContext): ErrorHandlingResult {
console.warn(`采取特殊处理措施,错误码: ${errorCode}, 错误次数超过阈值`);
switch (errorCode) {
case 1006: // 连接异常关闭
return {
action: 'IMMEDIATE_RECONNECT',
delay: 0,
message: '连接异常,立即重连',
notifyUser: false,
logLevel: 'WARN'
};
case 1009: // 消息过大
return {
action: 'SPLIT_AND_RETRY',
delay: 1000,
message: '消息过大,分割后重试',
notifyUser: true,
userMessage: '消息过大,正在尝试分割发送',
logLevel: 'INFO'
};
case 1011: // 服务器内部错误
return {
action: 'WAIT_LONG',
delay: 30000, // 30秒
message: '服务器内部错误,等待30秒后重试',
notifyUser: true,
userMessage: '服务器暂时不可用,请稍后重试',
logLevel: 'WARN'
};
case 1015: // TLS握手失败
return {
action: 'FALLBACK_TO_INSECURE',
delay: 0,
message: 'TLS握手失败,尝试非安全连接',
notifyUser: true,
userMessage: '安全连接失败,尝试普通连接',
logLevel: 'ERROR'
};
default:
return {
action: 'WAIT_AND_RETRY',
delay: 5000,
message: '错误频繁,等待5秒后重试',
notifyUser: false,
logLevel: 'WARN'
};
}
}
// 处理常规错误
private handleCommonError(errorCode: number, errorInfo: WebSocketErrorInfo, context: ErrorContext): ErrorHandlingResult {
const result: ErrorHandlingResult = {
action: 'CONTINUE',
delay: 0,
message: errorInfo.description,
notifyUser: false,
logLevel: errorInfo.level
};
// 根据错误级别和建议行动确定处理方式
switch (errorInfo.immediateAction) {
case 'RECONNECT':
result.action = 'RECONNECT';
result.delay = 1000;
break;
case 'WAIT_AND_RETRY':
result.action = 'WAIT_AND_RETRY';
result.delay = 2000;
break;
case 'VALIDATE_CONFIG':
result.action = 'VALIDATE_CONFIG';
result.notifyUser = true;
result.userMessage = '配置错误,请检查设置';
break;
case 'VALIDATE_DATA':
result.action = 'VALIDATE_DATA';
result.notifyUser = context.isUserAction || false;
if (result.notifyUser) {
result.userMessage = '数据格式错误,请检查输入';
}
break;
case 'SPLIT_DATA':
result.action = 'SPLIT_DATA';
result.delay = 500;
break;
case 'CHECK_NETWORK':
result.action = 'CHECK_NETWORK';
result.notifyUser = true;
result.userMessage = '网络连接异常,请检查网络';
break;
case 'VALIDATE_TLS':
result.action = 'VALIDATE_TLS';
result.notifyUser = true;
result.userMessage = '安全连接失败,请检查证书';
break;
case 'LOG_AND_CONTINUE':
result.action = 'CONTINUE';
break;
case 'NONE':
result.action = 'NONE';
break;
}
// 如果是用户操作,增加通知
if (context.isUserAction && !result.notifyUser) {
result.notifyUser = true;
result.userMessage = result.userMessage || '操作失败,请稍后重试';
}
return result;
}
// 重置错误计数
resetErrorCount(errorCode?: number): void {
if (errorCode !== undefined) {
this.errorCounts.delete(errorCode);
this.lastErrorTime.delete(errorCode);
} else {
this.errorCounts.clear();
this.lastErrorTime.clear();
}
}
// 获取错误统计
getErrorStats(): ErrorStats {
const stats: ErrorStats = {
totalErrors: 0,
errorsByCode: {},
recentErrors: [],
mostCommonError: { code: 0, count: 0 }
};
let maxCount = 0;
this.errorCounts.forEach((count, code) => {
stats.totalErrors += count;
stats.errorsByCode[code] = {
count,
lastOccurred: this.lastErrorTime.get(code) || 0
};
if (count > maxCount) {
maxCount = count;
stats.mostCommonError = { code, count };
}
});
// 获取最近10个错误
const errorEntries = Array.from(this.errorCounts.entries());
stats.recentErrors = errorEntries
.sort(([, a], [, b]) => b - a)
.slice(0, 10)
.map(([code, count]) => ({
code,
count,
lastOccurred: this.lastErrorTime.get(code) || 0
}));
return stats;
}
}
interface WebSocketErrorInfo {
code: number;
level: LogLevel;
description: string;
possibleCauses: string[];
suggestedActions: string[];
retryable: boolean;
immediateAction: ErrorAction;
}
type ErrorAction =
| 'RECONNECT'
| 'WAIT_AND_RETRY'
| 'VALIDATE_CONFIG'
| 'VALIDATE_DATA'
| 'SPLIT_DATA'
| 'CHECK_NETWORK'
| 'VALIDATE_TLS'
| 'LOG_AND_CONTINUE'
| 'NONE'
| 'IMMEDIATE_RECONNECT'
| 'WAIT_LONG'
| 'FALLBACK_TO_INSECURE';
interface ErrorContext {
operation: string;
timestamp: number;
url?: string;
messageSize?: number;
isUserAction?: boolean;
additionalInfo?: Record<string, any>;
}
interface ErrorHandlingResult {
action: string;
delay: number;
message: string;
notifyUser: boolean;
userMessage?: string;
logLevel: LogLevel;
}
interface ErrorStats {
totalErrors: number;
errorsByCode: Record<number, { count: number; lastOccurred: number }>;
recentErrors: Array<{ code: number; count: number; lastOccurred: number }>;
mostCommonError: { code: number; count: number };
}
七、完整示例应用
7.1 完整的WebSocket聊天应用示例
// ChatClient.ets 续接
// 构建调试面板(续)
@Builder
buildDebugPanel() {
Column({ space: 8 }) {
Text('调试信息')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
.padding(5)
.backgroundColor(Color.Blue.copy(0.1))
const debugInfo = this.getDebugInfo()
// 连接信息
Row({ space: 10 }) {
Text('连接状态:')
.fontSize(12)
.layoutWeight(1)
Text(debugInfo.connectionState)
.fontSize(12)
.fontColor(this.getConnectionStatusColor(debugInfo.connectionState))
}
.width('100%')
.padding(5)
// 网络质量
Row({ space: 10 }) {
Text('网络质量:')
.fontSize(12)
.layoutWeight(1)
Text(debugInfo.networkQuality)
.fontSize(12)
.fontColor(this.getNetworkQualityColor(debugInfo.networkQuality))
}
.width('100%')
.padding(5)
// 延迟信息
if (debugInfo.latency) {
Row({ space: 10 }) {
Text('网络延迟:')
.fontSize(12)
.layoutWeight(1)
Text(`${debugInfo.latency.toFixed(0)}ms`)
.fontSize(12)
.fontColor(this.getLatencyColor(debugInfo.latency))
}
.width('100%')
.padding(5)
}
// 丢包率
if (debugInfo.packetLoss !== undefined) {
Row({ space: 10 }) {
Text('丢包率:')
.fontSize(12)
.layoutWeight(1)
Text(`${debugInfo.packetLoss.toFixed(1)}%`)
.fontSize(12)
.fontColor(this.getPacketLossColor(debugInfo.packetLoss))
}
.width('100%')
.padding(5)
}
// 消息统计
Row({ space: 10 }) {
Text('消息数量:')
.fontSize(12)
.layoutWeight(1)
Text(debugInfo.messageCount.toString())
.fontSize(12)
}
.width('100%')
.padding(5)
// 缓冲消息
if (debugInfo.bufferedMessages) {
Row({ space: 10 }) {
Text('缓冲消息:')
.fontSize(12)
.layoutWeight(1)
Text(debugInfo.bufferedMessages.toString())
.fontSize(12)
.fontColor(debugInfo.bufferedMessages > 0 ? Color.Orange : Color.Black)
}
.width('100%')
.padding(5)
}
// 错误统计
Row({ space: 10 }) {
Text('错误数量:')
.fontSize(12)
.layoutWeight(1)
Text(debugInfo.errorCount.toString())
.fontSize(12)
.fontColor(debugInfo.errorCount > 0 ? Color.Red : Color.Black)
}
.width('100%')
.padding(5)
// 控制按钮
Row({ space: 5 }) {
Button('导出日志')
.fontSize(12)
.layoutWeight(1)
.onClick(() => {
this.exportDebugLogs()
})
Button('重置统计')
.fontSize(12)
.layoutWeight(1)
.onClick(() => {
this.debugger.clearLogs()
this.errorHandler.resetErrorCount()
})
Button('关闭面板')
.fontSize(12)
.layoutWeight(1)
.onClick(() => {
this.showDebugPanel = false
})
}
.width('100%')
.padding(5)
}
.width('100%')
.padding(10)
.backgroundColor(Color.White)
.border({ width: 1, color: Color.Gray })
}
// 获取网络质量颜色
private getNetworkQualityColor(quality: string): Color {
switch (quality) {
case 'EXCELLENT': return Color.Green
case 'GOOD': return Color.Blue
case 'FAIR': return Color.Orange
case 'POOR': return Color.Orange.copy(0.8)
case 'BAD': return Color.Red
default: return Color.Gray
}
}
// 获取连接状态颜色
private getConnectionStatusColor(status: string): Color {
switch (status) {
case 'OPEN': return Color.Green
case 'CONNECTING': return Color.Blue
case 'CLOSING': return Color.Orange
case 'CLOSED': return Color.Red
case 'ERROR': return Color.Red
default: return Color.Gray
}
}
// 获取延迟颜色
private getLatencyColor(latency: number): Color {
if (latency < 100) return Color.Green
if (latency < 200) return Color.Blue
if (latency < 500) return Color.Orange
return Color.Red
}
// 获取丢包率颜色
private getPacketLossColor(packetLoss: number): Color {
if (packetLoss < 1) return Color.Green
if (packetLoss < 5) return Color.Blue
if (packetLoss < 10) return Color.Orange
return Color.Red
}
}
// 类型定义
interface ChatMessage {
id: string
sender: string
content: string
timestamp: number
isOwn?: boolean
isSystem?: boolean
status?: 'sending' | 'sent' | 'delivered' | 'read' | 'failed'
}
interface DebugInfo {
connectionState: string
networkQuality: string
latency?: number
packetLoss?: number
messageCount: number
bufferedMessages?: number
errorCount: number
mostCommonError?: number
}
7.2 完整的应用入口和配置
// EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { display } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('EntryAbility onCreate');
// 设置显示参数
const displayClass = new display.Display();
displayClass.getDefaultDisplay().then((dis) => {
console.info('Display info: ' + JSON.stringify(dis));
}).catch((err) => {
console.error('Failed to obtain the default display. Code: ' + JSON.stringify(err));
});
}
onDestroy(): void {
console.info('EntryAbility onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
console.info('EntryAbility onWindowStageCreate');
// 设置主窗口
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('Failed to load the content. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
console.info('EntryAbility onWindowStageDestroy');
}
onForeground(): void {
console.info('EntryAbility onForeground');
}
onBackground(): void {
console.info('EntryAbility onBackground');
}
}
// pages/Index.ets
import { ChatClient } from '../components/ChatClient';
@Entry
@Component
struct Index {
@State currentPage: string = 'chat';
build() {
Column() {
if (this.currentPage === 'chat') {
ChatClient()
} else if (this.currentPage === 'settings') {
this.buildSettingsPage()
} else if (this.currentPage === 'about') {
this.buildAboutPage()
}
// 底部导航
this.buildBottomNavigation()
}
.width('100%')
.height('100%')
}
@Builder
buildSettingsPage() {
Column({ space: 20 }) {
Text('设置')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
// WebSocket 服务器设置
Row({ space: 10 }) {
Text('服务器地址:')
.fontSize(16)
.width(100)
TextInput({ placeholder: 'wss://example.com/ws' })
.layoutWeight(1)
}
.width('90%')
.padding(10)
// 重连设置
Row({ space: 10 }) {
Text('最大重连次数:')
.fontSize(16)
.width(120)
TextInput({ text: '5' })
.width(60)
}
.width('90%')
.padding(10)
// 网络检测
Row({ space: 10 }) {
Text('启用网络检测:')
.fontSize(16)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch })
}
.width('90%')
.padding(10)
// 消息压缩
Row({ space: 10 }) {
Text('消息压缩:')
.fontSize(16)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch })
}
.width('90%')
.padding(10)
Button('保存设置')
.width(200)
.margin({ top: 20 })
.onClick(() => {
// 保存设置逻辑
})
}
.width('100%')
.alignItems(HorizontalAlign.Center)
}
@Builder
buildAboutPage() {
Column({ space: 15 }) {
Text('WebSocket 聊天应用')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 30 })
Text('版本 1.0.0')
.fontSize(16)
.fontColor(Color.Gray)
Text('一个基于HarmonyOS的实时聊天应用示例')
.fontSize(14)
.textAlign(TextAlign.Center)
.margin({ top: 20, bottom: 20 })
.padding(20)
Text('功能特性')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 10 })
List({ space: 10 }) {
ListItem() {
Text('• 实时消息收发')
}
ListItem() {
Text('• 自动重连机制')
}
ListItem() {
Text('• 网络质量检测')
}
ListItem() {
Text('• 自适应传输策略')
}
ListItem() {
Text('• 详细的错误处理')
}
ListItem() {
Text('• 消息缓冲队列')
}
}
.width('90%')
.margin({ top: 10, bottom: 20 })
}
.width('100%')
.alignItems(HorizontalAlign.Center)
}
@Builder
buildBottomNavigation() {
Row({ space: 0 }) {
Column({ space: 5 }) {
Image($r('app.media.ic_chat'))
.width(24)
.height(24)
.fillColor(this.currentPage === 'chat' ? Color.Blue : Color.Gray)
Text('聊天')
.fontSize(12)
.fontColor(this.currentPage === 'chat' ? Color.Blue : Color.Gray)
}
.layoutWeight(1)
.padding(10)
.onClick(() => {
this.currentPage = 'chat'
})
Column({ space: 5 }) {
Image($r('app.media.ic_settings'))
.width(24)
.height(24)
.fillColor(this.currentPage === 'settings' ? Color.Blue : Color.Gray)
Text('设置')
.fontSize(12)
.fontColor(this.currentPage === 'settings' ? Color.Blue : Color.Gray)
}
.layoutWeight(1)
.padding(10)
.onClick(() => {
this.currentPage = 'settings'
})
Column({ space: 5 }) {
Image($r('app.media.ic_info'))
.width(24)
.height(24)
.fillColor(this.currentPage === 'about' ? Color.Blue : Color.Gray)
Text('关于')
.fontSize(12)
.fontColor(this.currentPage === 'about' ? Color.Blue : Color.Gray)
}
.layoutWeight(1)
.padding(10)
.onClick(() => {
this.currentPage = 'about'
})
}
.width('100%')
.height(60)
.backgroundColor(Color.White)
.border({ width: 1, color: Color.Gray.copy(0.2) })
}
}
7.3 配置文件
// module.json5
{
"module": {
"name": "websocketdemo",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"backgroundModes": [
"dataTransfer"
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.GET_NETWORK_INFO",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
}
]
}
}
// resources/base/profile/main_pages.json
{
"src": [
"pages/Index"
]
}
// resources/base/element/string.json
{
"string": [
{
"name": "module_desc",
"value": "WebSocket Demo Application"
},
{
"name": "EntryAbility_desc",
"value": "Main entry ability"
},
{
"name": "EntryAbility_label",
"value": "WebSocket Chat"
}
]
}
7.4 完整的工具函数和常量
// utils/WebSocketUtils.ets
export class WebSocketUtils {
// 生成UUID
static generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// 格式化时间
static formatTime(timestamp: number): string {
const date = new Date(timestamp);
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
}
// 格式化日期
static formatDate(timestamp: number): string {
const date = new Date(timestamp);
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
}
// 计算消息大小
static calculateMessageSize(message: any): number {
try {
const jsonString = JSON.stringify(message);
return new Blob([jsonString]).size;
} catch (error) {
return 0;
}
}
// 延迟函数
static delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 生成重连延迟(指数退避算法)
static getExponentialBackoffDelay(attempt: number, baseDelay: number = 1000, maxDelay: number = 30000): number {
const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
// 添加随机抖动
const jitter = delay * 0.1 * Math.random();
return delay + jitter;
}
// 验证WebSocket URL
static validateWebSocketUrl(url: string): boolean {
try {
const urlObj = new URL(url);
return urlObj.protocol === 'ws:' || urlObj.protocol === 'wss:';
} catch (error) {
return false;
}
}
// 安全的JSON解析
static safeJsonParse(jsonString: string): any {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error('JSON解析失败:', error);
return null;
}
}
// 深度克隆对象
static deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
// 节流函数
static throttle(func: Function, limit: number): Function {
let inThrottle: boolean;
return function(this: any, ...args: any[]) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 防抖函数
static debounce(func: Function, wait: number): Function {
let timeout: number;
return function(this: any, ...args: any[]) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
}
7.5 使用示例
// 使用示例
@Component
struct ExampleUsage {
private wsManager: AdaptiveWebSocketManager | null = null;
private networkMonitor: NetworkMonitor = new NetworkMonitor();
private debugger: WebSocketDebugger = new WebSocketDebugger();
aboutToAppear(): void {
this.initWebSocket();
}
private async initWebSocket(): Promise<void> {
// 初始化网络监控
await this.networkMonitor.initialize();
// 创建WebSocket管理器
this.wsManager = new AdaptiveWebSocketManager('wss://your-server.com/ws', 'your-protocol');
// 设置调试器
this.wsManager.setDebugger(this.debugger);
// 监听连接状态
this.wsManager.on('connection_state', (state: string) => {
console.log('连接状态:', state);
});
// 监听消息
this.wsManager.on('message', (data: any) => {
console.log('收到消息:', data);
});
// 监听网络质量
this.wsManager.on('network_quality', (quality: NetworkQuality) => {
console.log('网络质量:', quality);
});
// 监听错误
this.wsManager.on('error', (error: BusinessError) => {
console.error('WebSocket错误:', error);
});
// 连接服务器
try {
const connected = await this.wsManager.connect();
if (connected) {
console.log('连接成功');
// 发送消息
await this.wsManager.sendAdaptive({
type: 'hello',
message: 'Hello, Server!',
timestamp: Date.now()
});
// 获取网络质量报告
const report = this.wsManager.getNetworkQualityReport();
console.log('网络质量报告:', report);
// 获取传输策略
const strategy = this.wsManager.getCurrentTransmissionStrategy();
console.log('当前传输策略:', strategy);
}
} catch (error) {
console.error('连接失败:', error);
}
}
aboutToDisappear(): void {
// 清理资源
if (this.wsManager) {
this.wsManager.close(1000, '组件销毁');
}
this.networkMonitor.release();
}
build() {
Column() {
// 你的UI组件
}
}
}
8. 最佳实践总结
8.1 关键注意事项
-
连接管理
-
在组件生命周期中正确管理连接创建和销毁
-
使用重连机制处理网络波动
-
实现连接状态监控
-
-
错误处理
-
实现分级的错误处理策略
-
记录详细的错误日志
-
提供用户友好的错误提示
-
-
性能优化
-
根据网络质量自适应调整策略
-
实现消息缓冲和压缩
-
合理使用心跳包保持连接
-
-
内存管理
-
及时清理不再使用的消息
-
避免内存泄漏
-
合理使用缓存
-
-
用户体验
-
提供实时连接状态反馈
-
实现消息发送状态提示
-
支持离线消息缓冲
-
8.2 调试技巧
-
启用调试模式
// 在开发阶段启用详细日志
this.wsManager?.enableDebug(true);
-
监控网络质量
// 定期检查网络质量
setInterval(() => {
const report = this.wsManager?.getNetworkQualityReport();
console.log('网络质量报告:', report);
}, 30000);
-
导出调试信息
// 导出调试日志
const logs = this.debugger.exportLogs('json');
// 可以保存到文件或发送到服务器进行分析
8.3 生产环境建议
-
安全性
-
使用WSS(WebSocket Secure)
-
实现消息加密
-
添加身份验证机制
-
-
可扩展性
-
使用配置文件管理服务器地址
-
实现多服务器故障转移
-
支持协议协商
-
-
监控
-
集成应用性能监控
-
实现使用统计
-
设置告警机制
-
结束语
经过对HarmonyOS WebSocket技术的全面探讨和代码实现,我们可以看到,在鸿蒙生态系统中构建稳定、高效的实时通信应用需要综合考虑多个方面。以下是本指南的核心总结:
🌟 关键收获
1. 架构设计的重要性
-
分层设计确保各模块职责清晰
-
适配器模式实现不同网络环境的平滑切换
-
可插拔组件便于功能扩展和维护
2. 稳定性的核心要素
-
智能重连机制:指数退避+随机抖动避免服务器压力
-
心跳保活:动态调整间隔平衡性能和能耗
-
网络自适应:根据质量动态调整传输策略
-
消息缓冲:确保弱网环境下消息不丢失
3. 用户体验的优化
-
实时状态反馈让用户感知连接状态
-
优雅降级在不同网络环境下提供最佳体验
-
错误处理的友好提示提升用户信心
🚀 最佳实践建议
开发阶段
-
充分测试各种网络场景
-
Wi-Fi/移动网络切换
-
弱网和断网恢复
-
高延迟高丢包环境
-
-
监控关键指标
-
连接成功率
-
消息到达延迟
-
重连频率和成功率
-
资源消耗情况
-
-
实现完善的日志系统
-
结构化的日志格式
-
多级别的日志记录
-
方便的日志导出和分析
-
生产环境
-
安全第一
-
使用WSS协议
-
实现消息加密
-
添加身份验证机制
-
防止WebSocket攻击
-
-
性能优化
-
消息压缩减少流量
-
批量发送减少请求
-
合理的缓存策略
-
内存泄漏预防
-
-
监控告警
-
实时监控连接状态
-
异常情况自动告警
-
性能指标可视化
-
💡 扩展思考
未来发展方向
-
协议升级
-
支持WebSocket子协议
-
兼容MQTT等物联网协议
-
探索HTTP/3 QUIC协议
-
-
AI优化
-
基于历史数据的智能预测
-
自适应学习最优传输策略
-
异常行为自动检测
-
-
跨平台适配
-
统一的API接口
-
平台特定优化
-
一致的开发体验
-
📚 资源推荐
官方资源
学习资源
-
WebSocket协议RFC文档
-
网络编程相关书籍
-
开源项目代码学习
🤝 社区支持
在开发过程中遇到问题,可以通过以下方式获取帮助:
-
官方社区
-
HarmonyOS开发者论坛
-
官方技术博客
-
GitHub开源项目
-
-
开发者交流
-
技术交流群
-
线下开发者活动
-
技术分享会
-
-
贡献代码
-
提交Issue和PR
-
分享最佳实践
-
参与开源项目
-
✨ 最后的建议
-
从简单开始:先实现基础功能,再逐步添加高级特性
-
测试驱动:编写充分的测试用例,确保代码质量
-
性能监控:建立性能基准,持续优化
-
用户反馈:倾听用户需求,持续改进
-
技术演进:关注新技术发展,适时升级架构
WebSocket实时通信是构建现代应用的重要组成部分。在HarmonyOS平台上,充分利用其系统特性和性能优势,可以打造出体验优秀的实时应用。希望本指南能为您的开发工作提供有价值的参考。
技术之路,永无止境。愿您在鸿蒙生态中创造出更多精彩的应用!
本指南基于HarmonyOS最新版本编写,随着系统更新,部分API可能会有变化,请以官方最新文档为准。
祝您开发顺利,技术精进!🎯
更多推荐



所有评论(0)