在OSX上设置套接字选项

时间:2012-09-25 20:00:19

标签: macos sockets haskell

我试图在使用Network.Socket模块构建的套接字上设置接收超时。这是一个代码段:

import Network.Socket

host = "127.0.0.1"
port = PortNumber 3000

main = do
  addrinfos <- getAddrInfo Nothing (Just host) (Just port)
  let serveraddr = head addrinfos
  sock <- socket (addrFamily serveraddr) Stream defaultProtocol
  setSocketOption sock RecvTimeOut 120000000
  connect sock (addrAddress serveraddr)
  msg <- recv sock 1024
  putStrLn msg
  sClose sock

setSocketOption行抛出异常:

*** Exception: setSocketOption: invalid argument (Invalid argument)

setSocketOption仅接受Int个参数进行设置,但并非所有套接字选项都需要Int。具体而言,RecvTimeOutSendTimeOut期待struct timeval。还有另一种方法可以从haskell设置这些选项吗?

我在OSX 10.8.1上运行GHC 7.4.2

修改

Network.Socket.Options似乎是最好的解决方案,在OSX上编译它只需要一个很小的拉取请求。从版本0.2.0.1开始,network-socket-options现在在OSX上编译。

编辑2: Network.Socket.Options没有运气。 setRecvTimeout函数似乎对OSX没有任何影响。我最终使用timeout包中的System.Timeout作为解决方法。

msg <- timeout 120000000 $ recv sock 1024

1 个答案:

答案 0 :(得分:2)

我从这里读到了Haskell和结构定义:http://therning.org/magnus/archives/315 。这里来自MSDN的struct timeval定义(它与GNU上的结构相同,可能在OSX上):http://msdn.microsoft.com/en-us/library/windows/desktop/ms740560(v=vs.85).aspx

时间C-header:

...
typedef struct timeval {
    long tv_sec;
    long tv_usec;
} timeval;
....

看起来你需要在Haskell中定义某种结构构造函数。或者对时间标题的完全绑定(取自http://hackage.haskell.org/packages/archive/bindings-common/0.1.4/doc/html/src/CTypes.html):

                module CTypes where
import Foreign
import Foreign.C

-- time.h

data Tm = Tm {
    tm'sec,
    tm'min,
    tm'hour,
    tm'mday,
    tm'mon,
    tm'year,
    tm'wday,
    tm'yday,
    tm'isdst :: CInt
   }

instance Storable Tm where
    sizeOf _ = fromIntegral size_of_tm
    alignment = sizeOf
    peek p =
        with 0 $ \p1 -> with 0 $ \p2 -> with 0 $ \p3 ->
        with 0 $ \p4 -> with 0 $ \p5 -> with 0 $ \p6 ->
        with 0 $ \p7 -> with 0 $ \p8 -> with 0 $ \p9 ->
        c2hs_tm p p1 p2 p3 p4 p5 p6 p7 p8 p9 >>
        peek p1 >>= \v1 -> peek p2 >>= \v2 -> peek p3 >>= \v3 ->
        peek p4 >>= \v4 -> peek p5 >>= \v5 -> peek p6 >>= \v6 ->
        peek p7 >>= \v7 -> peek p8 >>= \v8 -> peek p9 >>= \v9 ->
        return $ Tm v1 v2 v3 v4 v5 v6 v7 v8 v9
    poke p (Tm v1 v2 v3 v4 v5 v6 v7 v8 v9) =
        hs2c_tm p v1 v2 v3 v4 v5 v6 v7 v8 v9

foreign import ccall size_of_tm :: CInt

foreign import ccall hs2c_tm
    :: Ptr Tm -> CInt -> CInt -> CInt -> CInt ->
       CInt -> CInt -> CInt -> CInt -> CInt -> IO ()

foreign import ccall c2hs_tm
    :: Ptr Tm -> Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt ->
       Ptr CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt ->
       Ptr CInt -> IO ()

-- sys/time.h

data Timeval = Timeval {timeval'tv_sec, timeval'tv_usec :: CLong}

instance Storable Timeval where

    sizeOf _ = fromIntegral size_of_timeval

    alignment = sizeOf

    peek p =
        with 0 $ \p1 ->
        with 0 $ \p2 ->

        c2hs_timeval p p1 p2 >>

        peek p1 >>= \v1 ->
        peek p2 >>= \v2 ->

        return $ Timeval {timeval'tv_sec = v1, timeval'tv_usec = v2}

    poke p v = hs2c_timeval p (timeval'tv_sec v) (timeval'tv_usec v)

foreign import ccall "size_of_timeval" size_of_timeval
    :: CInt

foreign import ccall "hs2c_timeval" hs2c_timeval
    :: Ptr Timeval -> CLong -> CLong -> IO ()

foreign import ccall "c2hs_timeval" c2hs_timeval
    :: Ptr Timeval -> Ptr CLong -> Ptr CLong -> IO ()

剥离到必要的版本将是:

module CTypes where
import Foreign
import Foreign.C

-- sys/time.h

data Timeval = Timeval {timeval'tv_sec, timeval'tv_usec :: CLong}

然后您应该能够通过以下方式初始化timeval结构:

timeval <- Timeval { tv_sec=120 , tv_usec=0 }

我希望有所帮助...