Haskell 如何更改runTCPClient超时持续时间?
问题说明了一切。。。。我正在使用Haskell 如何更改runTCPClient超时持续时间?,haskell,Haskell,问题说明了一切。。。。我正在使用Data.conductor.Network,有时服务器没有启动。默认超时需要几分钟,我的程序需要在几秒钟内知道 {-# LANGUAGE OverloadedStrings #-} import Data.Conduit.Network main = do --use any IP address that isn't up.... I use 1.2.3.4 for testing runTCPClient (clientSettings 80 "1
Data.conductor.Network
,有时服务器没有启动。默认超时需要几分钟,我的程序需要在几秒钟内知道
{-# LANGUAGE OverloadedStrings #-}
import Data.Conduit.Network
main = do --use any IP address that isn't up.... I use 1.2.3.4 for testing
runTCPClient (clientSettings 80 "1.2.3.4") $ \server -> do
putStrLn "connected"
我上下查阅了文档和资料来源,但答案对我来说并不清楚。我认为这可能是不可能的
回复@haoformayor回答的其他信息 我最终使用了@haoformayor建议的类似方法,但需要进行一些更改以使其正常工作。这是我目前的工作代码
runTCPClientWithConnectTimeout::ClientSettings->Double->(AppData->IO ())->IO ()
runTCPClientWithConnectTimeout settings secs cont = do
race <- newChan
resultMVar <- newEmptyMVar
timerThreadID <- forkIO $ do
threadDelaySeconds secs
writeChan race False
clientThreadID <- forkIO $ do
result <-
try $
runTCPClient settings $ \appData -> do
writeChan race True
cont appData
writeChan race True --second call needed because first call won't be hit in the case of an error caught by try
putMVar resultMVar result
timedOut <- readChan race
if timedOut
then do
killThread timerThreadID --don't want a buildup of timer threads....
result' <- readMVar resultMVar
case result' of
Left e -> throw (e::SomeException)
Right x -> return x
else do
error "runTCPClientWithConnectTimeout: could not connect in time"
killThread clientThreadID
runTCPClientWithConnectTimeout::ClientSettings->Double->(AppData->IO())->IO()
runTCPClientWithConnectTimeout设置secs cont=do
race即使在没有好的API的C世界中,也很难做到这一点
因此,假设您在POSIX上,Haskell代码最终将调用connect(3)
。正如文件所说:
如果无法立即建立连接,并且未为套接字的文件描述符设置O_NONBLOCK,则connect()将阻塞一段未指定的超时时间,直到建立连接为止。如果在建立连接之前超时,connect()将失败,连接尝试将中止~
未指定的超时间隔。用C语言可以做的是。它也是绝对不可移植的,可能只能保证在Linux上工作
通过谷歌搜索,似乎没有人真正将此类代码打包到C库中,更不用说Haskell库了。这给我们留下了一个直截了当的攻击:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Concurrent
import Data.Conduit.Network
-- | A more sensible unit of measurement for thread delays
threadDelaySeconds :: Double -> IO ()
threadDelaySeconds secs =
threadDelay (ceiling $ secs * 1e6)
runTCPClientBounded :: ClientSettings -> Double -> (AppData -> IO ()) -> IO ()
runTCPClientBounded settings secs cont = do
race <- newChan
_ <- forkIO (timer race)
_ <- forkIO (runTCPClient settings (handleServer race))
winner <- readChan race
case winner of
Nothing ->
error "runTCPClientBounded: could not connect in time"
Just appdata ->
cont appdata
where
timer :: Chan (Maybe AppData) -> IO ()
timer chan = do
putStrLn ("runTCPClientBounded: waiting $n seconds: " ++ show secs)
threadDelaySeconds secs
writeChan chan Nothing
handleServer :: Chan (Maybe AppData) -> AppData -> IO ()
handleServer chan appdata =
writeChan chan (Just appdata)
main :: IO ()
main =
runTCPClientBounded (clientSettings 80 "1.2.3.4") 1 (const (putStrLn "connected to 1.2.3.4!"))
-- runTCPClientBounded (clientSettings 80 "example.com") 1 (const (putStrLn "connected to example.com!"))
{-#语言重载字符串}
模块主要在哪里
导入控制。并发
导入Data.conductor.Network
--|更合理的线程延迟度量单位
threadDelaySeconds::Double->IO()
线程延迟秒=
线程延迟(上限$secs*1e6)
runTCPClientBounded::ClientSettings->Double->(AppData->IO())->IO()
runTCPClientBounded设置secs cont=do
竞速AppData->IO()
handleServer chan appdata=
writeChan chan(仅适用于appdata)
main::IO()
主要=
runTCPClientBounded(客户端设置80“1.2.3.4”)1(const(putStrLn“连接到1.2.3.4!”)
--runTCPClientBounded(clientSettings 80“example.com”)1(const(putStrLn“connected to example.com!”)
此代码在包含n
-秒计时器的线程和包含runTCPClient
的线程之间设置争用。如果计时器先关闭,我们抛出一个异常;如果connect(3)
首先关闭,我们将运行continuation。演示代码警告:如果runTCPClient
线程获胜,但端点仍然不存在(表示虽然计时器没有关闭,但操作系统仍然确定端点已死亡),则可能需要捕获异常。这两个线程通过一个通道进行通信
真讨厌 从软件包管道网络接受超时参数。但如果你看来源,他们没有做什么特别的事。他们只是在使用System.timeout
中的timeout
功能。也许你可以在管道网络上创建一个包装器。@Sibi-我在过去等待UDP数据包时做过类似的事情,在心跳中也会做同样的事情,只是在这种情况下,超时会包装整个连接,而不仅仅是失败的连接。既然一个成功的连接应该持续几个小时,我不能就这样结束IO操作…谢谢你的回答和背景评论,我认为这是正确的方法。。。。但是代码中有一个bug。appdata对象只有在runTCPClient完成运行后才能使用,因此连接关闭。我将尝试更改第一个线程实际运行的位置cont
,如果没有设置连接标志,第二个线程将终止它。你的答案让我有了80%的答案,如果我能修复这个错误,并且修复的代码放在上面,我肯定会给你正确的答案。好的,我做了一些修改。。。。我会在问题中发布我的修正版本,如果你想在这个答案中加入足够的修正,我会将这个答案标记为已接受。