2012-06-15 16 views
6

Ich habe einen kleinen UDP/Protobuf Sender und Empfänger gebaut. Ich habe den ganzen Vormittag damit zugebracht, herauszufinden, warum die Protoobuf-Dekodierung Fehler erzeugte, nur um festzustellen, dass es der Sender (Spoke.hs) war, der falsche Daten sendete.Warum ist Haskell/entpacken mit meinen Bytes?

Der Code unpack verwendet, um Lazy.ByteStrings in Strings zu verwandeln, die das Netzwerkpaket senden wird. Ich habe unpack in Hoogle gefunden. Es ist möglicherweise nicht die Funktion, die ich suche, aber seine Beschreibung sieht geeignet aus: "O (n) Konvertiert einen ByteString in einen String."

Spoke.hs die folgende Ausgabe erzeugt:

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

Während wireshark mir zeigt, dass die Daten in dem Paket ist:

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 

die Länge (45) das gleiche ist aus Spoke.hs und Wireshark.

Wireshark fehlt das letzte Byte (Wert Ox01) und ein Strom von zentralen Werten ist unterschiedlich (und ein Byte größer in Wireshark).

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

Da 0x10 DLE ist, fiel mir auf, dass es wahrscheinlich einige Entkommen gibt, aber ich weiß nicht warum.

Ich habe viele Jahre Vertrauen in Wireshark und nur ein paar Dutzend Stunden von Haskell Erfahrung, also habe ich angenommen, dass es der Code ist, der schuld ist.

Alle Vorschläge geschätzt.

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

Die Dokumentation, die ich für das Bytestring-Paket lese, listet 'entpack' auf, indem es einen' ByteString' in '[Word8]' umwandelt, was nicht dasselbe ist wie ein 'String'. Ich erwarte einen Byteunterschied zwischen 'ByteString' und' String', da 'String' Unicode-Daten sind, während' ByteString' nur ein effizientes Array von Bytes ist, aber 'unpack' nicht in der Lage sein sollte, einen' String' zu erzeugen erster Platz. –

+5

Können Sie Netzwerk-Bytestring verwenden, um redundante Datenkonvertierungen zu vermeiden? –

+0

@MatthewWalton: "entpacken" aus 'Data.ByteString.Char8', oder die faule Variante, Ausgabe' String's. Sie sind jedoch nicht Unicode-bewusst. –

Antwort

3

Die Dokumentation ich für die bytestring Paketlisten sehen entpacken eine ByteString-[Word8] Umwandlung, die nicht das gleiche wie ein String ist. Ich würde erwarten, einige Byte-Unterschied zwischen ByteString und String, weil String Unicode-Daten ist, während ByteString ist nur ein effizientes Array von Bytes, aber unpack sollte nicht in der Lage, eine String in erster Linie zu produzieren.

Sie fallen also wahrscheinlich mit Unicode-Konvertierung, oder zumindest etwas interpretiert es als Unicode, wenn die zugrunde liegenden Daten wirklich nicht sind und das selten gut endet.

+0

Nein, es sagt auch: entpacken :: ByteString -> [Char] '(Ich denke String ist ein Alias ​​für [Char]). http://hackage.haskell.org/packages/archive/bytestring/latest/doc/html/Data-ByteString-Char8.html#v:unpack – fadedbee

+1

Das ist 'Data.ByteString.Char8' - ich habe in' Data gesucht. ByteString.Lazy'. Nichtsdestotrotz, wie John L in den Kommentaren zu der Frage betont, die immer noch nicht Unicode-fähig ist. –

+2

Es ist definitiv Unicode-Konvertierung: z.B. [Codepunkt D8 ist C3 98 in UTF-8] (http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=d8&mode=hex). Deshalb wird jeder Wert unter 0x7F unbeschadet überstanden. – rxg

1

Ich glaube, Sie toString und fromString von utf8-string wollen werden statt unpack und pack. This blog post war sehr hilfreich für mich.

Verwandte Themen