在 Swift 中实现稳健的 WebSocket 实时通信

在当今的互联世界中,实时通信对许多应用都至关重要。WebSockets 为客户端和服务器之间的双向、全双工通信提供了强大的解决方案。在本文中,我们将探讨如何在 Swift 中稳健地实现 WebSocket 处理程序,该程序专为 iOS 应用程序而设计。

WebSocketHandler 协议

我们先来看看 WebSocketHandler 协议:

public protocol WebSocketHandler {
  func connect(request: NetworkRequest, completion: @escaping (Result<Void, EngineError.WebSocketError>) -> Void)
  func disconnect(completion: @escaping (Result<Void, EngineError.WebSocketError>) -> Void)
  func send<T>(message: T, completion: @escaping (Result<Void, EngineError.WebSocketError>) -> Void)  where T : Encodable
  func receive<T>(completion: @escaping (Result<T, EngineError.WebSocketError>) -> Void)  where T : Decodable
}

该协议定义了WebSocket通信的四种基本方法:

  1. connect:建立 WebSocket 连接
  2. disconnect:关闭 WebSocket 连接
  3. send:通过 WebSocket 发送消息
  4. receive:从 WebSocket 接收消息

每种方法都使用一个具有Result类型的完成处理程序,以便进行适当的错误处理。

DefaultWebSocketHandler 实现

现在,让我们深入了解 DefaultWebSocketHandler 类,它实现了 WebSocketHandler 协议:

public  class  DefaultWebSocketHandler : WebSocketHandler { 
  private  var webSocketTask: URLSessionWebSocketTask ? 
  private  let logger: Logger 
  private  let responseHandler: HTTPResponseHandler 
  
  public  init ( logger : Logger  =  DefaultLogger .shared, 
              responseHandler : HTTPResponseHandler  =  DefaultHTTPResponseHandler ()) { 
    self .logger = logger 
    self .responseHandler = responseHandler 
  } 
  
  // ... (协议方法的实现)
 }

该类使用 URLSessionWebSocketTask 管理 WebSocket 连接。它还包含一个用于调试的日志记录器和一个用于解码接收到的消息的响应处理程序。

连接到 WebSocket

connect 方法用于建立 WebSocket 连接:

