【发布时间】:2012-08-26 21:34:59
【问题描述】:
我在 Haskell 中使用 Network 和 Gloss 作为游戏服务器。它工作正常,除了客户端必须关闭服务器才能接收它发送的数据。我敢打赌这是一个懒惰的例子......
极简服务器:
import Network
import System.IO
main = do
sock <- listenOn (PortNumber (fromIntegral 12345))
loop sock
loop sock = do
(hIn, _, _) <- accept sock
str <- hGetContents hIn
print str
loop sock
极简客户端:
import Network
import System.IO
import Graphics.Gloss.Interface.IO.Game
main = playIO
(InWindow "Test Multi" (500, 500) (500, 500))
white
60
Nothing
draw
(\_ x -> return x)
advance
draw Nothing = return blank
draw (Just x) = return (Text (show x))
advance _ Nothing = do
hOut <- connectTo "000.000.0.0" (PortNumber (fromIntegral 12345))
hSetBuffering hOut NoBuffering
hPutStr hOut "Hello!"
return (Just hOut)
advance _ x = return x
我启动服务器,等待 10 秒,然后启动客户端,等待 15 秒,看到服务器上没有任何反应,关闭客户端,看到“你好!”突然出现在服务器上。
我想要“你好!”在客户端运行时出现在advance 调用中,否则我无法制作多人游戏(呜咽)!
但是,如果我将客户端的代码更改为
main = loop Nothing
loop x = do
x' <- advance 0 x
getLine
服务器立即显示“你好!”客户正在等待我的输入。
按照另一个问题中的建议,我尝试使用爆炸模式和hClose:
-- ...
!str <- hGetContents hIn
hClose hIn
-- ...
这使输出立即出现,而无需关闭客户端。那太棒了。但是,我打算使用字节串,因为我发送到服务器的数据是序列化的,所以我import qualified Data.ByteString as B并将hGetContents更改为B.hGetContents,这使得问题再次出现。
问题确实是懒惰。 hGetContents 懒惰地读取Handle 的所有内容,所以它只有在它关闭时才完成,当客户端中止连接时。相反,我使用了hGetLine,它在每次遇到\n 时返回内容,我将其用作end-of-message 标记。
【问题讨论】:
-
这可能与 playIO 功能有关吗?当我删除绘图代码时,我能够让服务器回显客户端的消息,但是会有几秒钟的延迟。
-
我也是这么想的。更准确地说,使用递归的客户端代码有这个问题,而我在问题中添加的代码使用
getLine工作正常。 -
即使我将
draw重写为draw _ = return Blank并等待 30 秒(同时写此消息:P),“你好!”直到我关闭才写。我用这种方式写了draw,这样我们就可以真正看到与服务器的连接成功了。 -
你是对的。我编辑了问题。
标签: haskell network-programming lazy-evaluation