I am sending simple UDP packets to this Haskell server. For a source of packets I use a plain text file generated by "aspell -l en dump master". However, any list of over 120,000 messages should work. If I start the consumer and producer at the same time, I do not loose packets. However, I want to be able to simulate a very busy consumer. If I introduce a threadDelay for 20 seconds before starting the consumer, I get packet loss. This to me is counter intuitive because I am doing less with standard out and disk IO when I delay consuming. Can anyone explain why I am getting loss with the delayed version? How can I manage the socket and TChan work better to not get any loss (just higher memory usage) while my consumer is very busy?
import Control.Monad (forever)
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.STM (writeTChan, readTChan, atomically)
import Control.Concurrent.STM.TChan
import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString
import Data.ByteString hiding(putStrLn, head)
import qualified Data.ByteString.Char8 as Char8 (putStrLn, putStr)
import System.IO
main :: IO ()
main = withSocketsDo $ do
hSetBuffering stdout NoBuffering
addrinfos <- getAddrInfo
(Just (defaultHints {addrFlags = [AI_PASSIVE]}))
Nothing (Just "2000")
let serveraddr = head addrinfos
sock <- socket (addrFamily serveraddr) Datagram defaultProtocol
bindSocket sock (addrAddress serveraddr)
chan <- newTChanIO
forkIO(producer chan sock)
-- Uncomment the threadDelay below to see lossy version
-- threadDelay (1000000 * 20)
forkIO(consumer chan)
forever $ threadDelay (1000000 * 60)
producer :: TChan ByteString -> Socket -> IO ()
producer chan sock = forever $ do
(msg) <- recv sock 256
atomically $ writeTChan chan msg
consumer :: TChan ByteString -> IO ()
consumer chan = forever $ do
msg <- atomically $ readTChan chan
Char8.putStr msg
The UDP packet loss is especially affected by TCP traffic and its flow control mechanism. This is because TCP flow control continues to increase its window size until packet loss occurs if the advertised window size is large enough.
The Asynchronous Serial Traffic over UDP feature provides the ability to encapsulate asynchronous data into User Datagram Protocol (UDP) packets and then unreliably send this data without needing to establish a connection with a receiving device.
Less than 0.01% is not unusual. Packet loss due to congestion obviously depends on how busy the link is. If there is spare capacity along the entire path, this number will be 0%. But as the network gets busy, this number will increase.
Why would you not expect data loss? This would happen in any language, not just Haskell, because the kernel has only a limited amount of buffer space for each socket. Since, unlike TCP, UDP is not flow controlled, the kernel will just drop packets. You can of course increase your buffer size with the SO_RCVBUF socket option. But fundamentally UDP is not reliable anyway, so any real application using it needs to handle packet loss.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With