Кодировать размер двоичной кодировки с помощью Data.Binary. - PullRequest
2 голосов
/ 30 апреля 2020

Я работаю с проводным протоколом, который вежливо кодирует общую длину сообщения в байтах как Int32le в первых 4 байтах сообщения. Это похоже на довольно распространенный вариант использования, но я не вижу очевидной функции для него в Data.Binary. Есть ли стандартный способ обработки кодирования в таком формате, как этот?

Кроме того, пока у вас есть, есть ли стандартный способ для чтения такого протокола с помощью Network.Socket? Я думаю, что я прочитал первые 4 байта, расшифровал их, а затем использовал это число для прочтения остальной части сообщения.


Для контекста, здесь BSON spe c. Мы пытаемся записать упакованные векторы в пользовательские двоичные поля (0x80-0xFF в элементе binary), которые не поддерживаются текущей библиотекой BSON , поэтому нам нужно немного поэкспериментировать.

1 Ответ

1 голос
/ 30 апреля 2020

Вот как бы я это сделал. Я не уверен, какая часть, по вашему мнению, нуждается в дополнительной служебной функции.

Сервер / отправитель:

import Data.Binary
import Data.Binary.Put
import qualified Data.ByteString.Lazy as BS
import Network.Run.TCP
import Network.Socket
import Network.Socket.ByteString.Lazy

sendWithLength :: Binary a => Socket -> a -> IO ()
sendWithLength s payload = do
  let payloadBS = encode payload
  let payloadLen = BS.length payloadBS
  let payloadLenBS = runPut $ putInt32le $ fromIntegral payloadLen
  sendAll s payloadLenBS
  sendAll s payloadBS

handleClient :: Socket -> IO ()
handleClient s = do
  sendWithLength s "Hello, World!"
  sendWithLength s ([123, 45, 678] :: [Int])

main :: IO ()
main = runTCPServer Nothing "3000" handleClient

Клиент / получатель:

import Data.Binary
import Data.Binary.Get
import qualified Data.ByteString.Lazy as BS
import Data.Int
import Network.Run.TCP
import Network.Socket
import Network.Socket.ByteString.Lazy

-- Like recv, but doesn't return a partial result unless the socket reaches
-- EOF.
recvAll :: Socket -> Int64 -> IO BS.ByteString
recvAll s n = do
  bytes <- recv s n
  let len = BS.length bytes
  if BS.null bytes || BS.length bytes == n
    then
      return bytes
    else do
      bytes' <- recvAll s (n - len)
      return $ bytes <> bytes'

recvWithLength :: Binary a => Socket -> IO a
recvWithLength s = do
  payloadLenBS <- recvAll s 4
  let payloadLen = fromIntegral $ runGet getInt32le payloadLenBS
  payloadBS <- recvAll s payloadLen
  let payload = decode payloadBS
  return payload

handleServer :: Socket -> IO ()
handleServer s = do
  payload1 <- recvWithLength s
  putStrLn payload1
  payload2 <- recvWithLength s
  print (payload2 :: [Int])

main :: IO ()
main = runTCPClient "127.0.0.1" "3000" handleServer

Кстати Network.Run.TCP из network-run пакет .

...