Swift中WebSocket入门:从理论到高级实践
在现代移动开发中,实时交互已不仅是功能需求,更是必备能力。用户期望聊天、新闻推送、报价和通知能够即时更新,无需手动下拉刷新。WebSocket协议允许你在客户端(Swift应用程序)与服务器之间建立永久双向连接。
本文将超越简单的"Hello, World!"案例,深入探讨Swift中WebSocket的高级应用技术,适用于iOS和macOS平台。你将学习如何建立可靠连接、处理复杂场景(连接断开、重连、后台模式),并构建能够承受真实证券交易所应用或数千用户聊天负载的架构。
我们将同时介绍原生URLSessionWebSocketTask(iOS 13+)和适用于旧版本系统的流行库Starscream。所有代码均经过完整功能验证,可直接集成到项目中。
1. 高级Swift WebSocket客户端配置
让我们从基础开始。不同于简单的连接,我们将创建一个管理器来管理连接生命周期、处理错误,并支持自动重连功能。
1.1 带自动重连的管理器
实现一个使用URLSessionWebSocketTask的WebSocketManager类。其核心特性是采用指数退避策略控制重连间隔,避免服务器过载。
import Foundation
final class WebSocketManager: NSObject { private var webSocketTask: URLSessionWebSocketTask? private var session: URLSession? private var isConnected = false private var reconnectAttempt = 0 private let maxReconnectDelay: TimeInterval = 30 // 最大延迟30秒 private let baseDelay: TimeInterval = 2 // 基础延迟2秒 var onMessageReceived: ((String) -> Void)? var onConnectionStateChanged: ((Bool) -> Void)? func connect(url: URL) { session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue()) let request = URLRequest(url: url, timeoutInterval: 10) webSocketTask = session?.webSocketTask(with: request) webSocketTask?.resume() receiveMessage() } private func reconnect() { guard let url = webSocketTask?.originalRequest?.url else { return } let delay = min(baseDelay * pow(2.0, Double(reconnectAttempt)), maxReconnectDelay) print("将在 \\(delay) 秒后重连... (第\\(reconnectAttempt + 1)次尝试)") DispatchQueue.global().asyncAfter(deadline: .now() + delay) { [weak self] in self?.connect(url: url) self?.reconnectAttempt += 1 } } private func receiveMessage() { webSocketTask?.receive { [weak self] result in switch result { case .success(let message): switch message { case .string(let text): self?.onMessageReceived?(text) case .data(let data): if let text = String(data: data, encoding: .utf8) { self?.onMessageReceived?(text) } @unknown default: break } self?.reconnectAttempt = 0 // 成功接收后重置计数器 self?.receiveMessage() // 继续监听 case .failure(let error): print("接收错误: \\(error.localizedDescription)") self?.disconnect() self?.reconnect() } } } func send(text: String) {