Я построил крошечный передатчик и приемник UDP/protobuf. Я потратил утро, пытаясь отследить, почему протобуф-декодирование создает ошибки, только чтобы обнаружить, что это передатчик (Spoke.hs), который отправлял неверные данные.Почему Haskell/unpack возится с моими байтами?
Используемый код unpack
, чтобы включить Lazy.ByteStrings в строки, которые отправит сетевой пакет. Я нашел unpack
в Hoogle. Это не может быть функция, которую я ищу, но ее описание выглядит следующим образом: «O (n) Преобразует байтовую строку в строку».
Spoke.hs производит следующий вывод:
[email protected]:~/Dropbox/haskell-workspace/hub/dist/build/spoke$ ./spoke
45
45
["a","8","4a","6f","68","6e","20","44","6f","65","10","d2","9","1a","10","6a","64","6f","65","40","65","78","61","6d","70","6c","65","2e","63","6f","6d","22","c","a","8","35","35","35","2d","34","33","32","31","10","1"]
В то время как Wireshark показывает мне, что данные в пакете:
0a:08:4a:6f:68:6e:20:44:6f:65:10:c3:92:09:1a:10:6a:64:6f:65:40:65:78:61:6d:70:6c:65:2e:63:6f:6d:22:0c:0a:08:35:35:35:2d:34:33:32:31:10
Длина (45) является то же самое от Spoke.hs и Wireshark.
В Wireshark отсутствует последний байт (значение Ox01), а поток центральных значений отличается (и один байт больше в Wireshark).
"65","10","d2","9"
в Spoke.hs против 65:10:c3:92:09
в Wireshark.
Как 0x10 является DLE, мне показалось, что, вероятно, происходит некоторое ускорение, но я не знаю, почему.
У меня есть многолетнее доверие к Wireshark и всего несколько десятков часов опыта Haskell, поэтому я предположил, что это код, который виноват.
Любые предложения оценены.
-- Spoke.hs:
module Main where
import Data.Bits
import Network.Socket -- hiding (send, sendTo, recv, recvFrom)
-- import Network.Socket.ByteString
import Network.BSD
import Data.List
import qualified Data.ByteString.Lazy.Char8 as B
import Text.ProtocolBuffers.Header (defaultValue, uFromString)
import Text.ProtocolBuffers.WireMessage (messageGet, messagePut)
import Data.Char (ord, intToDigit)
import Numeric
import Data.Sequence ((><), fromList)
import AddressBookProtos.AddressBook
import AddressBookProtos.Person
import AddressBookProtos.Person.PhoneNumber
import AddressBookProtos.Person.PhoneType
data UDPHandle =
UDPHandle {udpSocket :: Socket,
udpAddress :: SockAddr}
opensocket :: HostName --^Remote hostname, or localhost
-> String --^Port number or name
-> IO UDPHandle --^Handle to use for logging
opensocket hostname port =
do -- Look up the hostname and port. Either raises an exception
-- or returns a nonempty list. First element in that list
-- is supposed to be the best option.
addrinfos <- getAddrInfo Nothing (Just hostname) (Just port)
let serveraddr = head addrinfos
-- Establish a socket for communication
sock <- socket (addrFamily serveraddr) Datagram defaultProtocol
-- Save off the socket, and server address in a handle
return $ UDPHandle sock (addrAddress serveraddr)
john = Person {
AddressBookProtos.Person.id = 1234,
name = uFromString "John Doe",
email = Just $ uFromString "[email protected]",
phone = fromList [
PhoneNumber {
number = uFromString "555-4321",
type' = Just HOME
}
]
}
johnStr = B.unpack (messagePut john)
charToHex x = showIntAtBase 16 intToDigit (ord x) ""
main::IO()
main =
do udpHandle <- opensocket "localhost" "4567"
sent <- sendTo (udpSocket udpHandle) johnStr (udpAddress udpHandle)
putStrLn $ show $ length johnStr
putStrLn $ show sent
putStrLn $ show $ map charToHex johnStr
return()
Документация, которую я вижу для пакета bytestring, перечисляет 'unpack' как преобразование' ByteString' в '[Word8]', что не совпадает с 'String'. Я бы ожидал некоторую разницу в байтах между 'ByteString' и' String', потому что 'String' - это данные Unicode, а' ByteString' - просто эффективный массив байтов, но 'unpack' не должен иметь возможность создавать' String' в первое место. –
Можете ли вы использовать сетевое тестирование, чтобы избежать избыточных преобразований данных? –
@MatthewWalton: 'распаковать' из' Data.ByteString.Char8', или ленивый вариант, вывести 'String'. Тем не менее, они не поддерживают Unicode. –