1

I have played a bit with the Network library, the "simple" form where the use is fairly easy: a combination of 'listenOn', 'connectTo', 'accept' and we have something working. Now I am trying to use the "real" thing, meaning Network.Socket and Network.Socket.ByteString because I'd like to send files between a client and a server. But it is not as easy to use as the higher level interface and I'm looking for some code examples and/or how-tos. Particularly, the code dealing with 'getAddrInfo', 'AddrInfo' and 'SockAddr' I don't understand very deeply.
So, where can I get this type of resource?

nschoe
  • 1,967
  • 1
  • 19
  • 26

3 Answers3

1

There's an example at the bottom of the documentation for the Network.Socket.ByteString module.

tibbe
  • 8,809
  • 7
  • 36
  • 64
  • Yes, I have read and studied this example a lot, but it's quite small and not very explicit. It did give me some leads, especially in what order I had to call functions; but where I am particularly stuck is the part with the 'getAddrInfo': I don't know what to put in it, nor how it works. I have been reading the source code of the "simple" Network module to get some clues on that. – nschoe Aug 21 '11 at 12:38
1

The .Socket interface is little more than bindings to berkeley sockets, I suggest you read Beej's guide to network programming (for C) to get a handle on this.

EDIT: The pharse "little more than" isn't intended to slight the amount of work that goes into making this layer configure, build, and install smoothly on all the supported platforms. I'm just saying there's basically a one-to-one relation between many of the .Socket operations and the man (3) pages for the C primitives.

Thomas M. DuBuisson
  • 64,245
  • 7
  • 109
  • 166
  • Okay, so reading about *sockets* in general (not Haskell-related I mean) will help me understand? I'll do that then, because when I need to create a socket, I don't know what Family to use for instance, what are the differences between AI_INET, AI_UNIX etc. I'm going to read Beej's guide and see if it helps. Btw, will it give me some clues on how to use the 'getAddrInfo' function? Cause that is for now, my main problem. – nschoe Aug 21 '11 at 12:40
  • @nschoe Yes, `getaddrinfo` should be in beej's guide. If you don't already know, the man pages should help you plenty too. Just change things from camal case to lower case or to underscores for the C versions (ex: `man getaddrinfo`) – Thomas M. DuBuisson Aug 21 '11 at 14:54
0

I can just show an own example:

import Network.Socket
  ( SocketType(..), AddrInfo(..), AddrInfoFlag(..), Family(..), socket, sClose, 
    defaultHints, withSocketsDo, connect, getAddrInfo, defaultProtocol )
import Network.Socket.ByteString( sendAll, recv )
import qualified Data.ByteString.Char8 as BC( readFile, hPut, null )

sendFile :: String -> Int -> FilePath -> IO Bool
sendFile server port filename = withSocketsDo $ do
  addrinfos <- getAddrInfo desiredAddr (Just server) (Just . show $ port)
  if null addrinfos
    then do
      return False
    else do
      datafile <- BC.readFile filename
      let serveraddr = head addrinfos
      sock <- socket (addrFamily serveraddr) Stream defaultProtocol
      connect sock (addrAddress serveraddr)
      sendAll sock datafile
      sClose sock
      return True

I don't test (just cut from my code file) so maybe fails in the imports. On the server I use:

readerThread :: MyQueue -> Socket -> IO ()
readerThread queue serverSock = do
  (connsock, clientaddr) <- accept serverSock
  sClose serverSock
  putStrLn $ "> connected reader " ++ show clientaddr
  talk connsock
  sClose connsock
  putStrLn $ "> closed reader " ++ show clientaddr
  return ()
    where
      talk conn = do 
        msg <- recv conn 2048
        putStr $ "* get from " ++ show conn ++ "\n"
        myQueueWrite queue msg
        unless (B.null msg) $ do 
          talk conn

createInputPort :: ChildLocks -> MyQueue -> AddrInfo -> IO PortNumber
createInputPort children obuffer serverAddr = withSocketsDo $ do
  serverSock <- socket AF_INET Stream defaultProtocol
  bindSocket serverSock (addrAddress serverAddr)
  listen serverSock 1

  lock <- newChildLock children
  _ <- forkIO $ readerThread obuffer serverSock `finally` endChildLock lock
  socketPort serverSock

Y use a own queue (TCHAN) to comunicate between network reader an consumer thread, and childLocks to wait at the end with something like waitForChildren children construct that I found on stackoverflow.

UPDATE: the waitForChildren is on post Comparing Haskell threads to kernel threads - is my benchmark viable?

Community
  • 1
  • 1
Zhen
  • 4,171
  • 5
  • 38
  • 57
  • Thanks a lot for the code example, I'll try to get inspired from that, even though I didn't understand everything. What buggs me the most is your "talk" function. Why only read 2048 bytes, then do it again if there is still some data to receive? Can't we use lazyness here? I mean, when we read a simple file from disk, we just use readFile or hGetContents which is lazy. Can't we just do that with sockets too? – nschoe Aug 30 '11 at 10:06
  • Sorry for the 2048 constant, I use it because I want some kind of progress bar, but you are rigth, you can get all in one step instead of artificially split it on 2048/N Byte blocks. – Zhen Aug 31 '11 at 08:51