Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
709 views
in Technique[技术] by (71.8m points)

sockets - Detecting when TCP connection fails to open or is terminated

I am reading and writing to TCP socket via a duplex handle h.

In the client:

sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
connect sock $ addrAddress addr
h <- getSocketHandle conn

In the server:

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)

where:

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

When both the client and the server are available, it all works fine, but when the TCP connection terminates, e.g. because the client or the server exit, nothing happens - hGetLine/hPutStrLn just block the thread.

How can I make them throw exception?

Similarly, if the client fails to connect (e.g. because of the server not responding), connect also just blocks.

I suspect I should somehow set the timeouts on the socket, but experimenting with socket options didn't change much - I tried SO_LINGER, SO_KEEPALIVE, SO_USER_TIMEOUT doesn't seem to be supported by the system (it blocks when I try to get it), SO_RCVTIMEO / SO_SNDTIMEO don't seem to be supported by Network.Socket - at least that's what the docs say...

For connect/hPutStrLn I could use timeout from System.Timeout (it seems a hack, but it will work), but for hGetLine/hGet it won't work - it can just wait for user input, so it should only throw exception when the connection terminates.

I should probably just read the network programming - I was hoping to figure it out quickly... Any help is really appreciated.

EDIT: It seems like I should be using lower level send/sendAll and recv, do buffering and cutting to lines in my code (or read byte by byte - probably it would be buffered anyway as set in listen) and treat 0 length returns by recv as disconnection. Isn't there a way to make it work via handle with higher level functions?

question from:https://stackoverflow.com/questions/65858284/detecting-when-tcp-connection-fails-to-open-or-is-terminated

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Thanks to the suggestion of K. A. Buhr, I figured out that the issue was not related to handle/socket, and the exceptions are indeed thrown correctly.

Minimal client/server setup that is working: https://gist.github.com/epoberezkin/d6063c0d8f6e95d9e606652565113413

It's based on the example in Network.Socket package, but handles multiple resolved addresses and uses handles to read/write socket.

The real issue was due to incorrectly handling exceptions in some cases, specifically instead of something like:

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

I did

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...