add ping pong

This commit is contained in:
cheykrym 2025-10-21 04:43:14 +03:00
parent f886386374
commit 41adedee40
2 changed files with 45 additions and 21 deletions

View File

@ -39,6 +39,7 @@ final class SocketService {
private var socket: SocketIOClient? private var socket: SocketIOClient?
private var heartbeatTimer: DispatchSourceTimer? private var heartbeatTimer: DispatchSourceTimer?
private var heartbeatAckInFlight = false private var heartbeatAckInFlight = false
private var lastHeartbeatSentAt: Date?
private var consecutiveHeartbeatMisses = 0 private var consecutiveHeartbeatMisses = 0
#endif #endif
@ -193,6 +194,10 @@ final class SocketService {
self?.handleHeartbeatSuccess() self?.handleHeartbeatSuccess()
} }
socket.on("message") { [weak self] data, _ in
self?.handleMessageEvent(data)
}
self.manager = manager self.manager = manager
self.socket = socket self.socket = socket
} }
@ -224,6 +229,7 @@ final class SocketService {
heartbeatTimer = nil heartbeatTimer = nil
heartbeatAckInFlight = false heartbeatAckInFlight = false
consecutiveHeartbeatMisses = 0 consecutiveHeartbeatMisses = 0
lastHeartbeatSentAt = nil
} }
private func performHeartbeatCheck() { private func performHeartbeatCheck() {
@ -246,43 +252,61 @@ final class SocketService {
} }
private func sendHeartbeat(on socket: SocketIOClient) { private func sendHeartbeat(on socket: SocketIOClient) {
guard !heartbeatAckInFlight else { return } if heartbeatAckInFlight {
heartbeatAckInFlight = true if let lastSentAt = lastHeartbeatSentAt,
Date().timeIntervalSince(lastSentAt) >= heartbeatTimeout {
socket.emitWithAck(heartbeatEventName, ["timestamp": Date().timeIntervalSince1970]) heartbeatAckInFlight = false
.timingOut(after: heartbeatTimeout) { [weak self] data in handleMissedHeartbeat(for: socket)
guard let self else { return } } else {
self.heartbeatAckInFlight = false return
if self.isSuccessfulHeartbeatResponse(data) {
self.handleHeartbeatSuccess()
} else {
self.handleMissedHeartbeat(for: socket)
}
} }
}
heartbeatAckInFlight = true
lastHeartbeatSentAt = Date()
socket.emit(heartbeatEventName, ["data": "ping"])
} }
private func isSuccessfulHeartbeatResponse(_ data: [Any]) -> Bool { private func handleMessageEvent(_ data: [Any]) {
guard !data.isEmpty else { return true } guard let payload = data.first else { return }
if let stringValue = data.first as? String, stringValue.uppercased() == "NO ACK" { let messageText: String?
return false
if let dictionary = payload as? [String: Any] {
if let nestedData = dictionary["data"] as? [String: Any],
let nestedMessage = nestedData["message"] as? String {
messageText = nestedMessage
} else {
messageText = dictionary["message"] as? String
}
} else if let stringValue = payload as? String {
messageText = stringValue
} else {
messageText = nil
} }
if let ackStatus = data.first as? SocketAckStatus, ackStatus == .noAck { guard let message = messageText?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() else {
return false return
} }
return true if message == "pong" {
handleHeartbeatSuccess()
}
} }
private func handleHeartbeatSuccess() { private func handleHeartbeatSuccess() {
consecutiveHeartbeatMisses = 0 consecutiveHeartbeatMisses = 0
heartbeatAckInFlight = false
lastHeartbeatSentAt = nil
updateConnectionState(.connected) updateConnectionState(.connected)
} }
private func handleMissedHeartbeat(for socket: SocketIOClient) { private func handleMissedHeartbeat(for socket: SocketIOClient) {
consecutiveHeartbeatMisses += 1 consecutiveHeartbeatMisses += 1
heartbeatAckInFlight = false
lastHeartbeatSentAt = nil
updateConnectionState(.connecting)
guard consecutiveHeartbeatMisses > maxHeartbeatMissCount else { guard consecutiveHeartbeatMisses > maxHeartbeatMissCount else {
return return
} }

View File

@ -6,7 +6,7 @@ struct AppConfig {
static let PROTOCOL = "https" static let PROTOCOL = "https"
static let API_SERVER = "\(PROTOCOL)://api.yobble.org" static let API_SERVER = "\(PROTOCOL)://api.yobble.org"
static let SOCKET_PATH = "/socket.io/" static let SOCKET_PATH = "/socket.io/"
static let SOCKET_HEARTBEAT_EVENT = "ping" static let SOCKET_HEARTBEAT_EVENT = "client_message"
static let USER_AGENT = "yobble ios" static let USER_AGENT = "yobble ios"
static let APP_BUILD = "appstore" // appstore / freestore static let APP_BUILD = "appstore" // appstore / freestore