0

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

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

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该类中,每当有新客户端连接时,该通道就会添加到数组中。然后,当我准备好向所有客户端发送数据时,我只需遍历 , 中的通道ServerHandler并调用writeAndFlush.

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

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

如果这些问题很愚蠢,我深表歉意,我SwiftNIO最近才开始接触网络。

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

4

1 回答 1

0

你的问题一点都不傻!

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

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

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

    蔚来为什么要区分writeflush根本?在高性能网络应用程序中,最大的开销可能是系统调用开销。为了通过 TCP 发送数据,SwiftNIO 必须进行系统调用 ( write, writev, send, ...)。

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

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

于 2021-12-19T20:16:12.487 回答