You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 10:25:39