跳到主要内容

设计 IM 客户端

问题

如何设计一个 iOS IM 即时通讯客户端?

答案

架构

消息模型

struct Message: Codable {
let messageId: String
let chatId: String
let senderId: String
let content: MessageContent
let timestamp: TimeInterval
var status: MessageStatus // sending / sent / delivered / read / failed
}

enum MessageContent: Codable {
case text(String)
case image(url: String, width: Int, height: Int)
case voice(url: String, duration: Int)
}

WebSocket 连接管理

class IMConnection {
private var webSocket: URLSessionWebSocketTask?
private var heartbeatTimer: Timer?

func connect() {
let session = URLSession(configuration: .default)
webSocket = session.webSocketTask(with: URL(string: "wss://im.example.com")!)
webSocket?.resume()
startHeartbeat()
receiveMessage()
}

private func startHeartbeat() {
heartbeatTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in
self?.send(type: "ping")
}
}

// 自动重连(指数退避)
private var retryCount = 0
func reconnect() {
let delay = min(pow(2.0, Double(retryCount)), 60)
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
self?.retryCount += 1
self?.connect()
}
}
}

关键设计点

设计点方案
消息有序服务端分配递增序列号
消息 ACK发送→服务端ACK→对端ACK
离线消息上线后拉取未读消息
已读回执批量上报已读位置
消息缓存Core Data + NSFetchedResultsController

常见面试问题

Q1: 如何保证消息不丢不重?

答案:客户端生成唯一消息 ID(UUID),服务端去重。发送后等待 ACK,超时重发。服务端维护每个会话的消息序列号,客户端拉取时比对序列号补齐缺失消息。

相关链接