public func connect(request: NetworkRequest, completion: @escaping (Result<Void, EngineError.WebSocketError>) -> Void) {
  guard webSocketTask == nil else {
    completion(.failure(.connectionFailed("WebSocket is already connected")))
    return
  }
  
  let urlSession = URLSession(configuration:.default, delegate: nil, delegateQueue: OperationQueue())
  do {
    let urlRequest = try request.urlRequest()
    webSocketTask = urlSession.webSocketTask(with: urlRequest)
    webSocketTask?.resume()
    
    // 连接检查
    receive { [weak self] result in
      switch result {
      case.success:
        self?.logger.log(file: #file, function: #function, line: #line, logLevel:.info, "WebSocket connected")
        completion(.success(()))
      case.failure(let error):
        self?.logger.log(file: #file, function: #function, line: #line, logLevel:.error, error)
        completion(.failure(error))
      }
    }
  } catch {
    completion(.failure(.connectionFailed(error.localizedDescription)))
  }
}

该方法创建一个 URLSessionWebSocketTask 并尝试连接。然后,它将执行简单的连接检查,尝试接收一条消息,确认连接成功或失败。

发送消息

send方法通过 WebSocket 编码和发送消息:

public func send<T>(message: T, completion: @escaping (Result<Void, EngineError.WebSocketError>) -> Void) where T: Encodable {
  guard let webSocketTask = webSocketTask else {
    completion(.failure(.messageSendFailed("WebSocket is not connected")))
    return
  }
  
  do {
    let data = try JSONEncoder().encode(message)
    let message = URLSessionWebSocketTask.Message.data(data)
    webSocketTask.send(message) { [weak self] error in
      if let error = error {
        self?.logger.log(file: #file, function: #function, line: #line, logLevel:.error, error)
        completion(.failure(.messageSendFailed(error.localizedDescription)))
      } else {
        self?.logger.log(file: #file, function: #function, line: #line, logLevel:.info, "WebSocket message sent")
        completion(.success(()))
      }
    }
  } catch {
    let error = EngineError.WebSocketError.messageSendFailed("Failed to encode message")
    logger.log(file: #file, function: #function, line: #line, logLevel:.error, error)
    completion(.failure(error))
  }
}

该方法首先检查 WebSocket 是否已连接,然后对信息进行编码,并使用 webSocketTask.send 方法发送信息。

接收信息

receive 方法处理接收到的信息:

public func receive<T>(completion: @escaping (Result<T, EngineError.WebSocketError>) -> Void) where T: Decodable {
  guard let webSocketTask = webSocketTask else {
    completion(.failure(.messageReceiveFailed("WebSocket is not connected")))
    return
  }
  
  webSocketTask.receive { [weak self] result in
    switch result {
    case .success(let message):
      switch message {
      case .data(let data):
        do {
          let typedMessage = try self?.responseHandler.decode(data: data, to: T.self)
          self?.logger.log(file: #file, function: #function, line: #line, logLevel: .info, "WebSocket message received")
          if let typedMessage = typedMessage {
            completion(.success(typedMessage))
          } else {
            let error = EngineError.WebSocketError.messageReceiveFailed("Failed to decode received message")
            self?.logger.log(file: #file, function: #function, line: #line, logLevel: .error, error)
            completion(.failure(error))
          }
        } catch {
          let error = EngineError.WebSocketError.messageReceiveFailed("Failed to decode received message: \(error.localizedDescription)")
          self?.logger.log(file: #file, function: #function, line: #line, logLevel: .error, error)
          completion(.failure(error))
        }
      case .string(let string):
        let error = EngineError.WebSocketError.messageReceiveFailed("Received unexpected string message: \(string)")
        self?.logger.log(file: #file, function: #function, line: #line, logLevel: .error, error)
        completion(.failure(error))
      @unknown default:
        let error = EngineError.WebSocketError.messageReceiveFailed("Received unknown message type")
        self?.logger.log(file: #file, function: #function, line: #line, logLevel: .error, error)
        completion(.failure(error))
      }
    case .failure(let error):
      self?.logger.log(file: #file, function: #function, line: #line, logLevel: .error, error)
      completion(.failure(.messageReceiveFailed(error.localizedDescription)))
    }
  }
}

该方法从 WebSocket 接收信息,使用 responseHandler 对信息进行解码,并通过完成处理程序返回结果。

断开 WebSocket 连接

disconnect 方法会断开 WebSocket 连接:

public func disconnect(completion: @escaping (Result<Void, EngineError.WebSocketError>) -> Void) {
    guard let webSocketTask = webSocketTask else {
      completion(.failure(.connectionFailed("WebSocket is not connected")))
      return
    }
    
    webSocketTask.cancel(with:.goingAway, reason: nil)
    self.webSocketTask = nil
    logger.log(file: #file, function: #function, line: #line, logLevel:.info, "WebSocket disconnected")
    completion(.success(()))
  }

本例将演示连接 WebSocket、发送消息和接收消息。

import Foundation

// 定义一个简单的消息结构
struct ChatMessage: Codable {
    let sender: String
    let content: String
}

class ChatViewModel {
    private let webSocketHandler: WebSocketHandler
    
    init(webSocketHandler: WebSocketHandler = DefaultWebSocketHandler()) {
        self.webSocketHandler = webSocketHandler
    }
    
    func connectToChat() {
        // C为 WebSocket 连接创建网络请求
        let request = NetworkRequest(url: URL(string: "wss://example.com/chat")!)
        
        webSocketHandler.connect(request: request) { result in
            switch result {
            case .success:
                print("Connected to chat successfully")
                self.startListeningForMessages()
            case .failure(let error):
                print("Failed to connect to chat: \(error)")
            }
        }
    }
    
    func sendMessage(_ message: String) {
        let chatMessage = ChatMessage(sender: "User", content: message)
        
        webSocketHandler.send(message: chatMessage) { result in
            switch result {
            case .success:
                print("Message sent successfully")
            case .failure(let error):
                print("Failed to send message: \(error)")
            }
        }
    }
    
    private func startListeningForMessages() {
        self.receiveMessage()
    }
    
    private func receiveMessage() {
        webSocketHandler.receive { [weak self] (result: Result<ChatMessage, EngineError.WebSocketError>) in
            switch result {
            case .success(let message):
                print("Received message: \(message.sender): \(message.content)")
                //  处理收到的消息
                
                // 继续监听下一条消息
                self?.receiveMessage()
            case .failure(let error):
                print("Error receiving message: \(error)")
                // 处理错误(例如,尝试重新连接)
            }
        }
    }
    
    func disconnect() {
        webSocketHandler.disconnect { result in
            switch result {
            case .success:
                print("Disconnected from chat successfully")
            case .failure(let error):
                print("Failed to disconnect from chat: \(error)")
            }
        }
    }
}

// 使用示例
let chatViewModel = ChatViewModel()

// 连接到聊天
chatViewModel.connectToChat()

// 发送消息
chatViewModel.sendMessage("Hello, everyone!")

// 断开连接
chatViewModel.disconnect()

在本例中,我们创建了一个 ChatViewModel 类,该类利用 DefaultWebSocketHandler 管理聊天连接。下面是主要组件的分解:

  • ChatMessage 结构: 代表聊天信息的简单结构,符合 Codable 标准,便于编码和解码。
  • ChatViewModel 类: 该类使用 WebSocketHandler 封装了聊天功能。
  • connectToChat(): 建立与 WebSocket 聊天服务器的连接。
  • sendMessage(_:): 通过 WebSocket 发送聊天信息。
  • startListeningForMessages()receiveMessage(): 这些方法会建立一个连续的循环来接收接收到的信息。
  • disconnect():断开连接 当聊天会话结束时关闭 WebSocket 连接。

此 WebSocket 处理器实现为 Swift 应用程序中的实时通信提供了稳健的基础。它提供了错误处理、日志记录以及对可编码和可解码消息类型的支持。通过使用此处理程序,开发人员可以轻松地将 WebSocket 功能集成到他们的 iOS 应用程序中,从而启用实时功能并增强用户体验。

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/im/50083.html

(0)

相关推荐

发表回复

登录后才能评论