2012-06-15 14 views
6

Ho costruito un piccolo trasmettitore e ricevitore UDP/protobuf. Ho passato la mattinata cercando di rintracciare perché la decodifica di protobuf stava producendo errori, solo per scoprire che era il trasmettitore (Spoke.hs) che stava inviando dati errati.Perché Haskell/Unpack ha problemi con i miei byte?

Il codice utilizzato unpack per trasformare Lazy.ByteStrings in stringhe che il pacchetto di rete invierà. Ho trovato unpack in Hoogle. Potrebbe non essere la funzione che sto cercando, ma la sua descrizione sembra adeguata: "O (n) Converte un ByteString in una stringa."

Spoke.hs produce il seguente output:

[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"] 

Mentre wireshark mi mostra che i dati nel pacchetto è:

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 

La lunghezza (45) è lo stesso da Spoke.hs e Wireshark.

Wireshark manca l'ultimo byte (valore Ox01) e un flusso di valori centrali è diverso (e un byte più grande in Wireshark).

"65","10","d2","9" in Spoke.hs vs 65:10:c3:92:09 in Wireshark.

Poiché 0x10 è DLE, mi ha colpito il fatto che probabilmente ci sia qualche fuga in corso, ma non so perché.

Ho molti anni di fiducia in Wireshark e solo poche decine di ore di esperienza Haskell, quindi ho presunto che è il codice che è in difetto.

Qualsiasi suggerimento apprezzato.

-- 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

La documentazione che vedo per il pacchetto bytestring elenca 'unpack' come conversione di' ByteString' in '[Word8]', che non è la stessa cosa di 'String'. Mi aspetterei una certa differenza di byte tra 'ByteString' e' String' perché 'String' è dati Unicode mentre' ByteString' è solo una efficiente serie di byte, ma 'unpack' non dovrebbe essere in grado di produrre un' String' nel primo posto. –

+5

È possibile utilizzare il test della rete per evitare le conversioni di dati ridondanti? –

+0

@MatthewWalton: 'unpack' da' Data.ByteString.Char8', o la variante lazy, restituisce 'String's. Tuttavia non sono compatibili con Unicode. –

risposta

3

La documentazione che vedo per le liste dei pacchetti bytestring decomprimere come la conversione di un ByteString-[Word8], che non è la stessa di un String. Mi aspetterei una certa differenza di byte tra ByteString e String perché String è dati Unicode mentre ByteString è solo una matrice efficiente di byte, ma unpack non dovrebbe essere in grado di produrre un String in primo luogo.

Quindi probabilmente stai cadendo dalla conversione Unicode qui, o almeno qualcosa lo interpreta come Unicode quando i dati sottostanti in realtà non lo sono e raramente si concludono bene.

+0

No, dice anche 'unpack :: ByteString -> [Char]' (Credo che String sia un alias per [Char]). http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Char8.html#v:unpack – fadedbee

+1

Questo è 'Data.ByteString.Char8' - Stavo cercando in' Dati. ByteString.Lazy'. Nondimeno, come John L ha sottolineato nei commenti sulla domanda che non è ancora a conoscenza di Unicode. –

+2

È sicuramente la conversione Unicode: ad es. [il punto di codice D8 è C3 98 in UTF-8] (http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=d8&mode=hex). Ecco perché qualsiasi valore sotto 0x7F passa indenne. – rxg

1

penso che si vorrà toString e fromString da utf8-string invece di unpack e pack. This blog post è stato molto utile per me.

Problemi correlati