1-- | Multicast utilities
2{-# OPTIONS_GHC -fno-warn-orphans #-}
3module Control.Distributed.Process.Backend.SimpleLocalnet.Internal.Multicast (initMulticast) where
5import Data.Function (on)
7import qualified Data.Map as Map (empty)
8import Data.Binary (Binary, decode, encode)
9import Data.IORef (IORef, newIORef, readIORef, modifyIORef)
10import qualified Data.ByteString as BSS (ByteString, concat)
11import qualified Data.ByteString.Lazy as BSL
20import Data.Accessor (Accessor, (^:), (^.), (^=))
21import qualified Data.Accessor.Container as DAC (mapDefault)
22import Control.Applicative ((<$>))
23import Network.Socket (HostName, PortNumber, Socket, SockAddr)
24import qualified Network.Socket.ByteString as NBS (recvFrom, sendManyTo)
25import Network.Transport.Internal (decodeInt32, encodeInt32)
26import Network.Multicast (multicastSender, multicastReceiver)
28--------------------------------------------------------------------------------
30--------------------------------------------------------------------------------
32-- | Given a hostname and a port number, initialize the multicast system.
34-- Note: it is important that you never send messages larger than the maximum
35-- message size; if you do, all subsequent communication will probably fail.
37-- Returns a reader and a writer.
39-- NOTE: By rights the two functions should be "locally" polymorphic in 'a',
40-- but this requires impredicative types.
41initMulticast :: forall a. Binary a
42 => HostName -- ^ Multicast IP
43 -> PortNumber -- ^ Port number
44 -> Int -- ^ Maximum message size
45 -> IO (IO (a, SockAddr), a -> IO ())
46initMulticast host port bufferSize = do
47 (sendSock, sendAddr) <- multicastSender host port
48 readSock <- multicastReceiver host port
49 st <- newIORef Map.empty
50 return (recvBinary readSock st bufferSize, writer sendSock sendAddr)
52 writer :: forall a. Binary a => Socket -> SockAddr -> a -> IO ()
53 writer sock addr val = do
54 let bytes = encode val
55 len = encodeInt32 (BSL.length bytes)
56 NBS.sendManyTo sock (len : BSL.toChunks bytes) addr
58--------------------------------------------------------------------------------
59-- UDP multicast read, dealing with multiple senders --
60--------------------------------------------------------------------------------
62type UDPState = Map SockAddr BSL.ByteString
64#if MIN_VERSION_network(2,4,0)
65-- network-2.4.0 provides the Ord instance for us
67instance Ord SockAddr where
68 compare = compare `on` show
71bufferFor :: SockAddr -> Accessor UDPState BSL.ByteString
72bufferFor = DAC.mapDefault BSL.empty
74bufferAppend :: SockAddr -> BSS.ByteString -> UDPState -> UDPState
75bufferAppend addr bytes =
76 bufferFor addr ^: flip BSL.append (BSL.fromChunks [bytes])
78recvBinary :: Binary a => Socket -> IORef UDPState -> Int -> IO (a, SockAddr)
79recvBinary sock st bufferSize = do
80 (bytes, addr) <- recvWithLength sock st bufferSize
81 return (decode bytes, addr)
83recvWithLength :: Socket
86 -> IO (BSL.ByteString, SockAddr)
87recvWithLength sock st bufferSize = do
88 (len, addr) <- recvExact sock 4 st bufferSize
89 let n = decodeInt32 . BSS.concat . BSL.toChunks $ len
90 bytes <- recvExactFrom addr sock n st bufferSize
93-- Receive all bytes currently in the buffer
94recvAll :: Socket -> IORef UDPState -> Int -> IO SockAddr
95recvAll sock st bufferSize = do
96 (bytes, addr) <- NBS.recvFrom sock bufferSize
97 modifyIORef st $ bufferAppend addr bytes
104 -> IO (BSL.ByteString, SockAddr)
105recvExact sock n st bufferSize = do
106 addr <- recvAll sock st bufferSize
107 bytes <- recvExactFrom addr sock n st bufferSize
110recvExactFrom :: SockAddr
116recvExactFrom addr sock n st bufferSize = go
118 go :: IO BSL.ByteString
120 accAddr <- (^. bufferFor addr) <$> readIORef st
121 if BSL.length accAddr >= fromIntegral n
123 let (bytes, accAddr') = BSL.splitAt (fromIntegral n) accAddr
124 modifyIORef st $ bufferFor addr ^= accAddr'
127 _ <- recvAll sock st bufferSize
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list