@@ -0,0 +1,138 @@
1+{-# LANGUAGE CPP, BangPatterns #-}
9+ ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
10+ , SocketType (Stream), SocketOption (ReuseAddr)
11+ , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
13+ , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
14+import System.Environment (getArgs, withArgs)
15+import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
16+import System.IO (withFile, IOMode(..), hPutStrLn, Handle, stderr)
17+import Control.Concurrent (forkIO)
18+import Control.Concurrent.MVar (MVar, newEmptyMVar, takeMVar, putMVar)
19+import qualified Network.Socket as N
21+import Data.ByteString (ByteString)
22+import Data.ByteString.Char8 (pack, unpack)
23+import qualified Data.ByteString as BS
24+import qualified Network.Socket.ByteString as NBS
25+import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
26+import Data.ByteString.Internal as BSI
27+import Foreign.Storable (pokeByteOff, peekByteOff)
28+import Foreign.C (CInt(..))
29+import Foreign.ForeignPtr (withForeignPtr)
31+foreign import ccall unsafe "htonl" htonl :: CInt -> CInt
32+foreign import ccall unsafe "ntohl" ntohl :: CInt -> CInt
36+ [pingsStr] <- getArgs
37+ serverReady <- newEmptyMVar
38+ clientDone <- newEmptyMVar
42+ putStrLn "server: creating TCP connection"
43+ serverAddrs <- getAddrInfo
44+ (Just (defaultHints { addrFlags = [AI_PASSIVE] } ))
47+ let serverAddr = head serverAddrs
48+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
49+ setSocketOption sock ReuseAddr 1
50+ bindSocket sock (addrAddress serverAddr)
52+ putStrLn "server: awaiting client connection"
53+ putMVar serverReady ()
55+ (clientSock, clientAddr) <- accept sock
57+ -- Set up an mvar that we will listen on and echo to the client
59+ forkIO $ forever $ do
63+ -- Read from the client and output on the channel
64+ putStrLn "server: listening for pings"
70+ let pings = read pingsStr
71+ serverAddrs <- getAddrInfo
75+ let serverAddr = head serverAddrs
76+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
78+ N.connect sock (addrAddress serverAddr)
81+ putMVar clientDone ()
83+ -- Wait for the client to finish
86+pingMessage :: ByteString
87+pingMessage = pack "ping123"
89+ping :: Socket -> Int -> IO ()
90+ping sock pings = go pings
94+ putStrLn $ "client did " ++ show pings ++ " pings"
96+ before <- getCurrentTime
97+ send sock pingMessage
99+ after <- getCurrentTime
100+ -- putStrLn $ "client received " ++ unpack bs
101+ let latency = (1e6 :: Double) * realToFrac (diffUTCTime after before)
102+ hPutStrLn stderr $ show i ++ " " ++ show latency
105+pong :: Socket -> MVar ByteString -> IO ()
108+ -- putStrLn $ "server received " ++ unpack bs
109+ when (BS.length bs > 0) $ do
113+-- | Wrapper around NBS.recv (for profiling)
114+recv :: Socket -> Int -> IO ByteString
116+ header <- NBS.recv sock 4
117+ length <- decodeLength header
118+ NBS.recv sock (fromIntegral (length :: Int32))
120+-- | Wrapper around NBS.send (for profiling)
121+send :: Socket -> ByteString -> IO ()
123+ length <- encodeLength (fromIntegral (BS.length bs))
124+ NBS.sendMany sock [length, bs]
126+-- | Encode length (manual for now)
127+encodeLength :: Int32 -> IO ByteString
129+ BSI.create 4 $ \p ->
130+ pokeByteOff p 0 (htonl (fromIntegral i32))
132+-- | Decode length (manual for now)
133+decodeLength :: ByteString -> IO Int32
135+ let (fp, _, _) = BSI.toForeignPtr bs in
136+ withForeignPtr fp $ \p -> do
137+ w32 <- peekByteOff p 0
138+ return (fromIntegral (ntohl w32))