QSLSocketManager.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //
  2. // QSLSocketManager.swift
  3. // QuickSearchLocation
  4. //
  5. // Created by Destiny on 2024/12/6.
  6. //
  7. import SocketRocket
  8. import Foundation
  9. enum QSLSocketStatus: UInt {
  10. case connecting // 正在连接
  11. case connected // 已连接
  12. case failed // 失败
  13. case closedByServer // 系统关闭
  14. case closedByUser // 用户关闭
  15. case received // 接收消息
  16. }
  17. protocol QSLSocketManagerDelegate: AnyObject {
  18. func socketDidReceiveMessage(with string: String)
  19. }
  20. class QSLSocketManager: NSObject, SRWebSocketDelegate {
  21. static let shared = QSLSocketManager()
  22. private var webSocket: SRWebSocket?
  23. private var timer: Timer?
  24. private var pingTimer: Timer? // 每10秒钟发送一次ping消息
  25. private var currentCount: UInt = 0 // 当前重连次数
  26. var delegate: QSLSocketManagerDelegate?
  27. var urlString: String = ""
  28. var overtime: TimeInterval = 3.0 // 重连时间间隔,默认3秒钟
  29. var reconnectCount: UInt = UInt.max // 重连次数,默认无限次
  30. var status: QSLSocketStatus = .connecting
  31. private override init() {
  32. super.init()
  33. let url = "\(QSLApi.prodWSUrl)/websocket/\(QSLBaseManager.shared.userModel.authToken)"
  34. self.urlString = url
  35. }
  36. // MARK: - Public Methods
  37. @objc func connect() {
  38. // 先关闭连接
  39. self.webSocket?.close()
  40. self.webSocket?.delegate = nil
  41. // 后开启连接
  42. if let url = URL(string: self.urlString) {
  43. self.webSocket = SRWebSocket(urlRequest: URLRequest(url: url))
  44. self.webSocket?.delegate = self
  45. }
  46. self.status = .connecting
  47. self.webSocket?.open()
  48. }
  49. func close() {
  50. self.webSocket?.close()
  51. self.webSocket = nil
  52. self.timer?.invalidate()
  53. self.timer = nil
  54. self.pingTimer?.invalidate()
  55. self.pingTimer = nil
  56. }
  57. func reconnect() {
  58. guard currentCount < reconnectCount else {
  59. print("重连次数已用完……")
  60. self.timer?.invalidate()
  61. self.timer = nil
  62. return
  63. }
  64. // 计数器 +1
  65. currentCount += 1
  66. print("\(overtime)秒后进行第\(currentCount)次重试连接……")
  67. // 开启定时器进行重连
  68. self.timer = Timer.scheduledTimer(timeInterval: overtime, target: self, selector: #selector(connect), userInfo: nil, repeats: false)
  69. RunLoop.current.add(self.timer!, forMode: .common)
  70. }
  71. func sendMessage(_ message: String) {
  72. do {
  73. try self.webSocket?.send(string: message)
  74. ///判断要不要弹引导用户去评价
  75. QSLGuideusersToCommentManager.commentShare.manageWhetherTriggerPopUpWindow(QSLGuideusersToCommentType.nonMember)
  76. print("消息已发送")
  77. } catch {
  78. print("发送消息失败!")
  79. }
  80. }
  81. @objc func sendPingMessage() {
  82. guard status == .connected else { return }
  83. do {
  84. try self.webSocket?.sendPing(nil)
  85. print("发送心跳包")
  86. } catch {
  87. print("发送心跳包失败!")
  88. }
  89. }
  90. // MARK: - SRWebSocketDelegate Methods
  91. func webSocket(_ webSocket: SRWebSocket, didReceiveMessageWith string: String) {
  92. delegate?.socketDidReceiveMessage(with: string)
  93. let locationMessage = QSLMapMessageModel.mapModel(from: string)
  94. switch locationMessage.cmd {
  95. case "d.refresh.friend.list":
  96. NotificationCenter.default.post(name: QSLNotification.QSLRefreshFriend, object: nil)
  97. break
  98. case "d.refresh.friend.message":
  99. NotificationCenter.default.post(name: QSLNotification.QSLRefreshMessage, object: nil)
  100. break
  101. case "d.refresh.friend.request":
  102. NotificationCenter.default.post(name: QSLNotification.QSLRefreshRequest, object: nil)
  103. break
  104. case "d.refresh.contact":
  105. NotificationCenter.default.post(name: QSLNotification.QSLRefreshContact, object: nil)
  106. break
  107. case "d.refresh.member":
  108. NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil)
  109. break
  110. default:
  111. break
  112. }
  113. print("收到信息:\(string)")
  114. }
  115. func webSocketDidOpen(_ webSocket: SRWebSocket) {
  116. print("已链接服务器:\(webSocket.url ?? URL(fileURLWithPath: ""))")
  117. // 重置计数器
  118. self.currentCount = 0
  119. self.status = .connected
  120. // 开启ping定时器
  121. self.pingTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(sendPingMessage), userInfo: nil, repeats: true)
  122. RunLoop.current.add(self.pingTimer!, forMode: .common)
  123. }
  124. func webSocket(_ webSocket: SRWebSocket, didFailWithError error: Error) {
  125. print("链接失败:\(error.localizedDescription)")
  126. self.status = .failed
  127. // 尝试重新连接
  128. reconnect()
  129. }
  130. func webSocket(_ webSocket: SRWebSocket, didCloseWithCode code: Int, reason: String?, wasClean: Bool) {
  131. print("链接已关闭:code:\(code) reason:\(String(describing: reason))")
  132. if code == 1000 {
  133. self.status = .closedByUser
  134. } else {
  135. self.status = .closedByServer
  136. reconnect()
  137. }
  138. }
  139. func webSocket(_ webSocket: SRWebSocket, didReceivePingWith data: Data?) {
  140. print("收到 Ping")
  141. }
  142. func webSocket(_ webSocket: SRWebSocket, didReceivePong pongData: Data?) {
  143. print("收到 Pong")
  144. }
  145. }