Игровой сервер в Haskell
Я использую Network
и Gloss
для игрового сервера в Haskell. Он отлично работает, за исключением того, что клиент должен закрыть сервер для получения отправленных данных. Держу пари, это случай лени...
Минималистский сервер:
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
the sever немедленно показывает "Привет!" . пока клиент ждет моего ввода.
Я попробовал, как было предложено в другом вопросе, использовать шаблоны ударов и hClose
:
-- ...
!str <- hGetContents hIn
hClose hIn
-- ...
который делает вывод немедленно, без закрытия клиента. Это здорово. Но я планирую использовать bytestrings, потому что данные, которые я отправляю на сервер, сериализуется, поэтому я import qualified Data.ByteString as B
и измените hGetContents
на B.hGetContents
, что заставляет проблему повторно появляться.
Проблема была действительно делом лени. hGetContents
лениво читает все содержимое Handle
, поэтому оно заканчивается только тогда, когда оно закрывается, когда клиент прерывает соединение. Вместо этого я использовал hGetLine
, который возвращает содержимое каждый раз, когда он встречает \n
, который я использую в качестве тега end-of-message
.
Ответы
Ответ 1
Возможно, я ошибаюсь, но это не проблема hGetContents? Разумеется, это должно подождать, пока все содержимое, отправленное через ваш сокет, не будет отправлено до следующей строки (print...). hGetContents разработан, чтобы предоставить вам все содержимое, отправленное до закрытия сокета. Что-то вроде hGetLine может немедленно прекратиться, и вы можете оставить сокет открытым, чтобы отправить больше данных позже. Затем ваш клиент мог использовать hPutStrLn вместо hPutStr.
Ответ 2
По умолчанию используется вывод с линейной буферизацией, поэтому hPutStr
(который не предоставляет окончание строки) не выводит ничего, пока вы не сбросите буфер. Есть два способа решить эту проблему:
a) Вызовите hFlush stdout
вручную в любое время, когда вы хотите очистить вывод.
b) Используйте hSetBuffering
, чтобы настроить буферизацию на NoBuffering
Все эти функции находятся в модуле System.IO
.
Изменить: Ничего, я только видел, где вы сделали это в клиенте. Я отклоняю свой ответ с извинениями.
Ответ 3
Вероятно, вам нужно отключить алгоритм Nagle.
Попробуйте этот код:
import Network.Socket
setSocketOption sock NoDelay 1