首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Swift-NIO +WebSocket: Mac应用程序中的正确设置/清理

Swift-NIO +WebSocket: Mac应用程序中的正确设置/清理
EN

Stack Overflow用户
提问于 2020-11-16 05:11:59
回答 1查看 343关注 0票数 3

上下文

我正在开发一个Mac应用程序。在这个应用程序中,我想运行一个websocket服务器。为此,我使用了Swift和Websocket。我的完整设置在下面。

问题

Websocket和SwiftNIO的所有文档都面向创建一个服务器端进程,该进程在您从命令行启动它时启动,然后无限地运行。

在我的应用程序中,我必须能够启动websocket服务器,然后根据需要关闭它并重新启动它,而无需重新启动我的应用程序。下面的代码可以做到这一点,但我想确认两件事:

  1. test()函数中,我向所有连接的客户端发送一些文本。我不确定这是否是线程安全和正确。我可以像在这里一样存储WebSocket实例并从我的应用程序的主线程发送消息吗?

  1. 我是否正确地关闭了websocket服务器?调用serverBootstrap(group:)[...].bind(host:port:).wait()的结果会创建一个Channel,然后无限等待。当我在关联的shutdownGracefully()上调用EventLoopGroup时,该服务器是否被正确清理?(在关闭之后,我可以确认端口5759又是免费的,所以我猜一切都清理了吗?)

感谢您的输入;很难在应用程序中找到使用SwiftNIO和Websocket的例子。

代码

代码语言:javascript
复制
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
        })
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-16 10:24:46

在test()函数中,我向所有连接的客户端发送一些文本。我不确定这是否是线程安全和正确。我可以像在这里一样存储WebSocket实例并从我的应用程序的主线程发送消息吗?

就像你在这里做的一样,是的,那应该是安全的。ws.eventLoop.execute将在属于该WebSocket连接的事件循环线程上执行该块。这会很安全的。

当我在关联的EventLoopGroup上调用shutdownGracefully()时,服务器是否被正确清理?(我可以确认,在这次关闭之后,端口5759又是免费的,所以我猜所有的东西都清理干净了?)

是。shutdownGracefully强制关闭所有连接和侦听套接字。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64852971

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档