Frage

So schreibe ich ein Packet Sniffing App. Im Grunde wollte ich es sniff für TCP-Sitzungen, und analysieren sie dann zu sehen, ob sie http sind, und wenn sie sind, und wenn sie das Recht haben, Inhaltstyp, usw., speichern Sie sie als Datei auf meiner Festplatte.

Also, zu diesem Zweck wollte ich es effizient sein. Da die aktuellen http Bibliothek String basiert, und ich werde mit großen Dateien, und ich nur wirklich notwendig, um Parst HTTP-Antworten, habe ich beschlossen, meine eigenen in attoparsec zu rollen.

Wenn ich mein Programm beendet ist, fand ich, dass, wenn ich in eine 9 meg HTTP-Antwort mit einer WAV-Datei Parsen, wenn ich es profilierte, wurde sie einen Gig von Zuweisen von Speicher, wenn er versuchte, den Körper zu analysieren aus die hTTP-Antwort. Als ich im Blick HTTP.prof sehe ich ein paar Zeilen:

httpBody              Main                                                 362           1   0.0    0.0    93.8   99.3

 take                 Data.Attoparsec.Internal                             366        1201   0.0    0.0    93.8   99.3
     takeWith            Data.Attoparsec.Internal                             367        3603   0.0    0.0    93.8   99.3
      demandInput        Data.Attoparsec.Internal                             375         293   0.0    0.0    93.8   99.2
       prompt            Data.Attoparsec.Internal                             378         293   0.0    0.0    93.8   99.2
        +++              Data.Attoparsec.Internal                             380         586  93.8   99.2    93.8   99.2

So wie Sie sehen können, irgendwo innerhalb httpbody, nehmen 1201 mal aufgerufen wird, verursacht 500+ (+++) Verkettungen von bytestrings, was eine absurde Menge an Speicherzuweisung verursacht.

Hier ist der Code. N ist nur die Inhaltslänge der HTTP-Antwort, wenn es einen gibt. Wenn es nicht man es nur versucht, alles zu nehmen.

ich es wollte einen faulen bytestring von 1000 oder so Zeichen bytestrings zurück, aber selbst wenn ich es ändern, um nur zu nehmen n und gibt eine strenge bytestring, es hat immer noch diese Zuweisungen darin (und verwendet 14 GB Speicher) .


httpBody n = do
  x <- if n > 0
    then AC.take n
    else AC.takeWhile (\_ -> True)
  if B.length x == 0
    then return Nothing
    else return (Just x)

Ich lese in ein Blog von dem Kerl, die combinatorrent tat und er war das gleiche Problem hat, aber ich habe nie von einer Auflösung gehört. Hat jemand schon einmal über dieses Problem laufen, bevor oder eine Lösung gefunden?

Edit: Okay, gut verließ ich diese auf den ganzen Tag und bekam nichts. Nach der Untersuchung des Problems glaube ich nicht, dass es eine Möglichkeit, es zu tun, ist ein fauler bytestring Accessor attoparsec ohne Zugabe. Ich habe auch bei allen anderen Bibliotheken gesucht und sie entweder bytestrings oder andere Dinge fehlten.

gefunden Also habe ich eine Abhilfe. Wenn Sie über eine HTTP-Anforderung zu denken, geht es Header, Newline, Newline, Körper. Da der Körper letzte ist, und gibt ein Tupel mit den beiden Parsen, was Sie analysiert und was der bytestring übrig ist, kann ich überspringen den Körper innerhalb attoparsec Parsen und stattdessen den Körper gerade aus dem bytestring zupfen die noch vorhanden sind.


parseHTTPs bs = if P.length results == 0
  then Nothing
  else Just results
  where results = foldParse(bs, [])

foldParse (bs,rs) = case ACL.parse httpResponse bs of
  ACL.Done rest r -> addBody (rest,rs) r
  otherwise ->  rs

addBody (rest,rs) http = foldParse (rest', rs')
  where
    contentlength = ((read . BU.toString) (maybe "0" id (hdrContentLength (rspHeaders http))))
    rest' = BL.drop contentlength rest
    rs' = rs ++ [http { rspBody = body' }]
    body'
      | contentlength == 0  = Just rest
      | BL.length rest == 0 = Nothing
      | otherwise           = Just (BL.take contentlength rest)
httpResponse = do
  (code, desc) <- statusLine
  hdrs <- many header
  endOfLine
--  body <- httpBody ((read . BU.toString) (maybe "0" id (hdrContentLength parsedHeaders)))

  return Response { rspCode = code, rspReason = desc, rspHeaders = parseHeaders hdrs,  rspBody = undefined }

Es ist ein wenig chaotisch, aber letztlich es funktioniert schnell und ordnet nichts mehr, als ich wollte. Also im Grunde falten Sie die über bytestring sammeln http Datenstrukturen, dann zwischen Sammlungen, ich die Inhaltslänge der Struktur prüfe ich gerade habe, ziehe eine entsprechende Menge aus dem verbleibenden bytestring, und dann weiter, wenn es irgendein links ByteString.

Edit: Ich beenden tatsächlich dieses Projekt auf. Klappt wunderbar. Ich bin nicht richtig cabalized aber wenn jemand will die gesamte Quelle anzuzeigen, können Sie es unter https finden: // github.com/onmach/Audio-Sniffer .

War es hilfreich?

Lösung

combinatorrent Kerl hier:)

Wenn der Speicher dient, das Problem mit attoparsec ist, dass Anforderungen Eingang ein wenig in einer Zeit, einen faulen bytestring aufzubauen, die schließlich verkettet. Meine „Lösung“ war die Eingangsfunktion selbst zu rollen. Das heißt, ich den Eingangsstrom für attoparsec von einer Netzwerk-Socket erhalten, und ich weiß, wie viele Bytes in einer Nachricht zu erwarten. Im Grunde habe ich in zwei Fällen:

  • Die Botschaft ist klein. Read 4k aus der Steckdose und essen, ein wenig in einer Zeit ByteString (Scheiben bytestrings sind schnell und wir wegwerfen, die 4k, nachdem sie erschöpft ist)

  • Die Botschaft ist „large“ (groß bedeutet hier rund 16 Kilobyte in Bittorrent sprechen): Wir berechnen, wie viel die 4k chunk wir haben erfüllen können, und dann fordern wir einfach den zugrunde liegenden Netzwerk-Socket zu füllen Dinge in. wir haben jetzt zwei bytestrings, den verbleibenden Teil des 4k Brocken und den großen Brocken. Sie haben alle Daten, so dass diejenigen, verketten und Parsen sie in das, was wir tun.

    Sie können in der Lage sein, sich die Verkettung Schritt zu optimieren.

Der TL; DR-Version. Ich kann es außerhalb attoparsec handhaben und die Schleife Handroll um das Problem zu vermeiden

Die entsprechende combinatorrent begehen ist fc131fe24 finden Sie unter

https://github.com/jlouis/combinatorrent/commit/fc131fe24207909dd980c674aae6aaba27b966d4

für die Details.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top