所以我正在写一个数据包嗅探应用程序。基本上我希望它侦听tcp会话,然后解析它们,看它们是否是http,如果它们是,以及它们是否具有正确的内容类型等,则将它们保存为我的硬盘上的文件。Attoparsec在大量的'take'呼叫中分配一吨内存
所以,为此,我希望它是有效的。由于当前的http库是基于字符串的,我将处理大文件,而且我只需要解析http响应,所以我决定在attoparsec中推出自己的文件。
当我完成我的程序时,我发现当我解析一个9兆字节的http响应时,它带有一个wav文件,当我剖析它时,它正在分配一段内存,当它试图解析出身体的http响应。当我看着我HTTP.prof看到一些行:
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
因此,大家可以看到,内httpbody的地方,采取的是所谓的1201次,造成500+(+++)字节串的级联,这会导致荒谬的内存分配量。
这是代码。 N只是http响应的内容长度,如果有的话。如果没有一个它只是试图采取一切。
我希望它返回一个包含1000个左右字符字节串的惰性字节串,但即使我将它改为只取n并返回一个严格的字节串,它仍然具有这些分配(并且它使用14个内存)。
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)
我正在读那个combinatorrent的人的博客,他有同样的问题,但我从来没有听说过一个决议。有没有人曾经遇到过这个问题或找到解决方案?
编辑:好的,那么我整整一天都离开了这里,一无所有。在研究了这个问题之后,我认为没有一种方法可以在不向attoparsec添加惰性字节串访问器的情况下实现。我还查看了所有其他库,它们缺少字节串或其他东西。
所以我找到了一个解决方法。如果你考虑一个http请求,它会去头,换行符,换行符,正文。由于主体是最后一个,并且解析返回一个包含你解析的和剩余字节串的元组,所以我可以跳过解析attoparsec中的主体,而是直接从剩下的字符串中取出主体。
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 }
这是一个有点混乱,但最终它工作速度快,分配没有比我想要的更多。因此,基本上,您将收集HTTP数据结构的字符串进行折叠,然后在集合之间检查刚刚获得的结构的内容长度,从剩余的字符串中提取适当的数量,然后在剩下任何字符串时继续操作。
编辑:我实际完成了这个项目。奇迹般有效。我没有妥善封闭,但如果有人想查看整个资料来源,你可以在https://github.com/onmach/Audio-Sniffer找到它。
什么是AC? (填充) – 2010-11-11 21:39:12
使用[blaze-builder的碎片整理操作](http://lambda-view.blogspot.com/2010/11/defragmenting-lazy-bytestrings.html)是否解决了这个问题? – 2010-11-12 18:30:45
AC适用于Data.Attoparsec.Char8 – 2010-11-13 02:05:08