我一直在尝试为Go客户端编写Haskell服务器。对于Haskell TCP服务器,我只是使用Network.Socket
。每当我尝试运行hWaitForInput
时,我都会收到此错误:
fdReady: fd is too big.
这是服务器代码 -
connHandler :: (Socket, SockAddr) -> IO()
connHandler (sock, _) = do
putStrLn "Starting Handler"
handle <- socketToHandle sock ReadWriteMode
hSetBuffering handle LineBuffering
hPutStrLn handle "Hello Client!"
putStrLn "Waiting for Input"
success <- hWaitForInput handle (1000*10)
putStrLn "Wait done"
if success
then do
putStrLn "Client timed out"
else do
msg <- hGetLine handle
putStrLn msg
hClose handle
Go客户端正在接收并打印服务器的消息(“Hello Client!”),但是haskell服务器在打印“Waiting for Input”后立即抛出错误
答案 0 :(得分:3)
你没有做错任何事。您看到的特定错误消息仅显示在Windows上运行的GHC&gt; = 8.0.2,并表示他们试图在非Windows体系结构上解决的内部GHC函数fdReady
中的错误/限制但是在Windows上没有固定。 (不要觉得太嫉妒 - 非Windows体系结构上的“修复”目前也已破坏并崩溃。)尝试早期版本的GHC可能无济于事 - 它仍然会导致错误,但是错误信息会有所不同。
问题出现了:在Windows上,内部函数fdReady
使用select()
系统调用来轮询文件描述符以获取套接字,而select
仅限于某个最大数值。它可以轮询的文件描述符。看起来这个值的Windows默认值非常低(64),但可以在编译时增加(编译 GHC 的时间,遗憾的是,不是GHC编译程序的时间)。
如果添加以下行:
hShow handle >>= putStrLn
在hWaitForInput
之前,您应该看到为套接字打印的一些调试信息,包括loc=<socket: nnn>
,其中nnn
是文件描述符。这可以帮助您验证您是否看到导致问题的文件描述符大于64。
如果是这种情况,我建议您提交GHC错误,看看是否可以修复它。
答案 1 :(得分:2)
作为替代/解决方法,您可以尝试在一个线程中读取一行,在另一个线程中读取一个计时器。
putStrLn "Waiting for Input"
msgMVar <- newEmptyMVar
tid <- forkIO $ hGetLine handle >>= putMVar msgMVar
maybeExn <- waitTimeout tid (1000*10)
case maybeExn of
Nothing -> do
killThread tid
putStrLn "Client timed out"
Just (Just _) ->
putStrLn "Exception"
_ -> do
msg <- takeMVar msgMVar
putStrLn msg
hClose handle
这确实有不同的行为(可以在读取行中间超时)而不是代码(如果可以读取单个字节,则永远不会超时),即使行不完整也是如此。