14

我在 Haskell 中使用NetworkandGloss作为游戏服务器。它工作正常,除了客户端必须关闭服务器才能接收它发送的数据。我敢打赌,这是一个懒惰的例子......

极简服务器:

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更改hGetContentsB.hGetContents,这使得问题再次出现。


这个问题确实是一个懒惰的例子。hGetContents懒惰地读取 的所有内容Handle,因此它仅在关闭时完成,当客户端中止连接时。相反,我使用hGetLine它在每次遇到 a 时返回内容\n,我将其用作end-of-message标记。

4

3 回答 3

9

我可能完全错了,但问题不是 hGetContents 吗?当然,这应该等到通过您的套接字发送的全部内容到达下一行(打印...)之前。hGetContents旨在为您提供在套接字关闭之前发送的所有内容。hGetLine 之类的东西可以立即终止,您可以让套接字保持打开状态,以便稍后发送更多数据。然后,您的客户端可以使用 hPutStrLn 而不是 hPutStr。

于 2012-08-26T23:40:52.757 回答
3

它默认为行缓冲输出,这就是为什么hPutStr(不提供行结尾)在刷新缓冲区之前不输出任何内容的原因。有两种方法可以解决这个问题:

hFlush stdouta)在您想要刷新输出的任何时候手动调用。

b) 用于hSetBuffering将缓冲设置为NoBuffering

所有这些功能都可以在System.IO模块中找到。

编辑:没关系,我刚刚看到你在客户端做了什么。我带着歉意收回我的回答。

于 2012-08-26T21:38:54.610 回答
2

可能,您需要禁用算法 Nagle。试试这个代码:

import Network.Socket

setSocketOption sock NoDelay 1
于 2012-08-27T04:59:33.483 回答