टीसीपी की आवश्यकता है कि एप्लिकेशन अपना संदेश सीमा मार्कर प्रदान करे। संदेश सीमाओं को चिह्नित करने के लिए एक सरल प्रोटोकॉल डेटा के एक हिस्से की लंबाई, डेटा का हिस्सा, और शेष संदेश हैं जो एक ही संदेश का हिस्सा हैं। संदेश सीमा की जानकारी रखने वाले शीर्षलेख के लिए इष्टतम आकार संदेश आकारों के वितरण पर निर्भर करता है।
अपना खुद का छोटा संदेश प्रोटोकॉल विकसित करना, हम अपने शीर्षकों के लिए दो बाइट्स का उपयोग करेंगे। बाइट्स से सबसे महत्वपूर्ण बिट (Word16
के रूप में माना जाता है) संदेश में शेष भाग हैं या नहीं, यह धारण करेगा। शेष 15 बिट्स बाइट्स में संदेश की लंबाई रखेंगे। यह ठेठ टीसीपी पैकेट की तुलना में 32k तक की चंक आकारों की अनुमति देगा। यदि संदेश आमतौर पर बहुत छोटे होते हैं, तो विशेष रूप से यदि वे 127 बाइट से छोटे होते हैं तो दो बाइट हेडर कम से कम इष्टतम होंगे।
हम अपने कोड के नेटवर्किंग हिस्से के लिए network-simple का उपयोग करने जा रहे हैं। हम binary पैकेज के साथ संदेशों को क्रमबद्ध या deserialize करेंगे जो encode
एस और decode
एस आलसी ByteString
एस से और उसके लिए है।
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString as B
import Network.Simple.TCP
import Data.Bits
import Data.Binary
import Data.Functor
import Control.Monad.IO.Class
पहले उपयोगिता हम की आवश्यकता होगी सख्त ByteString
रों में Word16
हेडर लिखने और उन्हें वापस बाहर फिर से पढ़ने की क्षमता है। हम उन्हें बड़े एंडियन क्रम में लिखेंगे। वैकल्पिक रूप से, इन्हें Binary
उदाहरण Word16
के संदर्भ में लिखा जा सकता है।
writeBE :: Word16 -> B.ByteString
writeBE x = B.pack . map fromIntegral $ [(x .&. 0xFF00) `shiftR` 8, x .&. 0xFF]
readBE :: B.ByteString -> Maybe Word16
readBE s =
case map fromIntegral . B.unpack $ s of
[w1, w0] -> Just $ w1 `shiftL` 8 .|. w0
_ -> Nothing
मुख्य चुनौती भेजने के लिए और आलसी ByteString
रों द्विआधारी पैकेज से हम पर मजबूर प्राप्त करने के लिए किया जाएगा। चूंकि हम एक समय में केवल 32k बाइट्स भेजने में सक्षम होंगे, इसलिए हमें rechunk
भाग में आलसी बाइटस्ट्रिंग करने में सक्षम होना चाहिए, जो कुल मिलाकर लंबाई से अधिक नहीं है। एक सिंगल हिस्सा पहले से कहीं अधिक हो सकता है; कोई भी हिस्सा जो हमारे नए हिस्सों में फिट नहीं होता है, कई हिस्सों में विभाजित होता है।
rechunk :: Int -> [B.ByteString] -> [(Int, [B.ByteString])]
rechunk n = go [] 0 . filter (not . B.null)
where
go acc l [] = [(l, reverse acc)]
go acc l (x:xs) =
let
lx = B.length x
l' = lx + l
in
if l' <= n
then go (x:acc) l' xs
else
let (x0, x1) = B.splitAt (n-l) x
in (n, reverse (x0:acc)) : go [] 0 (x1:xs)
recvExactly
बाइट्स हम अनुरोध के सभी जब तक इच्छा पाश प्राप्त किया गया है।
recvExactly :: MonadIO m => Socket -> Int -> m (Maybe [B.ByteString])
recvExactly s toRead = go [] toRead
where
go acc toRead = do
body <- recv s toRead
maybe (return Nothing) (go' acc toRead) body
go' acc toRead body =
if B.length body < toRead
then go (body:acc) (toRead - B.length body)
else return . Just . reverse $ acc
एक आलसी ByteString
भेजा जा रहा है एक आकार हम जानते हैं कि हम भेज सकते हैं के टुकड़ों में यह तोड़ने और हैडर आकार धारण और है कि क्या वहाँ किसी भी अधिक हिस्सा हैं के साथ प्रत्येक हिस्सा भेजने के होते हैं।
एक आलसी ByteString
प्राप्त दो बाइट हैडर पढ़ने, आकार हैडर ने संकेत का एक हिस्सा पढ़ने, और जब तक पढ़ने के लिए के रूप में शीर्ष लेख संकेत दिया अधिक मात्रा देखते हैं जारी होते हैं।
recvLazyBS :: (MonadIO m, Functor m) => Socket -> m (Maybe L.ByteString)
recvLazyBS s = fmap L.fromChunks <$> go []
where
go acc = do
header <- recvExactly s 2
maybe (return Nothing) (go' acc) (header >>= readBE . B.concat)
go' acc h = do
body <- recvExactly s . fromIntegral $ h .&. 0x7FFF
let next = if h .&. 0x8000 /= 0
then go
else return . Just . concat . reverse
maybe (return Nothing) (next . (:acc)) body
भेजा जा रहा है या है कि एक Binary
उदाहरण सिर्फ एक encode
घ आलसी ByteString
भेजना या इसे ing आलसी ByteString
और decode
प्राप्त कर रहा है एक संदेश प्राप्त करने।
sendBinary :: (MonadIO m, Binary a) => Socket -> a -> m()
sendBinary s = sendLazyBS s . encode
recvBinary :: (MonadIO m, Binary a, Functor m) => Socket -> m (Maybe a)
recvBinary s = d . fmap decodeOrFail <$> recvLazyBS s
where
d (Just (Right (_, _, x))) = Just x
d _ = Nothing
मैं आपके सभी प्रश्नों के जवाब कर सकते हैं नहीं है, लेकिन मैं नाली विश्वास करते हैं और पाइप आप से बचने के लिए मदद मिलेगी आलसी मैं/हे: बाहर [Data.Conduit.Binary] जाँच (https://hackage.haskell.org/ पैकेज/कंड्यूट-0.4.0/डॉक्स/डेटा-कंडिट-बाइनरी.html) और [पाइप्स.बेटस्ट्रिंग] (https://hackage.haskell.org/package/pipes-bytestring-2.1.1/docs/Pipes-ByteString ।एचटीएमएल) –
मेरे पास मेरे पुराने मानक नहीं हैं, लेकिन मुझे याद है कि यह एक बड़ी 'बाइटस्ट्रिंग' बनाने और उसे भेजने के बजाय सीधे सीधे 'hPut' और' hGet' आपकी संख्या को सॉकेट पर अधिक कुशल था। गति में अंतर 'hPut' /' hGet' के लिए 5 गुना तेज हो सकता है। इस प्रकार, उदाहरण के लिए, सभी 'blaze- * 'पैकेजों को उनकी गति में सुधार कैसे मिलता है। –
@ गैब्रियल गोंज़ालेज़, ऐसा इसलिए है क्योंकि 'एचपीयूटी' और 'एचजीएटी' उन कार्यों का उपयोग करते हैं जो पहले से ही अपने स्वयं के बफरिंग करते हैं? – dfeuer