一、技术概述

前端实现即时通讯的主要技术方案包括:

  1. 传统轮询(Polling)
  2. 长轮询(Long Polling)
  3. WebSocket
  4. Server-Sent Events (SSE)
  5. Socket.IO

二、具体实现

1. 传统轮询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Polling {
constructor(url, interval = 3000) {
this.url = url;
this.interval = interval;
this.timer = null;
}

start() {
this.timer = setInterval(async () => {
try {
const response = await fetch(this.url);
const data = await response.json();
this.handleMessage(data);
} catch (error) {
console.error('Polling error:', error);
}
}, this.interval);
}

stop() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}

handleMessage(data) {
// 处理接收到的消息
console.log('Received:', data);
}
}

// 使用示例
const polling = new Polling('/api/messages');
polling.start();

2. 长轮询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class LongPolling {
constructor(url) {
this.url = url;
this.isPolling = false;
}

async start() {
this.isPolling = true;
while (this.isPolling) {
try {
const response = await fetch(this.url, {
timeout: 30000 // 30秒超时
});

if (response.status === 200) {
const data = await response.json();
this.handleMessage(data);
}
} catch (error) {
console.error('Long polling error:', error);
// 错误后等待一段时间再重试
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
}

stop() {
this.isPolling = false;
}

handleMessage(data) {
console.log('Received:', data);
}
}

3. WebSocket

3.1 基础实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class WebSocketClient {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectInterval = 3000;
}

connect() {
try {
this.ws = new WebSocket(this.url);
this.bindEvents();
} catch (error) {
console.error('WebSocket connection error:', error);
this.reconnect();
}
}

bindEvents() {
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
};

this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};

this.ws.onclose = () => {
console.log('WebSocket closed');
this.reconnect();
};

this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}

reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`Reconnecting... Attempt ${this.reconnectAttempts}`);
this.connect();
}, this.reconnectInterval);
}
}

send(message) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
} else {
console.error('WebSocket is not connected');
}
}

close() {
if (this.ws) {
this.ws.close();
}
}

handleMessage(data) {
console.log('Received:', data);
}
}

3.2 心跳检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class HeartbeatWebSocket extends WebSocketClient {
constructor(url) {
super(url);
this.heartbeatInterval = 30000; // 30秒
this.heartbeatTimer = null;
}

startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
this.send({ type: 'ping' });
}, this.heartbeatInterval);
}

stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}

bindEvents() {
super.bindEvents();

this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
this.startHeartbeat();
};

this.ws.onclose = () => {
console.log('WebSocket closed');
this.stopHeartbeat();
this.reconnect();
};
}
}

4. Server-Sent Events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class SSEClient {
constructor(url) {
this.url = url;
this.eventSource = null;
}

connect() {
this.eventSource = new EventSource(this.url);

this.eventSource.onopen = () => {
console.log('SSE connected');
};

this.eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};

this.eventSource.onerror = (error) => {
console.error('SSE error:', error);
this.eventSource.close();
// 可以在这里实现重连逻辑
};
}

close() {
if (this.eventSource) {
this.eventSource.close();
}
}

handleMessage(data) {
console.log('Received:', data);
}
}

5. Socket.IO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import io from 'socket.io-client';

class SocketIOClient {
constructor(url) {
this.socket = io(url, {
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 3000
});

this.bindEvents();
}

bindEvents() {
this.socket.on('connect', () => {
console.log('Socket.IO connected');
});

this.socket.on('disconnect', () => {
console.log('Socket.IO disconnected');
});

this.socket.on('error', (error) => {
console.error('Socket.IO error:', error);
});

// 自定义事件监听
this.socket.on('message', (data) => {
this.handleMessage(data);
});
}

send(event, data) {
this.socket.emit(event, data);
}

close() {
this.socket.close();
}

handleMessage(data) {
console.log('Received:', data);
}
}

三、技术对比

1. 性能对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class PerformanceTest {
static async runTests() {
const results = {
polling: await this.testPolling(),
longPolling: await this.testLongPolling(),
webSocket: await this.testWebSocket(),
sse: await this.testSSE()
};

console.table(results);
}

static async testPolling() {
const startTime = performance.now();
// 测试代码...
const endTime = performance.now();

return {
latency: endTime - startTime,
bandwidth: '高',
serverLoad: '高',
realtime: '低'
};
}

// 其他测试方法...
}

2. 应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RealtimeStrategy {
static getRecommendedTechnology(requirements) {
const {
messageFrequency,
userCount,
browserSupport,
bidirectional
} = requirements;

if (bidirectional && browserSupport.webSocket) {
return 'WebSocket';
}

if (!bidirectional && browserSupport.sse) {
return 'SSE';
}

if (messageFrequency === 'high') {
return 'Long Polling';
}

return 'Traditional Polling';
}
}

四、最佳实践

1. 消息可靠性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ReliableMessaging {
constructor() {
this.messageQueue = new Map();
this.messageId = 0;
}

send(message) {
const id = this.messageId++;
this.messageQueue.set(id, message);

this.sendWithRetry(id, message);
}

async sendWithRetry(id, message, retries = 3) {
try {
await this.doSend(message);
this.messageQueue.delete(id);
} catch (error) {
if (retries > 0) {
setTimeout(() => {
this.sendWithRetry(id, message, retries - 1);
}, 1000);
}
}
}
}

2. 断线重连

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ReconnectionManager {
constructor() {
this.maxRetries = 5;
this.retryDelay = 3000;
this.exponentialFactor = 2;
}

async reconnect(connectFn) {
let retries = 0;
let delay = this.retryDelay;

while (retries < this.maxRetries) {
try {
await connectFn();
return true;
} catch (error) {
retries++;
console.log(`Reconnection attempt ${retries} failed`);

if (retries < this.maxRetries) {
await new Promise(resolve => setTimeout(resolve, delay));
delay *= this.exponentialFactor;
}
}
}

return false;
}
}

3. 消息压缩

1
2
3
4
5
6
7
8
9
10
class MessageCompression {
static compress(message) {
// 使用 MessagePack 或其他压缩算法
return msgpack.encode(message);
}

static decompress(data) {
return msgpack.decode(data);
}
}

参考文献