我是 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 10
。 take 10
将尝试评估 列表的前 10 个元素并返回它们,如果列表中的元素少于 10 个,它将简单地返回所有元素。在 take 10
之后,我们有 putStrLn
和 unlines
,它们都与惰性完美兼容。
现在假设客户端发送了一个只有 6 行长的输入,然后开始等待响应。我们的服务器延迟接收内容并尝试打印前 10 行。首先,take 10
函数愉快地使用前 6 行并将它们传递给 putStrLn。 unlines
,然后会发生什么? take 10
不能直接完成它的输出,因为绝对没有迹象表明它已经结束。句柄仍然打开,字节仍然可以从客户端 float 到服务器,所以它只是等待更多的输入。
这种行为可以通过运行观察到:
nc localhost 5002
然后手动输入 10 行。输入将在您键入时逐行显示在服务器上。在您键入第 10 行后,服务器将响应“Hello”消息。
P.S:我猜你描述的行为发生是因为你的网络浏览器发送了 6 到 9 行的请求。
要测试、调试和分析这种低级服务器,您应该使用简单的工具,如 nc
和 curl
而不是您的网络浏览器:)
关于http - 为什么在打印hGetContents 的结果后不能使用hPutStr?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29689065/