我正在通过双工句柄h读取和写入TCP套接字。
在客户端:
sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
connect sock $ addrAddress addr
h <- getSocketHandle conn
在服务器中:
sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
setSocketOption sock ReuseAddr 1
withFdSocket sock setCloseOnExecIfNeeded
bind sock $ addrAddress addr
listen sock 1024
forever $ do
(conn, _) <- accept sock
h <- getSocketHandle conn
forkFinally (server h) (const $ hClose h)
其中:
getSocketHandle :: Socket -> IO Handle
getSocketHandle conn = do
h <- socketToHandle conn ReadWriteMode
hSetBinaryMode h True
hSetNewlineMode h NewlineMode {inputNL = CRLF, outputNL = CRLF}
hSetBuffering h LineBuffering
return h
当客户端和服务器都可用时,一切正常,但是当TCP连接终止时,例如因为客户端或服务器退出,什么都不会发生-- hGetLine
/hPutStrLn
只是阻塞线程。
怎样才能让他们抛出异常呢?
类似地,如果客户端连接失败(例如,因为服务器没有响应),connect
也会被阻塞。
我想我应该以某种方式设置套接字上的超时,但尝试套接字选项并没有太大的变化-我尝试了SO_LINGER,SO_KEEPALIVE,SO_USER_TIMEOUT似乎不被系统支持(当我试图获取它时它阻塞了),SO_RCVTIMEO / SO_SNDTIMEO似乎不被Network.Socket支持-至少文档是这么说的……
对于connect
/hPutStrLn
,我可以使用来自System.Timeout
的timeout
(这似乎是个技巧,但它可以工作),但是对于hGetLine
/hGet
,它不能工作-它只能等待用户输入,所以它应该只在连接终止时抛出异常。
我可能只需要阅读网络编程--我希望能很快弄明白……任何帮助都是非常感谢的。
编辑:看起来我应该使用更低级的send
/sendAll
和recv
,在我的代码中做缓冲和剪切到行(或者一个字节接一个字节地读--它可能会像listen
中设置的那样被缓冲),并将recv
返回的0长度视为断开连接。有没有办法通过处理更高级别的函数来让它工作?
发布于 2021-01-24 09:26:10
多亏了K. A. Buhr的建议,我发现这个问题与句柄/套接字无关,而且确实正确地抛出了异常。
正在运行的最小客户端/服务器设置:https://gist.github.com/epoberezkin/d6063c0d8f6e95d9e606652565113413
它基于Network.Socket包中的示例,但处理多个已解析的地址,并使用句柄读/写套接字。
真正的问题是由于在某些情况下没有正确处理异常,而不是像这样的东西:
race (send h) (receive h) `finally` disconnected
我做到了
race (send h) (receive h) >> disconnected
https://stackoverflow.com/questions/65858284
复制