Http 为什么可以';打印hGetContents结果后,我是否使用hPutStr?
我是新手,所以如果我做错了什么,请原谅我。我试图理解一个简单的服务器在Haskell中是如何工作的。我想我遗漏了一些关于hGetContents如何工作的非常简单或基本的东西Http 为什么可以';打印hGetContents结果后,我是否使用hPutStr?,http,haskell,network-programming,Http,Haskell,Network Programming,我是新手,所以如果我做错了什么,请原谅我。我试图理解一个简单的服务器在Haskell中是如何工作的。我想我遗漏了一些关于hGetContents如何工作的非常简单或基本的东西 import Network import System.IO main = withSocketsDo $ do socket <- listenOn $ PortNumber 5002 (h, _, _) <- accept socket c <- hGetConte
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
导入网络
导入系统.IO
main=带库存的订单$do
socket当您在句柄上启动延迟读取时,您将放弃对句柄执行任何其他操作的权利,直到内容字符串完全强制执行为止,或者手动关闭句柄(此时尝试强制执行更多内容字符串将导致不良行为或错误)
TL;博士
在这种情况下,惰性I/O是不合适的。在套接字上进行延迟读取是合适的情况下,很可能是零指数。如果您愿意,您可以使用常规的严格I/O,或导管
,或管道
,或一些Haskell web框架,如Yesod或Scotty或其他各种竞争对手。我试图重现您的问题。为此,我执行了您的代码,并使用nc向其发送请求:
printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11" | nc localhost 5002
正如预期的那样,服务器(您问题中的代码)打印出前10行,并在没有任何错误的情况下退出。客户端(nc)打印:
也没有出错就退出了
所以,一开始我不明白你的问题是什么,但后来我尝试发送一个较小的请求:
printf "1\n2\n3\n4\n5\n6\n" | nc localhost 5002
服务器打印了前6行,但没有退出。客户端也没有退出,所以我用Ctrl-C中断了它,然后服务器退出,出现“资源消失”错误
我想了想,这对我来说开始有意义了。我对懒惰IO的理解不太好,所以如果我的解释不清楚或不正确,如果有更好理解的人能够改进它,这将是有帮助的
让我们按照您的代码进行操作。第一:
(h, _, _) <- accept socket
c <- hGetContents h
在这里,您可以将懒惰、未评估的内容传递给另一个函数take 10
take 10
将尝试计算列表中的前10个元素并返回它们,如果列表中的元素少于10个,则只返回所有元素。在take 10
之后,我们有putStrLn
和unlines
,这两者都与懒惰完全相容
现在让我们假设客户机发送一个只有6行长的输入,然后开始等待响应。我们的服务器延迟接收内容并尝试打印前10行。首先,take 10
函数愉快地使用前6行并将它们传递给putStrLn。取消线
,然后会发生什么take 10
不能仅仅完成它的输出,因为没有任何迹象表明它已经结束了。句柄仍然打开,字节仍然可以从客户端浮动到服务器,所以它只是等待更多的输入
可以通过运行以下程序来观察此行为:
nc localhost 5002
然后手动输入10行。输入将在您键入时逐行显示在服务器上。在您键入第10行后,服务器将以“Hello”消息响应
注:我猜您描述的行为是因为您的web浏览器在请求中发送了6到9行内容
要测试、调试和分析此类低级服务器,您应该使用简单的工具,如nc
和curl
,而不是web浏览器:)调用hGetContents
将句柄置于“半关闭”状态。在此点之后,不应对句柄执行任何操作。您应该只使用从hGetContents
返回的字符串
简单地说,这里不要使用惰性I/O。您需要一次手动读取和写入一个字符串,因为时间很重要
一般来说,惰性I/O有点整洁,但它不适用于玩具示例以外的任何东西。这个解释(特别是从“现在让我们假设客户机…”开始)是正确的。TCP连接的每个方向都可以独立关闭,如果服务器支持HTTP保持活动状态,则web浏览器在至少收到HTTP响应头之前不会关闭客户端->服务器方向。因此,最终会出现典型的死锁情况:服务器在收到10行请求或关闭接收通道之前无法发送响应,而浏览器在收到服务器的响应之前不会关闭响应。(继续)然而,这与懒惰的IO没有太大关系。您将从C程序中获得相同的行为,如inth=accept(socket,…);对于(inti=0;i<10;i++){if(fgets(buf,size,h)){printf(“%s”,buf);}}fprintf(h,“…”)
如果您仔细考虑强制执行的请求量,可以使用hGetContents
实现服务器。然而,由于HTTP是一种简单的基于行的协议,因此使用hGetLine
和显式循环可能更容易。因此,单独使用hPutStrLn c
是否也无法达到类似的效果,这意味着hGetContents
永远不会发出结束的信号?
putStrLn $ unlines $ take 10 $ lines c
nc localhost 5002