我一直在使用conduit-extra
的UNIX软件包,它基本上允许使用UNIX域套接字轻松创建服务器,特别是使用runUnixServer
funciton。< / p>
问题是在函数存在之后它不会清理套接字文件,这意味着它需要手动清理。这是一个简单的例子,它基本上创建了一个echo服务器。
main :: IO ()
main = do
let settings = serverSettings "foobar.sock"
runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad))
我稍微谷歌了一下,发现在这里处理资源的正确方法是使用resourcet
包。虽然问题在于资源中的大多数API都希望我自己分配资源,但runUnixSever
的情况并非如此,而register
并不会返回任何资源。
起初我以为我可以使用main :: IO ()
main = runResourceT $ do
register $ removeLink "foobar.sock"
let settings = serverSettings "foobar.sock"
liftIO $ runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad))
来注册删除文件的函数,例如以下
allocate
这种方法存在问题,至少就register
的文档说:
这与调用分配然后注册发布操作几乎完全相同,但这正确地处理了异步异常的屏蔽。
这是否意味着runUnixServer
本身并不处理异步异常?如果是这样,当allocate
(文档说它为每个客户端产生一个线程)产生的处理程序之一引发错误时,这可能是一个问题吗?
我提出的第三个也是最后一个解决方案是使用main :: IO ()
main = runResourceT $ do
allocate (return 1) (const $ removeLink "foobar.sock")
let settings = serverSettings "foobar.sock"
liftIO $ runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad))
,以确保正确处理异步异常(我不确定在这种情况下是否真的有必要) )。
(return 1)
但这真的是最好的解决方案吗?由于我创建了一个我永远不会使用const
的值,然后使用{{1}}函数忽略终结器中的值。
答案 0 :(得分:9)
在解决resourcet
问题之前:
resourcet
。你可以使用finally
函数来做这样的事情,例如: runUnixServer settings (\ad -> ...)
最后removeLink "foobar.sock"
。也就是说,您使用register
的初始代码很好。我看到的唯一问题是,如果在创建foobar.sock
之前抛出异常,尽管我的finally
解决方案也容易受到攻击。
有关allocate vs register的注释与代码如下所示:
handle <- openFile fp ReadMode
register $ hClose handle
此代码容易受到openFile
和register
调用之间抛出的异步异常的影响。由于您没有分配这样的资源,register
没问题。