Last active
June 26, 2023 16:03
-
-
Save VaslD/2a643081a40dd736da069fc6fb00d02c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Network | |
/// TCP 监听(服务端) | |
public final class TCPServer: Sendable { | |
let queue: DispatchQueue | |
let listener: NWListener | |
public init(port: Int) throws { | |
guard let unsigned = UInt16(exactly: port), let port = NWEndpoint.Port(rawValue: unsigned) else { | |
throw NWError.posix(POSIXErrorCode.EADDRNOTAVAIL) | |
} | |
self.queue = DispatchQueue(label: "NWListener (:\(port))") | |
self.listener = try NWListener(using: .tcp, on: port) | |
} | |
public var state: NWListener.State { | |
self.listener.state | |
} | |
public var service: NWListener.Service? { | |
get { self.listener.service } | |
set { self.listener.service = newValue } | |
} | |
/// 开始监听 | |
/// | |
/// 此方法需要 `await`,将在监听配置成功或异常后返回。当监听正在配置时,继续(并发)调用此方法将立即导致 `CancellationError` | |
/// 错误。如果监听已开始,调用此方法无效。 | |
public func start() async throws { | |
guard self.listener.stateUpdateHandler == nil else { | |
throw CancellationError() | |
} | |
guard self.listener.state != .ready else { | |
return | |
} | |
try await withUnsafeThrowingContinuation { continuation in | |
self.listener.stateUpdateHandler = { | |
switch $0 { | |
case .ready: | |
self.listener.stateUpdateHandler = nil | |
continuation.resume() | |
case let .failed(error): | |
self.listener.stateUpdateHandler = nil | |
continuation.resume(throwing: error) | |
case .cancelled: | |
self.listener.stateUpdateHandler = nil | |
continuation.resume(throwing: CancellationError()) | |
case let .waiting(error): | |
self.listener.stateUpdateHandler = nil | |
self.listener.cancel() | |
continuation.resume(throwing: error) | |
default: | |
break | |
} | |
} | |
self.listener.start(queue: self.queue) | |
} | |
} | |
/// 停止监听 | |
/// | |
/// 此方法不会使得 ``clients`` 的异步流提前或正常终止。如果代码正在并发枚举客户端,请自行处理。 | |
public func stop() { | |
self.listener.cancel() | |
} | |
/// 获取请求连接客户端的异步流 | |
/// | |
/// 由于 TCP 是全双工通讯,异步流中的客户端已连接到服务端 (SYN)、但服务端尚未回应 (ACK)。如果希望接受连接,请调用 | |
/// ``TCPConnection/connect()`` 完成握手;如果希望拒绝连接,请调用 ``TCPConnection/disconnect()`` | |
/// 取消握手;如果长时间不执行任何操作,客户端连接请求将会超时。 | |
/// | |
/// 每次枚举异步流只会获得新的连接请求对应的客户端。如需重复与某个客户端通讯,请另行留存 ``TCPClient``、不可期待多次枚举返回相同元素。 | |
public var clients: AsyncStream<TCPClient>! { | |
guard self.listener.newConnectionHandler == nil else { | |
return nil | |
} | |
return AsyncStream { stream in | |
stream.onTermination = { _ in | |
self.listener.newConnectionHandler = nil | |
} | |
self.listener.newConnectionHandler = { | |
stream.yield(TCPClient($0)) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment