检测 TCP 连接何时无法打开或终止

Detecting when TCP connection fails to open or is terminated

我正在通过双工句柄 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/sendAllrecv,对代码中的行进行缓冲和切割(或逐字节读取 -可能它会按照 listen 中的设置进行缓冲,并将 recv 的 0 长度 returns 视为断开连接。有没有办法通过具有更高级别功能的句柄使其工作?

感谢K. A. Buhr的建议,我发现问题与handle/socket无关,确实抛出了异常。

有效的最小 client/server 设置:https://gist.github.com/epoberezkin/d6063c0d8f6e95d9e606652565113413

它基于 Network.Socket 包中的示例,但处理多个已解析的地址并使用 read/write 套接字的句柄。

真正的问题是由于在某些情况下不正确地处理异常,而不是像这样:

race (send h) (receive h) `finally` disconnected

我做到了

race (send h) (receive h) >> disconnected