http - 为什么在打印hGetContents 的结果后不能使用hPutStr?

标签 http haskell network-programming

我是 stackoverflow 的新手,所以如果我做错了什么请原谅我。我试图了解一个简单的服务器如何在 Haskell 中工作。我想我遗漏了一些关于 hGetContents 如何工作的非常简单或基本的东西。

import Network 
import System.IO

main = withSocketsDo $ do
     socket <- listenOn $ PortNumber 5002
     (h, _, _) <- accept socket
     c <- hGetContents h
--   putStrLn c  -- doesn't work
--   putStrLn $ head $ lines c -- works!
--   putStrLn $ unlines $ take 2 $ lines c -- works!
--   putStrLn $ unlines $ take 3 $ lines c -- works!
--   putStrLn $ unlines $ take 6 $ lines c -- works!
     putStrLn $ unlines $ take 10 $ lines c -- doesn't work
     hPutStr h $ "HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nHello!\r\n"
     hClose h

运行程序后,我通过网络浏览器导航到 http://localhost:5002 .问题似乎是,根据我对句柄内容的解析程度,我最终无法发送响应。我希望能够在发送响应之前解析请求。我在代码中评论了有效的案例和无效的案例。 Hoogle 说对于 hGetContents(惰性)句柄在读取时是“半封闭的”。我是在误解懒惰还是在开始解析其内容后就应该认为句柄已关闭?

我收到的错误是“hPutChar:资源消失(破损的管道)”。感谢您的帮助。

最佳答案

我试图重现你的问题。为此,我执行了您的代码并使用 nc 向它发送了一个请求:

printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11" | nc localhost 5002

正如预期的那样,服务器(您问题中的代码)打印出前 10 行并退出,没有任何错误。客户端(nc)打印:

HTTP/1.0 200 OK
Content-Length: 5
Hello!

并且也没有错误地退出。

所以,起初我不明白你的问题是什么,但后来我尝试发送一个较小的请求:

printf "1\n2\n3\n4\n5\n6\n" | nc localhost 5002

服务器打印前 6 行并没有退出。客户端也没有退出,所以我用 Ctrl-C 中断了它,之后服务器退出并出现“资源消失”错误。

我想了想,它开始对我有意义。我不太了解惰性 IO,所以如果我的解释不清楚或不正确,如果有更好理解的人会改进它会有所帮助。

让我们遵循您的代码。第一:

(h, _, _) <- accept socket
c <- hGetContents h

您打开句柄并阅读其内容。注意句柄是惰性的,得到的内容也是惰性的。当我们说某些东西是惰性的时,我们的意思是它可以在不被求值的情况下传递(它通常被称为“按名称调用”与“按值调用”)。

现在:

putStrLn $ unlines $ take 10 $ lines c

在这里,您将惰性的、未评估的内容传递给另一个函数 take 10take 10 将尝试评估 列表的前 10 个元素并返回它们,如果列表中的元素少于 10 个,它将简单地返回所有元素。在 take 10 之后,我们有 putStrLnunlines,它们都与惰性完美兼容。

现在假设客户端发送了一个只有 6 行长的输入,然后开始等待响应。我们的服务器延迟接收内容并尝试打印前 10 行。首先,take 10 函数愉快地使用前 6 行并将它们传递给 putStrLn。 unlines,然后会发生什么? take 10 不能直接完成它的输出,因为绝对没有迹象表明它已经结束。句柄仍然打开,字节仍然可以从客户端 float 到服务器,所以它只是等待更多的输入。

这种行为可以通过运行观察到:

nc localhost 5002

然后手动输入 10 行。输入将在您键入时逐行显示在服务器上。在您键入第 10 行后,服务器将响应“Hello”消息。

P.S:我猜你描述的行为发生是因为你的网络浏览器发送了 6 到 9 行的请求。

要测试、调试和分析这种低级服务器,您应该使用简单的工具,如 nccurl 而不是您的网络浏览器:)

关于http - 为什么在打印hGetContents 的结果后不能使用hPutStr?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29689065/

相关文章:

haskell - 如何修改状态单子(monad)?

c++ - 如何在本地 Windows 网络上发现我的应用程序的所有其他实例?

c - 自动将一个套接字转发到另一个套接字

http - 使用 HTTP PUT 方法测试文件上传

jquery - 如何在用户在论坛上发帖时保持他们的 session 活跃?

haskell - Reader monad 可以做哪些应用函数不能做的事情?

c - 如何在 Linux 中获取 NIC RX 环数

c++ - 具有保持事件状态 (HTTP) 的 recv 功能 block

python - Scapy 中 HTTP 连接的低级管理

haskell - 如何在 Haskell 中以更好/更惯用的方式编写我的展开器版本?