Simple TCP client - memory issues
hGetContents gets all remaining input, using lazy IO.  The second call to hGetContents throws an error because the first call has already claimed the data, in a sense.

What I would do is have a separate thread tunnel data from the handle to standard output, and have the main thread tunnel data from standard input to the handle:

_ &lt;- forkIO $ hGetContents h &gt;&gt;= putStr
getContents &gt;&gt;= hPutStr h


I would also use LineBuffering rather than NoBuffering, so it doesn't have to transmit a TCP packet for every character (*).

Thus, we have:

import Control.Concurrent
import Network
import System.Environment
import System.IO

main :: IO ()
main = do
    [host, port] &lt;- getArgs
    h &lt;- connectTo host $ PortNumber $ toEnum $ read port
    hSetBuffering stdout LineBuffering
    hSetBuffering h      LineBuffering
    _ &lt;- forkIO $ hGetContents h &gt;&gt;= putStr
    getContents &gt;&gt;= hPutStr h


A couple notes:


I was able to avoid the type signature by using toEnum rather than fromIntegral.
_ &lt;- forkIO $ ... suppresses a warning when the program is compiled with -Wall.


We can do better, though.  Currently, if the server disconnects, the user does not see that the server disconnected until hitting enter a couple times, which produces an ugly error message:

tcp-client: &lt;socket: 3&gt;: commitBuffer: resource vanished (Broken pipe)


Let's see if we can get the program to terminate when either the server or the client closes the connection.  Bear in mind that:


getContents and hGetContents terminate the list when EOF is reached.  Thus:


hGetContents h &gt;&gt;= putStr terminates when the server closes the connection
getContents &gt;&gt;= hPutStr h terminates when the user presses Ctrl+D

The program terminates when the main thread terminates, regardless if child threads still have work to do.


A good way to do this, I think, is to perform the receiving and sending in two separate threads, and have the main thread wait on an MVar:

done &lt;- newEmptyMVar

_ &lt;- forkIO $ (hGetContents h &gt;&gt;= putStr)
              `finally` tryPutMVar done ()

_ &lt;- forkIO $ (getContents &gt;&gt;= hPutStr h)
              `finally` tryPutMVar done ()

-- Wait for at least one of the above threads to complete
takeMVar done


* Actually, sending individual characters at a time will probably trigger Nagle's algorithm.  Still, sending characters one at a time creates a lot of unnecessary CPU overhead.