2012-06-15 3 views
6

Я построил крошечный передатчик и приемник 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() 
+1

Документация, которую я вижу для пакета bytestring, перечисляет 'unpack' как преобразование' ByteString' в '[Word8]', что не совпадает с 'String'. Я бы ожидал некоторую разницу в байтах между 'ByteString' и' String', потому что 'String' - это данные Unicode, а' ByteString' - просто эффективный массив байтов, но 'unpack' не должен иметь возможность создавать' String' в первое место. –

+5

Можете ли вы использовать сетевое тестирование, чтобы избежать избыточных преобразований данных? –

+0

@MatthewWalton: 'распаковать' из' Data.ByteString.Char8', или ленивый вариант, вывести 'String'. Тем не менее, они не поддерживают Unicode. –

ответ

3

В документации я вижу списки байтовой строки пакета распаковывать как преобразование ByteString в [Word8], который не является такой же, как String. Я ожидал бы некоторую байтовую разницу между ByteString и String, потому что String - это данные в Юникоде, а ByteString - это просто эффективный массив байтов, но unpack не должен иметь возможность создавать String.

Таким образом, вы, вероятно, ошибаетесь в преобразовании Юникода здесь или, по крайней мере, что-то интерпретируете его как Юникод, когда базовые данные действительно отсутствуют, и это редко заканчивается.

+0

Нет, это также говорит 'unpack :: ByteString -> [Char]' (я думаю, что String является псевдонимом для [Char]). http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Char8.html#v:unpack – fadedbee

+1

Это 'Data.ByteString.Char8' - я искал в' Data. ByteString.Lazy'. Тем не менее, как отметил Джон Л. В комментариях к вопросу, который до сих пор не поддерживает Unicode. –

+2

Это, безусловно, Unicode-преобразование: например. [кодовая точка D8 - C3 98 в UTF-8] (http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=d8&mode=hex). Вот почему любое значение под 0x7F проходит через невредимое. – rxg

1

Я думаю, что вы хотите toString и fromString из utf8-string вместо unpack и pack. This blog post был очень полезен для меня.