fix
This commit is contained in:
parent
67110d4e60
commit
6de2bfff27
@ -9,9 +9,14 @@ import Network
|
|||||||
|
|
||||||
enum SDLIPV6AssistError: Error {
|
enum SDLIPV6AssistError: Error {
|
||||||
case lostConnection
|
case lostConnection
|
||||||
|
case requestTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
actor SDLIPV6AssistClient {
|
actor SDLIPV6AssistClient {
|
||||||
|
private struct PendingRequest {
|
||||||
|
let continuation: CheckedContinuation<SDLV6AssistProbeReply, Error>
|
||||||
|
let timeoutTask: Task<Void, Never>
|
||||||
|
}
|
||||||
|
|
||||||
private enum State {
|
private enum State {
|
||||||
case idle
|
case idle
|
||||||
@ -25,7 +30,7 @@ actor SDLIPV6AssistClient {
|
|||||||
private let assistServerAddress: NWEndpoint
|
private let assistServerAddress: NWEndpoint
|
||||||
|
|
||||||
private var packetId: UInt32 = 1
|
private var packetId: UInt32 = 1
|
||||||
private var pendingRequests: [UInt32: CheckedContinuation<SDLV6AssistProbeReply, Error>] = [:]
|
private var pendingRequests: [UInt32: PendingRequest] = [:]
|
||||||
|
|
||||||
// 用来处理关闭事件
|
// 用来处理关闭事件
|
||||||
private let closeStream: AsyncStream<Void>
|
private let closeStream: AsyncStream<Void>
|
||||||
@ -104,40 +109,52 @@ actor SDLIPV6AssistClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func probe() async throws -> SDLV6AssistProbeReply {
|
func probe(requestTimeout: Duration = .seconds(5)) async throws -> SDLV6AssistProbeReply {
|
||||||
guard case .running = self.state, let connection = self.connection, connection.state == .ready else {
|
guard case .running = self.state, let connection = self.connection, connection.state == .ready else {
|
||||||
throw SDLIPV6AssistError.lostConnection
|
throw SDLIPV6AssistError.lostConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
return try await withCheckedThrowingContinuation { cont in
|
|
||||||
let pktId = self.nextPacketId()
|
let pktId = self.nextPacketId()
|
||||||
|
let requestTimeout = self.requestTimeout
|
||||||
var assistProbe = SDLV6AssistProbe()
|
var assistProbe = SDLV6AssistProbe()
|
||||||
assistProbe.pktID = pktId
|
assistProbe.pktID = pktId
|
||||||
|
|
||||||
do {
|
|
||||||
let data = try assistProbe.serializedData()
|
let data = try assistProbe.serializedData()
|
||||||
|
|
||||||
|
return try await withCheckedThrowingContinuation { cont in
|
||||||
|
let timeoutTask = Task { [weak self] in
|
||||||
|
try? await Task.sleep(for: requestTimeout)
|
||||||
|
await self?.handleRequestTimeout(packetId: pktId)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pendingRequests[pktId] = .init(continuation: cont, timeoutTask: timeoutTask)
|
||||||
connection.send(content: data, completion: .contentProcessed { error in
|
connection.send(content: data, completion: .contentProcessed { error in
|
||||||
if let error {
|
if let error {
|
||||||
Task {
|
Task {
|
||||||
await self.handleProcesseError(packetId: pktId, error: error)
|
await self.handleProcessError(packetId: pktId, error: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.pendingRequests[pktId] = cont
|
|
||||||
} catch let err {
|
|
||||||
cont.resume(throwing: err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleProcesseError(packetId: UInt32, error: NWError) {
|
private func handleProcessError(packetId: UInt32, error: NWError) {
|
||||||
if let cont = self.pendingRequests.removeValue(forKey: packetId) {
|
if let request = self.takePendingRequest(packetId: packetId) {
|
||||||
cont.resume(throwing: error)
|
request.continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleRequestTimeout(packetId: UInt32) {
|
||||||
|
if let request = self.takePendingRequest(packetId: packetId) {
|
||||||
|
request.continuation.resume(throwing: SDLIPV6AssistError.requestTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
|
self.stop(pendingError: SDLIPV6AssistError.lostConnection)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stop(pendingError: any Error) {
|
||||||
guard self.state != .stopped else {
|
guard self.state != .stopped else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -147,6 +164,7 @@ actor SDLIPV6AssistClient {
|
|||||||
self.receiveTask = nil
|
self.receiveTask = nil
|
||||||
self.connection?.cancel()
|
self.connection?.cancel()
|
||||||
self.connection = nil
|
self.connection = nil
|
||||||
|
self.failAllPendingRequests(error: pendingError)
|
||||||
self.finishCloseStreamIfNeeded()
|
self.finishCloseStreamIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +179,7 @@ actor SDLIPV6AssistClient {
|
|||||||
self.startReceiveTask(for: connection)
|
self.startReceiveTask(for: connection)
|
||||||
case .failed(let error):
|
case .failed(let error):
|
||||||
SDLLogger.log("[SDLIPV6AssistClient] Connection failed: \(error)", for: .debug)
|
SDLLogger.log("[SDLIPV6AssistClient] Connection failed: \(error)", for: .debug)
|
||||||
self.stop()
|
self.stop(pendingError: error)
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
self.stop()
|
self.stop()
|
||||||
default:
|
default:
|
||||||
@ -191,8 +209,8 @@ actor SDLIPV6AssistClient {
|
|||||||
do {
|
do {
|
||||||
let packet = try SDLV6AssistProbeReply(serializedBytes: data)
|
let packet = try SDLV6AssistProbeReply(serializedBytes: data)
|
||||||
let pktId = packet.pktID
|
let pktId = packet.pktID
|
||||||
if let cont = self.pendingRequests.removeValue(forKey: pktId) {
|
if let request = self.takePendingRequest(packetId: pktId) {
|
||||||
cont.resume(returning: packet)
|
request.continuation.resume(returning: packet)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
SDLLogger.log("[SDLIPV6AssistClient] Receive error: \(error)", for: .debug)
|
SDLLogger.log("[SDLIPV6AssistClient] Receive error: \(error)", for: .debug)
|
||||||
@ -227,6 +245,25 @@ actor SDLIPV6AssistClient {
|
|||||||
return packetId
|
return packetId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func takePendingRequest(packetId: UInt32) -> PendingRequest? {
|
||||||
|
guard let request = self.pendingRequests.removeValue(forKey: packetId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
request.timeoutTask.cancel()
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
private func failAllPendingRequests(error: any Error) {
|
||||||
|
let pendingRequests = self.pendingRequests
|
||||||
|
self.pendingRequests.removeAll()
|
||||||
|
|
||||||
|
pendingRequests.values.forEach { request in
|
||||||
|
request.timeoutTask.cancel()
|
||||||
|
request.continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.connection?.cancel()
|
self.connection?.cancel()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user