How to properly close network connections using a network channel?
To learn the basics of the library conduit
, I used network-conduit
to create a simple echo server:
import Control.Monad.IO.Class import qualified Data.ByteString.Char8 as BS import Data.Conduit import Data.Conduit.Network -- A conduit that print the input it receives on the console -- and passes it through. echo :: (MonadIO m) => Conduit BS.ByteString m BS.ByteString echo = do yield (BS.pack "Anything you type will be echoed back.\n") -- Print the received data to the console as well: awaitForever (\x -> liftIO (BS.putStr x) >> yield x) echoApp :: (MonadIO m) => Application m echoApp appData = appSource appData $= echo $$ appSink appData -- Listen on port 4545: main :: IO () main = runTCPServer (serverSettings 4545 HostAny) echoApp
It does what I wanted, but when the client closes its part of the connection, the server is still waiting for input instead of writing any remaining data and closing its sending part of the connection:
$ nc localhost 4545 <<<"Hello world!"
Anything you type will be echoed back.
Hello world!
I tried to remove echo
and do simply
echoApp appData = appSource appData $$ appSink appData
but the problem still exists. What am I doing wrong?
source to share
I'm not sure what you mean by "the server is not responding"? I think you are expecting the server to shutdown after the client shutdown. If so, this is not the intention of the library: it continues connections to the server in an infinite loop as long as they keep coming in. By using addCleanup
, you can see that the individual connection handlers actually complete, for example:
echo :: (MonadIO m) => Conduit BS.ByteString m BS.ByteString
echo = addCleanup (const $ liftIO $ putStrLn "Stopping") $ do
yield (BS.pack "Anything you type will be echoed back.\n")
-- Print the received data to the console as well:
awaitForever (\x -> liftIO (BS.putStr x) >> yield x)
source to share
It turned out that the problem is not network-conduit
, this part is working correctly. The problem was nc
, that doesn't close its sending part of the socket when all data is sent. I made a test python script and it works against the server as expected:
#!/usr/bin/env python
import socket
HOST = 'localhost'
PORT = 4545
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('Hello, world')
# This was missing in `nc`:
s.shutdown(socket.SHUT_WR);
print 'Received'
data = s.recv(1024)
while data:
print data,
data = s.recv(1024)
s.close()
source to share