【发布时间】:2016-03-20 12:59:48
【问题描述】:
在深入研究 Haskell 的 Network 库时,我正在根据来自 this link 的信息制作一个非常简单的 HTTP 服务器。
import Control.Concurrent
import Control.Monad
import Network
import System.IO
main = withSocketsDo $ listenOn (PortNumber 8080) >>= loop
loop :: Socket -> IO ()
loop sock = do
(h,_,_) <- accept sock
forkIO $ handleRequest h
loop sock
handleRequest :: Handle -> IO ()
handleRequest h = do
hPutStr h $ httpRequest "Pong!\n"
hFlush h
hClose h
httpRequest :: String -> String
httpRequest body = "HTTP/1.0 200 OK\r\n"
++ "Content-Length: " ++ (show.length) body ++ "\r\n"
++ "\r\n" ++ body ++ "\r\n"
但是,即使我设法得到了一些响应,手柄似乎很快就会意外关闭(有时?),因为 curl 告诉我:
$ curl localhost:8080
Pong!
curl: (56) Recv failure: Connection reset by peer
注意:有时我什至没有收到消息 (Pong!) 或者只是其中的一部分。有时,它可以工作......但如果我连续运行 100 个curls,我最终会重置一些连接。
为什么会重置连接?我尝试使用和不使用forkIO 都没有成功。我错过了一些关于 Haskell 中 IO 流的基本信息吗?谢谢!
操作系统:最近的 Ubuntu; GHC:7.8.4
--- 编辑:---
jozefg 发现问题出在请求内容的耗尽上!但是,我想将此内容发送回客户端,但在使用以下代码时它会挂起:
handleRequest :: Handle -> IO ()
handleRequest h = do
contents <- getHandleContents h
hPutStr h $ httpRequest contents
hFlush h
hClose h
getHandleContents :: Handle -> IO String
getHandleContents h = do
iseof <- hIsEOF h
if iseof
then return []
else do
newLine <- hGetLine h
nextLines <- getHandleContents h
return $ newLine ++ '\n' : nextLines
此外,我没有成功使用hGetContents 排出全部内容。知道为什么吗?
【问题讨论】:
-
回答我在编辑中的附加问题:直到 EOF 我才能尝试阅读,因为关闭连接是服务器的角色(因此,我们只会等待)。相反,如果客户端指定
Content-Length标头,我们将读取指定的字节数以获取正文。这也解释了为什么hGetContents会挂起程序。 -
你在写完所有东西之前就关闭了句柄。当您关闭句柄时,您也关闭了连接。删除
hClose h使它在我的机器上 100% 的工作。 在实践中的解决方案是使用更高级别的 http 服务器库并让它担心这些事情。