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%的答案,如果我能修复这个错误,并且修复的代码放在上面,我肯定会给你正确的答案。好的,我做了一些修改。。。。我会在问题中发布我的修正版本,如果你想在这个答案中加入足够的修正,我会将这个答案标记为已接受。