我正在编写一个服务器,其中一个要求是它需要能够将数据推送到客户端,而无需客户端直接请求数据。我正在使用导管,但感觉这超出了导管的能力。我遇到的问题是似乎没有办法判断套接字是否有可用的数据,并且 await 将阻止执行,直到有可用的数据。假设我有以下功能
getPacket :: Conduit ByteString IO ClientPacket --take a bytestring and yield a ClientPacket i.e. the ByteString deserialized into a sensible form
processPacket :: Conduit ClientPacket IO ServerPacket --take a ClientPacket and yield a ServerPacket i.e. a response to the client's request
putPacket :: Conduit ServerPacket IO ByteString --serialize the ServerPacket
然后我将管道与来自 Conduit.Network 库的源和接收器连接在一起
appSource appData $$ getPacket =$= processPacket =$= putPacket $= appSink appData
现在,我从管道外部引入一个数据源,并且我想将该数据合并到管道中。例如,如果这是一个聊天服务器,则外部数据将是其他客户端发送的消息。问题是,无论我尝试在哪里引入这些外部数据,它都会被调用 await 阻塞。本质上,我最终会得到如下所示的代码。
yield processOutsideData --deal with the outside data
data <- await --await data from upstream
处理更多外部数据的唯一方法是上游组件产生某些东西,但上游只有在从客户端获取数据时才会产生,这正是我试图避免的。我已经尝试使用多个线程和 TChan 来解决这个问题,但似乎 appSource 和 appSink 必须在同一个线程中使用,否则我会从 recv 获得无效的文件描述符异常(这是有道理的)。
但是,如果套接字源和接收器在同一个线程中运行,我会再次遇到等待阻塞的问题,并且我无法检查套接字是否有可用的数据。在这一点上,我似乎已经用导管撞到了墙上。
但我真的很喜欢使用导管,并且更愿意继续使用它们。所以我的问题是:有没有办法做我想要通过管道实现的目标?