URLSessionWebSocket未调用didClose代理方法如何解决?
URLSessionWebSocket无法感知服务器意外断开的解决方法
当你用wscat --listen 3000 --slash启动测试服务器,按下Command + C强制关闭时,服务器不会发送标准的WebSocket关闭帧,而是直接终止TCP连接。这种情况下,urlSession(_:webSocketTask:didCloseWith:reason:)代理方法不会被触发——该方法仅在双方通过WebSocket协议正常关闭连接时才会调用。
要捕获这种意外断开的情况,你需要做以下两点:
1. 实现持续接收消息的逻辑
URLSessionWebSocketTask的receive()方法是单次调用,仅接收一条消息。如果想要持续监听连接状态,需要在每次接收完成后递归调用receive(),这样当TCP连接断开时,下一次的receive()会返回错误,从而感知到连接中断。
2. 正确处理任务完成的错误
当TCP连接意外断开时,urlSession(_:task:didCompleteWithError:)代理方法会被触发并携带对应错误信息,你可以在这里处理连接断开后的逻辑。
修改后的完整代码
class WebSocketManager: NSObject, URLSessionTaskDelegate, URLSessionWebSocketDelegate { private var urlSession: URLSession! var webSocketTask: URLSessionWebSocketTask? func run() { urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let url = URL(string: "ws://127.0.0.1:3000")! let urlRequest = URLRequest(url: url) webSocketTask = urlSession.webSocketTask(with: urlRequest) webSocketTask!.resume() // 启动持续接收 startReceiving() } func send() { guard let task = webSocketTask else { return } task.send(URLSessionWebSocketTask.Message.string("Hello from Client"), completionHandler: { error in if let error = error { print("Error sending message: \(error)") // 发送失败时检测连接状态 self.handleConnectionError(error) } else { print("Sent message") } }) } // 持续接收消息的方法 private func startReceiving() { webSocketTask?.receive(completionHandler: { [weak self] result in guard let self = self else { return } switch result { case .failure(let error): print("Error receiving message: \(error)") self.handleConnectionError(error) case .success(let message): switch message { case .string(let text): print("Received: \(text)") case .data(let data): print("Received binary message: \(data)") @unknown default: fatalError() } // 递归调用,保持持续接收 self.startReceiving() } }) } // 统一处理连接错误 private func handleConnectionError(_ error: Error) { print("连接已断开: \(error.localizedDescription)") // 这里可以添加重新连接逻辑或者通知上层业务 self.cleanupTask() } // 清理任务 private func cleanupTask() { webSocketTask?.cancel(with: .goingAway, reason: "连接断开".data(using: .utf8)) webSocketTask = nil } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let error = error { print("任务完成错误: \(error)") handleConnectionError(error) } else { print("任务正常完成") } } func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { if let error = error { print("Session失效: \(error)") handleConnectionError(error) } } func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { print("WebSocket正常关闭,code: \(closeCode)") cleanupTask() } func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { print("连接成功,协议: \(`protocol` ?? "无")") } }
关键改动说明
- 添加了
startReceiving()方法,通过递归调用保持持续监听消息,一旦连接断开,receive()会返回错误并进入handleConnectionError处理断开逻辑。 - 新增
handleConnectionError()方法统一处理各种连接错误场景(接收失败、发送失败、任务完成错误)。 - 新增
cleanupTask()方法用于清理断开后的WebSocket任务,避免资源泄漏。 - 在
send()方法中添加错误处理,发送失败时同样触发连接错误处理。
这样修改后,当你按下Command + C关闭服务器时,客户端会通过receive()的错误回调或者didCompleteWithError代理方法感知到连接断开,并执行相应的处理逻辑。
内容的提问来源于stack exchange,提问作者Charlie Fish
相关产品推荐
相关产品推荐

