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?

+3


source to share


2 answers


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)

      

+2


source


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()

      

+2


source







All Articles