繁体   English   中英

发送到已连接客户端的最佳实践

[英]Best practice for sending to connected clients

我正在尝试设计一个 SwiftNIO 服务器,其中多个客户端(如 2 个或 3 个)可以连接到服务器,并且在连接后,它们都可以从服务器接收信息。

为此,我创建了一个ServerHandler class,它被共享并添加到连接的客户端的每个管道中。

let group = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let handler = ServerHandler()
let bootstrap = ServerBootstrap(group: group)
    .serverChannelOption(ChannelOptions.backlog, value: 2)
    .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
    .childChannelInitializer { $0.pipeline.addHandler(handler) }
    .childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)

上面的代码灵感来自https://github.com/apple/swift-nio/blob/main/Sources/NIOChatServer/main.swift

ServerHandler class 中,每当有新客户端连接时,该通道就会添加到数组中。 然后,当我准备好向所有客户端发送数据时,我只需遍历ServerHandler中的通道,然后调用writeAndFlush

这似乎工作得很好,但有几件事我担心:

  1. 似乎并不真正推荐创建共享处理程序,而是应该为每个客户端创建一个新处理程序。 但是,我将如何访问需要向其发送数据的所有客户端通道? (我在由 UI 确定的时间发送数据)
  2. 为什么Channel.write似乎什么也没做? 如果我在服务器中使用Channel.write而不是writeAndFlush ,我的客户端将无法接收任何数据。

如果这些问题很愚蠢,我深表歉意,我最近才开始使用SwiftNIO和一般网络。

如果有人能给我一些见解,那就太棒了。

你的问题一点都不傻!

  1. 是的,共享ChannelHandler可能算作“不推荐”。 但不是因为它不起作用,更多的是它不寻常,可能不是其他 NIO 程序员所期望的。 但是,如果您对此感到满意,那很好。 如果您足够高性能以至于您担心每个Channel的确切分配数量,那么您可以通过共享处理程序来节省一些。 但我真的不会过早地优化。

    如果您不想共享处理程序,那么您可以使用多个处理程序来共享对某种协调器 object 的引用。 不要误会我的意思,它实际上仍然是同一件事:跨多个网络连接的一个共享引用。 唯一真正的区别是测试可能更容易一些,并且对于其他 NIO 程序员来说可能感觉更自然。 (在任何情况下,都要小心确保所有这些Channel都在同一个EventLoop上或使用外部同步(比如说一个锁,从性能的角度来看这可能不是理想的)。

  2. write只是将一些要写入的数据排入队列 flush使 SwiftNIO 尝试发送所有先前写入的数据。 writeAndFlush只需调用write然后flush

    为什么 NIO 完全区分writeflush 在高性能网络应用程序中,最大的开销可能是系统调用开销。 为了通过 TCP 发送数据,SwiftNIO 必须进行系统调用( writewritevsend ......)。

    如果您忽略writeflush并始终使用writeAndFlush ,任何 SwiftNIO 程序都可以工作。 但是,如果网络跟上,那么每次writeAndFlush调用都会花费你一个系统调用。 然而,在许多情况下,使用 SwiftNIO 的库/应用程序已经知道它想要将多位数据排入队列以通过网络发送。 在那种情况下,连续说三个writeAndFlush会很浪费。 如果累积三位数据然后使用“向量写入”(例如writev系统调用)在一个系统调用中发送它们会更好。 如果你确实说writewritewriteflush ,这正是 SwiftNIO 会做的事情。 因此,三个写入都将使用一个writev系统调用发送。 SwiftNIO 将简单地获取指向数据位的三个指针并将它们交给 kernel,然后它会尝试通过网络发送它们。

    你可以更进一步。 假设您是一台高性能服务器,并且您想要响应大量传入请求。 您将通过channelRead从客户端获取您的请求。 如果您现在能够同步回复,您可以只为它们write响应(这将使它们排队)。 一旦获得channelReadComplete (这标志着“读取突发”的结束),您就可以flush 这将允许您仅使用一个writev系统调用在一次读取突发中响应尽可能多的请求。 在某些情况下,这可能是非常重要的优化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM