My PacketHandler
is also where my session state lives. It keeps track of all ongoing connections, not just the state for one, so it's added to the channels somewhat differently, as you can see in my DragonflyServer
class that sets up the channel handlers:
static func bootstrapServer(enableLogging: Bool) -> (group: MultiThreadedEventLoopGroup, bootstrap: ServerBootstrap) {
let sharedPacketHandler = PacketHandler()
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
let bootstrap = ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.childChannelInitializer { channel in
creatHandlers(for: channel, packetHandler: sharedPacketHandler, enableLogging: enableLogging)
}
.childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1)
.childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
.childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
return (group: group, bootstrap: bootstrap)
}
As you can see, I have one instance of PacketHandler
that is shared amongst all connections. Since these connections are handled in parallel, anything shared between them must be thread-safe. (Note that some of this setup may be outdated, as I haven't updated it since June, and there have been some changes to NIO that make some of it unnecessary.)
Whether it's great design to have my session state inside an ChannelInboundHandler
, I'm not sure, I'll let a NIO expert weight in here, but it is how the ChatServer
example works as well.