@@ -5,10 +5,10 @@ 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)
11+import Control.Monad (forever, forM_)
12import Data.ByteString.Lazy.Char8 (ByteString)
12import Data.ByteString.Lazy.Char8 (ByteString)
13import Data.IntMap (IntMap)
13import Data.IntMap (IntMap)
14import Data.Int
14import Data.Int
@@ -25,8 +25,10 @@ import qualified Data.IntMap as IntMap
25import qualified Network.Socket as N
25import qualified Network.Socket as N
26import qualified Network.Socket.ByteString.Lazy as NBS
26import qualified Network.Socket.ByteString.Lazy as NBS
28type ChanId = Int
30type ChanId = Int
29−type Chans = MVar (ChanId, IntMap (Chan ByteString, MVar [Socket]))
31+type Chans = MVar (ChanId, IntMap (Chan ByteString, [(ThreadId, Socket)]))
31-- | This deals with several different configuration properties:
33-- | This deals with several different configuration properties:
32-- * Buffer size, specified in Hints
34-- * Buffer size, specified in Hints
@@ -42,7 +44,7 @@ data TCPConfig = TCPConfig Hints HostName ServiceName
42-- Messages are all queued using an unbounded Chan.
44-- Messages are all queued using an unbounded Chan.
43mkTransport :: TCPConfig -> IO Transport
45mkTransport :: TCPConfig -> IO Transport
44mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
46mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
45− channels <- newMVar (0, IntMap.empty)
47+ chans <- newMVar (0, IntMap.empty)
46 serverAddrs <- getAddrInfo
48 serverAddrs <- getAddrInfo
47 (Just (N.defaultHints { addrFlags = [AI_PASSIVE] }))
49 (Just (N.defaultHints { addrFlags = [AI_PASSIVE] }))
@@ -51,21 +53,31 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
51 [] -> error "mkTransport: getAddrInfo returned []"
53 [] -> error "mkTransport: getAddrInfo returned []"
52 as -> head as
54 as -> head as
53 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
55 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
56+ setSocketOption sock ReuseAddr 1
54 bindSocket sock (addrAddress serverAddr)
57 bindSocket sock (addrAddress serverAddr)
55 listen sock 5
58 listen sock 5
56− _ <- forkIO $ procConnections channels sock
59+ threadId <- forkIO $ procConnections chans sock
58 return Transport
61 return Transport
59 { newConnectionWith = \_ -> do
62 { newConnectionWith = \_ -> do
60− (chanId, chanMap) <- takeMVar channels
63+ (chanId, chanMap) <- takeMVar chans
61 chan <- newChan
64 chan <- newChan
62− socks <- newMVar [sock]
63− putMVar channels (chanId + 1, IntMap.insert chanId (chan, socks) chanMap)
65+ putMVar chans (chanId + 1, IntMap.insert chanId (chan, []) chanMap)
64− return (mkSourceAddr host service chanId, mkTargetEnd chan socks)
66+ return (mkSourceAddr host service chanId, mkTargetEnd chans chanId chan)
65 , newMulticastWith = error "newMulticastWith: not defined"
67 , newMulticastWith = error "newMulticastWith: not defined"
66 , deserialize = \bs ->
68 , deserialize = \bs ->
67 let (host, service, chanId) = B.decode bs in
69 let (host, service, chanId) = B.decode bs in
68 Just $ mkSourceAddr host service chanId
70 Just $ mkSourceAddr host service chanId
71+ , closeTransport = do
72+ -- Kill the transport channel process
75+ -- Kill all target end processes
76+ (chanId, chanMap) <- takeMVar chans
77+ forM_ (IntMap.elems chanMap) (\(chan, socks) ->
78+ forM_ socks (\(threadId', sock') -> do
@@ -93,13 +105,22 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
93 , closeSourceEnd = sClose sock
105 , closeSourceEnd = sClose sock
96− mkTargetEnd :: Chan ByteString -> MVar [Socket] -> TargetEnd
108+ mkTargetEnd :: Chans -> ChanId -> Chan ByteString -> TargetEnd
97− mkTargetEnd chan socks = TargetEnd
109+ mkTargetEnd chans chanId chan = TargetEnd
98 { -- for now we will implement this as a Chan
110 { -- for now we will implement this as a Chan
99 receive = do
111 receive = do
100 bs <- readChan chan
112 bs <- readChan chan
101 return [bs]
113 return [bs]
102− , closeTargetEnd = takeMVar socks >>= mapM_ sClose
114+ , closeTargetEnd = do
115+ (chanId', chanMap) <- takeMVar chans
116+ case IntMap.lookup chanId chanMap of
117+ Nothing -> putMVar chans (chanId', chanMap)
118+ Just (_, socks) -> do
119+ forM_ socks $ \(threadId, sock) -> do
122+ let chanMap' = IntMap.delete chanId chanMap
123+ putMVar chans (chanId', chanMap')
105 procConnections :: Chans -> Socket -> IO ()
126 procConnections :: Chans -> Socket -> IO ()
@@ -108,36 +129,50 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
108 -- decode the first message to find the correct chanId
129 -- decode the first message to find the correct chanId
109 bs <- recvExact clientSock 8
130 bs <- recvExact clientSock 8
110 let chanId = fromIntegral (B.decode bs :: Int64)
131 let chanId = fromIntegral (B.decode bs :: Int64)
111− (_, chanMap) <- readMVar chans
132+ (chanId', chanMap) <- takeMVar chans
112 case IntMap.lookup chanId chanMap of
133 case IntMap.lookup chanId chanMap of
135+ putMVar chans (chanId', chanMap)
113− Nothing -> error "procConnections: cannot find chanId"
136+ error "procConnections: cannot find chanId"
114 Just (chan, socks) -> do
137 Just (chan, socks) -> do
115− modifyMVar_ socks (return . (clientSock :))
116− forkIO $ procMessages chan clientSock
138+ threadId <- forkIO $ procMessages chans chanId chan clientSock
139+ let chanMap' = IntMap.insert chanId (chan, (threadId, clientSock):socks) chanMap
140+ putMVar chans (chanId', chanMap')
118 -- This function first extracts a header of type Int64, which determines
142 -- This function first extracts a header of type Int64, which determines
119 -- the size of the ByteString that follows. The ByteString is then
143 -- the size of the ByteString that follows. The ByteString is then
120 -- extracted from the socket, and then written to the Chan only when
144 -- extracted from the socket, and then written to the Chan only when
121− -- complete.
145+ -- complete. A length of -1 indicates that the socket has finished
122− procMessages :: Chan ByteString -> Socket -> IO ()
147+ procMessages :: Chans -> ChanId -> Chan ByteString -> Socket -> IO ()
123− procMessages chan sock = forever $ do
148+ procMessages chans chanId chan sock = do
124 sizeBS <- recvExact sock 8
149 sizeBS <- recvExact sock 8
125 let size = B.decode sizeBS
150 let size = B.decode sizeBS
153+ (chanId', chanMap) <- takeMVar chans
154+ case IntMap.lookup chanId chanMap of
156+ putMVar chans (chanId', chanMap)
157+ error "procMessages: chanId not found."
158+ Just (chan, socks) -> do
159+ let socks' = filter ((/= sock) . snd) socks
160+ let chanMap' = IntMap.insert chanId (chan, socks') chanMap
161+ putMVar chans (chanId', chanMap')
126− bs <- recvExact sock size
164+ bs <- recvExact sock size
127− writeChan chan bs
165+ writeChan chan bs
166+ procMessages chans chanId chan sock
129−-- The result of `recvExact sock n` is a `ByteString` whose length is exactly
168+-- | The result of `recvExact sock n` is a `ByteString` of length `n`, received
130−-- `n`. No more bytes than necessary are read from the socket.
169+-- from `sock`. No more bytes than necessary are read from the socket.
131-- NB: This uses Network.Socket.ByteString.recv, which may *discard*
170-- NB: This uses Network.Socket.ByteString.recv, which may *discard*
132-- superfluous input depending on the socket type.
171-- superfluous input depending on the socket type.
133recvExact :: Socket -> Int64 -> IO ByteString
172recvExact :: Socket -> Int64 -> IO ByteString
134−recvExact sock n = do
173+recvExact sock n = go BS.empty sock n
175+ go bs sock 0 = return bs
135− bs <- NBS.recv sock n
177+ bs' <- NBS.recv sock n
136− let remainder = n - BS.length bs
178+ go (BS.append bs bs') sock (n - BS.length bs')
139− bs' <- recvExact sock remainder
140− return (BS.append bs bs')