#swift #websocket #swift-nio
#swift #websocket #swift-nio
Вопрос:
Контекст
Я разрабатываю приложение для Mac. В этом приложении я хочу запустить сервер websocket. Для этого я использую Swift NIO и Websocket-Kit. Моя полная настройка приведена ниже.
Вопрос
Вся документация для Websocket-Kit и SwiftNIO направлена на создание единого серверного процесса, который запускается при запуске из командной строки, а затем выполняется бесконечно.
В моем приложении я должен иметь возможность запускать сервер websocket, а затем выключать его и перезапускать по требованию, без повторного запуска моего приложения. Приведенный ниже код делает это, но я хотел бы получить подтверждение двух вещей:
-
В
test()
функции я отправляю некоторый текст всем подключенным клиентам. Я не уверен, что это потокобезопасно и правильно. Могу ли я сохранитьWebSocket
экземпляры, как я делаю здесь, и отправить их из основного потока моего приложения? -
Правильно ли я завершаю работу сервера websocket? Результат вызова
serverBootstrap(group:)[...].bind(host:port:).wait()
создает aChannel
, а затем ожидает бесконечно. Когда я вызываюshutdownGracefully()
связанныйEventLoopGroup
, правильно ли очищен этот сервер? (Я могу подтвердить, что порт 5759 снова свободен после этого выключения, поэтому я предполагаю, что все очищено?)
Спасибо за информацию; сложно найти примеры использования SwiftNIO и Websocket-Kit внутри приложения.
Код
import Foundation
import NIO
import NIOHTTP1
import NIOWebSocket
import WebSocketKit
@objc class WebsocketServer: NSObject
{
private var queue: DispatchQueue?
private var eventLoopGroup: MultiThreadedEventLoopGroup?
private var websocketClients: [WebSocket] = []
@objc func startServer()
{
queue = DispatchQueue.init(label: "socketServer")
queue?.async
{
let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel, req in
WebSocket.server(on: channel) { ws in
ws.send("You have connected to WebSocket")
DispatchQueue.main.async {
self.websocketClients.append(ws)
print("websocketClients after connection: (self.websocketClients)")
}
ws.onText { ws, string in
print("received")
ws.send(string.trimmingCharacters(in: .whitespacesAndNewlines).reversed())
}
ws.onBinary { ws, buffer in
print(buffer)
}
ws.onClose.whenSuccess { value in
print("onClose")
DispatchQueue.main.async
{
self.websocketClients.removeAll { (socketToTest) -> Bool in
return socketToTest === ws
}
print("websocketClients after close: (self.websocketClients)")
}
}
}
}
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let port: Int = 5759
let promise = self.eventLoopGroup!.next().makePromise(of: String.self)
let server = try? ServerBootstrap(group: self.eventLoopGroup!)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { channel in
let webSocket = NIOWebSocketServerUpgrader(
shouldUpgrade: { channel, req in
return channel.eventLoop.makeSucceededFuture([:])
},
upgradePipelineHandler: upgradePipelineHandler
)
return channel.pipeline.configureHTTPServerPipeline(
withServerUpgrade: (
upgraders: [webSocket],
completionHandler: { ctx in
// complete
})
)
}.bind(host: "0.0.0.0", port: port).wait()
_ = try! promise.futureResult.wait()
}
}
///
/// Send a message to connected clients, then shut down the server.
///
@objc func test()
{
self.websocketClients.forEach { (ws) in
ws.eventLoop.execute {
ws.send("This is a message being sent to all websockets.")
}
}
stopServer()
}
@objc func stopServer()
{
self.websocketClients.forEach { (ws) in
try? ws.eventLoop.submit { () -> Void in
print("closing websocket: (ws)")
_ = ws.close()
}.wait() // Block until complete so we don't shut down the eventLoop before all clients get closed.
}
eventLoopGroup?.shutdownGracefully(queue: .main, { (error: Error?) in
print("Eventloop shutdown now complete.")
self.eventLoopGroup = nil
self.queue = nil
})
}
}
Ответ №1:
В функции test () я отправляю некоторый текст всем подключенным клиентам. Я не уверен, что это потокобезопасно и правильно. Могу ли я сохранить экземпляры WebSocket, как я делаю здесь, и отправить их из основного потока моего приложения?
Точно так же, как вы делаете здесь, да, это должно быть безопасно. ws.eventLoop.execute
выполнит этот блок в потоке цикла событий, принадлежащем этому соединению с WebSocket. Это будет безопасно.
Когда я вызываю shutdownGracefully() в связанной EventLoopGroup, правильно ли очищен этот сервер? (Я могу подтвердить, что порт 5759 снова свободен после этого выключения, поэтому я предполагаю, что все очищено?)
Да. shutdownGracefully
принудительно закрывает все соединения и прослушивающие сокеты.