mirror of https://github.com/voidlizard/hbs2
135 lines
4.0 KiB
Haskell
135 lines
4.0 KiB
Haskell
{-# Language UndecidableInstances #-}
|
|
{-# Language RecordWildCards #-}
|
|
module HBS2.Net.Messaging.UDP where
|
|
|
|
import HBS2.Prelude
|
|
import HBS2.OrDie
|
|
import HBS2.Defaults
|
|
import HBS2.Net.IP.Addr
|
|
import HBS2.Net.Messaging
|
|
|
|
import Data.Function
|
|
import Control.Monad.Trans.Maybe
|
|
import Control.Monad.Trans.Cont
|
|
import Control.Concurrent.STM.TQueue qualified as Q0
|
|
import Data.ByteString.Lazy (ByteString)
|
|
import Data.ByteString.Lazy qualified as LBS
|
|
import Data.List qualified as L
|
|
import Data.Maybe
|
|
import Data.Text qualified as Text
|
|
import Lens.Micro.Platform
|
|
import Network.Socket
|
|
import Network.Socket.ByteString
|
|
import Network.Multicast
|
|
|
|
import UnliftIO
|
|
|
|
-- One address - one peer - one messaging
|
|
data MessagingUDP =
|
|
MessagingUDP
|
|
{ listenAddr :: SockAddr
|
|
, sink :: TQueue (From L4Proto, ByteString)
|
|
, inbox :: TQueue (To L4Proto, ByteString)
|
|
, sock :: TVar (Maybe Socket)
|
|
, mcast :: Bool
|
|
}
|
|
|
|
getOwnPeer :: MessagingUDP -> Peer L4Proto
|
|
getOwnPeer mess = PeerL4 UDP (listenAddr mess)
|
|
|
|
newMessagingUDPMulticast :: MonadUnliftIO m => String -> m (Maybe MessagingUDP)
|
|
newMessagingUDPMulticast s = runMaybeT $ do
|
|
|
|
(host, port) <- MaybeT $ pure $ getHostPort (Text.pack s)
|
|
|
|
so <- liftIO $ multicastReceiver host port
|
|
|
|
liftIO $ setSocketOption so ReuseAddr 1
|
|
|
|
a <- liftIO $ getSocketName so
|
|
|
|
liftIO $ MessagingUDP a <$> Q0.newTQueueIO
|
|
<*> Q0.newTQueueIO
|
|
<*> newTVarIO (Just so)
|
|
<*> pure True
|
|
|
|
isUDPSocketClosed :: MonadUnliftIO m => MessagingUDP -> m Bool
|
|
isUDPSocketClosed MessagingUDP{..} = readTVarIO sock <&> isNothing
|
|
|
|
newMessagingUDP :: (MonadUnliftIO m) => Bool -> Maybe String -> m (Maybe MessagingUDP)
|
|
newMessagingUDP reuse saddr =
|
|
case saddr of
|
|
Just s -> do
|
|
runMaybeT $ do
|
|
l <- MaybeT $ liftIO $ parseAddrUDP (Text.pack s) <&> listToMaybe . sorted
|
|
let a = addrAddress l
|
|
so <- liftIO $ socket (addrFamily l) (addrSocketType l) (addrProtocol l)
|
|
|
|
when reuse $ do
|
|
liftIO $ setSocketOption so ReuseAddr 1
|
|
|
|
liftIO $ MessagingUDP a <$> Q0.newTQueueIO
|
|
<*> Q0.newTQueueIO
|
|
<*> newTVarIO (Just so)
|
|
<*> pure False
|
|
|
|
|
|
Nothing -> do
|
|
so <- liftIO $ socket AF_INET Datagram defaultProtocol
|
|
sa <- liftIO $ getSocketName so
|
|
|
|
liftIO $ Just <$> ( MessagingUDP sa <$> Q0.newTQueueIO
|
|
<*> Q0.newTQueueIO
|
|
<*> newTVarIO (Just so)
|
|
<*> pure False
|
|
)
|
|
|
|
where
|
|
sorted = L.sortBy ( compare @Integer `on` proto)
|
|
proto x = case addrAddress x of
|
|
SockAddrInet{} -> 0
|
|
SockAddrInet6{} -> 1
|
|
SockAddrUnix{} -> 2
|
|
|
|
|
|
-- FIXME: stopping
|
|
|
|
runMessagingUDP :: MonadUnliftIO m => MessagingUDP -> m ()
|
|
runMessagingUDP MessagingUDP{..} = void $ flip runContT pure do
|
|
|
|
let addr = listenAddr
|
|
so <- liftIO (readTVarIO sock) >>= orThrowUser "UDP socket is not ready"
|
|
|
|
void $ ContT $ bracket (pure (Just so)) $ \case
|
|
Just so -> liftIO (close so >> atomically (writeTVar sock Nothing))
|
|
Nothing -> pure ()
|
|
|
|
unless mcast $ do
|
|
liftIO $ bind so addr
|
|
|
|
w <- ContT $ withAsync do
|
|
forever $ liftIO do
|
|
(msg, from) <- recvFrom so defMaxDatagram
|
|
liftIO $ atomically $
|
|
Q0.writeTQueue sink (From (PeerL4 UDP from), LBS.fromStrict msg)
|
|
|
|
link w
|
|
|
|
waitCatch w >>= either throwIO (const $ pure ())
|
|
|
|
instance Messaging MessagingUDP L4Proto ByteString where
|
|
|
|
sendTo bus (To whom) _ msg = liftIO do
|
|
-- atomically $ Q0.writeTQueue (inbox bus) (To whom, msg)
|
|
mso <- readTVarIO (sock bus)
|
|
for_ mso $ \so -> do
|
|
sendAllTo so (LBS.toStrict msg) (view sockAddr whom)
|
|
|
|
receive bus _ = liftIO do
|
|
-- so <- readTVarIO (sock bus)
|
|
-- (msg, from) <- recvFrom so defMaxDatagram
|
|
-- pure [(From (PeerUDP from), LBS.fromStrict msg)]
|
|
|
|
liftIO $ atomically $ Q0.readTQueue (sink bus) <&> L.singleton
|
|
|