@@ -5,23 +5,29 @@ module Network.Transport.TCP
6import Network.Transport
6import Network.Transport
8−import Control.Concurrent (forkIO)
8+import Control.Concurrent (forkIO, ThreadId, killThread)
9import Control.Concurrent.Chan
9import Control.Concurrent.Chan
10import Control.Concurrent.MVar
10import Control.Concurrent.MVar
11−import Control.Monad (forever, unless)
11+import Control.Monad (forever, forM_)
12−import Data.ByteString.Char8 (ByteString)
12+import Data.ByteString.Lazy.Char8 (ByteString)
13import Data.IntMap (IntMap)
13import Data.IntMap (IntMap)
14import Data.Word
15import Data.Word
15import Network.Socket
16import Network.Socket
17+ ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
18+ , SocketType (Stream), SocketOption (ReuseAddr)
19+ , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
20+ , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
16import Safe
21import Safe
18−import qualified Data.ByteString.Char8 as BS
23+import qualified Data.Binary as B
24+import qualified Data.ByteString.Lazy.Char8 as BS
19import qualified Data.IntMap as IntMap
25import qualified Data.IntMap as IntMap
20import qualified Network.Socket as N
26import qualified Network.Socket as N
21−import qualified Network.Socket.ByteString as NBS
27+import qualified Network.Socket.ByteString.Lazy as NBS
23−type Chans = MVar (Int, IntMap (Chan ByteString))
24type ChanId = Int
29type ChanId = Int
30+type Chans = MVar (ChanId, IntMap (Chan [ByteString], [(ThreadId, Socket)]))
26-- | This deals with several different configuration properties:
32-- | This deals with several different configuration properties:
27-- * Buffer size, specified in Hints
33-- * Buffer size, specified in Hints
@@ -37,7 +43,7 @@ data TCPConfig = TCPConfig Hints HostName ServiceName
37-- Messages are all queued using an unbounded Chan.
43-- Messages are all queued using an unbounded Chan.
38mkTransport :: TCPConfig -> IO Transport
44mkTransport :: TCPConfig -> IO Transport
39mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
45mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
40− channels <- newMVar (0, IntMap.empty)
46+ chans <- newMVar (0, IntMap.empty)
41 serverAddrs <- getAddrInfo
47 serverAddrs <- getAddrInfo
42 (Just (N.defaultHints { addrFlags = [AI_PASSIVE] }))
48 (Just (N.defaultHints { addrFlags = [AI_PASSIVE] }))
@@ -46,69 +52,163 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
46 [] -> error "mkTransport: getAddrInfo returned []"
52 [] -> error "mkTransport: getAddrInfo returned []"
47 as -> head as
53 as -> head as
48 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
54 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
55+ setSocketOption sock ReuseAddr 1
49 bindSocket sock (addrAddress serverAddr)
56 bindSocket sock (addrAddress serverAddr)
50 listen sock 5
57 listen sock 5
51− forkIO $ procConnections channels sock
58+ threadId <- forkIO $ procConnections chans sock
53 return Transport
60 return Transport
54− { newConnectionWith = \_ -> do
61+ { newConnectionWith = {-# SCC "newConnectionWith" #-}\_ -> do
55− (chanId, chanMap) <- takeMVar channels
62+ (chanId, chanMap) <- takeMVar chans
56 chan <- newChan
63 chan <- newChan
57− putMVar channels (chanId + 1, IntMap.insert chanId chan chanMap)
64+ putMVar chans (chanId + 1, IntMap.insert chanId (chan, []) chanMap)
58− return (mkSourceAddr host service chanId, mkTargetEnd chan)
65+ return (mkSourceAddr host service chanId, mkTargetEnd chans chanId chan)
59− , newMulticastWith = undefined
66+ , newMulticastWith = error "newMulticastWith: not defined"
60− , deserialize = \bs ->
67+ , deserialize = {-# SCC "deserialize" #-} \bs ->
61− case readMay . BS.unpack $ bs of
68+ let (host, service, chanId) = B.decode bs in
62− Nothing -> error "deserialize: cannot parse"
63− Just (host, service, chanId) -> Just $ mkSourceAddr host service chanId
69+ Just $ mkSourceAddr host service chanId
70+ , closeTransport = {-# SCC "closeTransport" #-} do
71+ -- Kill the transport channel process
74+ -- Kill all target end processes
75+ (chanId, chanMap) <- takeMVar chans
76+ forM_ (IntMap.elems chanMap) (\(chan, socks) ->
77+ forM_ socks (\(threadId', sock') -> do
67− mkSourceAddr :: HostName -> ServiceName -> ChanId -> SourceAddr
82+mkSourceAddr :: HostName -> ServiceName -> ChanId -> SourceAddr
68− mkSourceAddr host service chanId = SourceAddr
83+mkSourceAddr host service chanId = SourceAddr
69− { connectWith = \_ -> mkSourceEnd host service chanId
84+ { connectWith = {-# SCC "connectWith" #-} \_ -> mkSourceEnd host service chanId
70− , serialize = BS.pack . show $ (host, service, chanId)
85+ , serialize = {-# SCC "serialize" #-} B.encode (host, service, chanId)
73− mkSourceEnd :: HostName -> ServiceName -> ChanId -> IO SourceEnd
88+mkSourceEnd :: HostName -> ServiceName -> ChanId -> IO SourceEnd
74− mkSourceEnd host service chanId = withSocketsDo $ do
89+mkSourceEnd host service chanId = withSocketsDo $ do
75− serverAddrs <- getAddrInfo Nothing (Just host) (Just service)
90+ serverAddrs <- getAddrInfo Nothing (Just host) (Just service)
76− let serverAddr = case serverAddrs of
91+ let serverAddr = case serverAddrs of
77− [] -> error "mkSourceEnd: getAddrInfo returned []"
92+ [] -> error "mkSourceEnd: getAddrInfo returned []"
78− as -> head as
93+ as -> head as
79− sock <- socket (addrFamily serverAddr) Stream defaultProtocol
94+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
80− setSocketOption sock ReuseAddr 1
95+ setSocketOption sock ReuseAddr 1
81− N.connect sock (addrAddress serverAddr)
96+ N.connect sock (addrAddress serverAddr)
82− NBS.sendAll sock $ BS.pack . show $ chanId
97+ NBS.sendAll sock $ B.encode (fromIntegral chanId :: Int64)
83− return $ SourceEnd
98+ return SourceEnd
84− { Network.Transport.send = \bss -> NBS.sendMany sock bss
99+ { send = {-# SCC "send" #-} \bss -> do
100+ let size = fromIntegral (sum . map BS.length $ bss) :: Int64
103+ NBS.sendAll sock (B.encode (fromIntegral size :: Word8))
105+ NBS.sendAll sock (B.encode (255 :: Word8))
106+ NBS.sendAll sock (B.encode size)
107+ mapM_ (NBS.sendAll sock) bss
108+ , closeSourceEnd = {-# SCC "closeSourceEnd" #-} sClose sock
111+mkTargetEnd :: Chans -> ChanId -> Chan [ByteString] -> TargetEnd
112+mkTargetEnd chans chanId chan = TargetEnd
113+ { -- for now we will implement this as a Chan
114+ receive = {-# SCC "receive" #-} readChan chan
115+ , closeTargetEnd = {-# SCC "closeTargetEnd" #-} do
116+ (chanId', chanMap) <- takeMVar chans
117+ case IntMap.lookup chanId chanMap of
118+ Nothing -> putMVar chans (chanId', chanMap)
119+ Just (_, socks) -> do
120+ forM_ socks $ \(threadId, sock) -> do
123+ let chanMap' = IntMap.delete chanId chanMap
124+ putMVar chans (chanId', chanMap')
127+-- | This function waits for inbound connections. If a connection fails
128+-- for some reason, an error is raised.
129+procConnections :: Chans -> Socket -> IO ()
130+procConnections chans sock = forever $ do
131+ (clientSock, _clientAddr) <- accept sock
132+ -- decode the first message to find the correct chanId
133+ bss <- recvExact clientSock 8
135+ [] -> error "procConnections: inbound chanId aborted"
137+ let bs = BS.concat bss
138+ let chanId = fromIntegral (B.decode bs :: Int64)
139+ (chanId', chanMap) <- takeMVar chans
140+ case IntMap.lookup chanId chanMap of
142+ putMVar chans (chanId', chanMap)
143+ error "procConnections: cannot find chanId"
144+ Just (chan, socks) -> do
145+ threadId <- forkIO $ procMessages chans chanId chan clientSock
146+ let chanMap' = IntMap.insert chanId (chan, (threadId, clientSock):socks) chanMap
147+ putMVar chans (chanId', chanMap')
87− mkTargetEnd :: Chan ByteString -> TargetEnd
149+-- | This function first extracts a header of type Word8, which determines
88− mkTargetEnd chan = TargetEnd
150+-- the size of the ByteString that follows. If this size is 0, this indicates
89− { -- for now we will implement this as a Chan
151+-- that the ByteString is large, so the next value is an Int64, which
90− receive = do
152+-- determines the size of the ByteString that follows. The ByteString is then
91− bs <- readChan chan
153+-- extracted from the socket, and then written to the Chan only when
92− return [bs]
154+-- complete. If either of the first header size is null this indicates the
93− }
155+-- socket has closed.
156+procMessages :: Chans -> ChanId -> Chan [ByteString] -> Socket -> IO ()
157+procMessages chans chanId chan sock = do
158+ sizeBSs <- recvExact sock 1
162+ let size = fromIntegral (B.decode . BS.concat $ sizeBSs :: Word8)
165+ sizeBSs' <- recvExact sock 8
168+ _ -> procMessage (B.decode . BS.concat $ sizeBSs' :: Int64)
169+ else procMessage size
171+ closeSocket :: IO ()
173+ (chanId', chanMap) <- takeMVar chans
174+ case IntMap.lookup chanId chanMap of
176+ putMVar chans (chanId', chanMap)
177+ error "procMessages: chanId not found."
178+ Just (chan, socks) -> do
179+ let socks' = filter ((/= sock) . snd) socks
180+ let chanMap' = IntMap.insert chanId (chan, socks') chanMap
181+ putMVar chans (chanId', chanMap')
183+ procMessage :: Int64 -> IO ()
184+ procMessage size = do
185+ bss <- recvExact sock size
190+ procMessages chans chanId chan sock
95− procConnections :: Chans -> Socket -> IO ()
192+-- | The result of `recvExact sock n` is a `[ByteString]` whose concatenation
96− procConnections chans sock = forever $ do
193+-- is of length `n`, received from `sock`. No more bytes than necessary are
97− (clientSock, _clientAddr) <- accept sock
194+-- read from the socket.
98− -- decode the first message to find the correct chanId
195+-- NB: This uses Network.Socket.ByteString.recv, which may *discard*
196+-- superfluous input depending on the socket type. Also note that
197+-- if `recv` returns an empty `ByteString` then this means that the socket
198+-- was closed: in this case, we return an empty list.
199+-- NB: It may be appropriate to change the return type to
200+-- IO (Either ByteString ByteString), where `return Left bs` indicates
201+-- a partial retrieval since the socket was closed, and `return Right bs`
202+-- indicates success. This hasn't been implemented, since a Transport `receive`
203+-- represents an atomic receipt of a message.
204+recvExact :: Socket -> Int64 -> IO [ByteString]
205+recvExact sock n = go [] sock n
207+ go :: [ByteString] -> Socket -> Int64 -> IO [ByteString]
208+ go bss _ 0 = return (reverse bss)
99− bs <- NBS.recv clientSock 4096
210+ bs <- NBS.recv sock n
100− case BS.readInt bs of
101− Nothing -> error "procConnections: cannot parse chanId"
102− Just (chanId, bs') -> do
103− -- lookup the channel
104− (_, chanMap) <- readMVar chans
105− case IntMap.lookup chanId chanMap of
106− Nothing -> error "procConnections: cannot find chanId"
107− Just chan -> forkIO $ do
108− unless (BS.null bs') $ writeChan chan bs'
211+ if BS.null bs
109− procMessages chan clientSock
212+ then return []
213+ else go (bs:bss) sock (n - BS.length bs)
111− procMessages :: Chan ByteString -> Socket -> IO ()
112− procMessages chan sock = forever $ do
113− bs <- NBS.recv sock 4096