Haskell: Как использовать forkIO, чтобы несколько клиентов могли подключаться к серверу? - PullRequest
9 голосов
/ 08 ноября 2011

Я пытаюсь подключить несколько клиентов к серверу.Мне удалось подключить одного клиента к серверу, используя для сервера:

main = withSocketsDo $ do 
          socket                 <- listenOn port
          (handle, host, portno) <- accept socket
          hSetBuffering handle LineBuffering
          msg <- hGetLine handle
          putStrLn $ "The client says: " ++ msg
          hClose handle
          sClose socket
          putStrLn "Server is done."

и для клиента:

main = withSocketsDo $ do
  handle <- connectTo "localhost" port
  hSetBuffering handle LineBuffering
  hPutStrLn handle "Hello!"
  hClose handle

Это явно только для целей тестирования.;)

Теперь я прочитал, что мне нужно использовать forkIO, чтобы несколько клиентов могли подключаться к этому одному серверу.Однако я не смог найти, как мне использовать forkIO или как управлять несколькими клиентами, которые будут подключаться.Может кто-нибудь объяснить мне, что я должен делать?

Заранее спасибо!

Ответы [ 2 ]

10 голосов
/ 08 ноября 2011

Ключ в том, что после того, как вы приняли соединение, используя accept, вы захотите разветвить новый поток для обработки соединения, пока основной поток возвращается к прослушиванию.Итак, что-то вроде этого должно помочь.

main = withSocketsDo $ do 
          socket <- listenOn port
          -- We want to accept multiple connections,
          -- so we need to accept in a loop
          forever $ do 
              (handle, host, portno) <- accept socket
              -- But once we've accepted, we want to go off and handle the
              -- connection in a separate thread
              forkIO $ do
                  hSetBuffering handle LineBuffering
                  msg <- hGetLine handle
                  putStrLn $ "The client says: " ++ msg
                  hClose handle

Обратите внимание, что таким образом сервер продолжает работать до тех пор, пока вы не завершите процесс, что является обычным поведением для многих серверов.Реализация более плавного завершения работы потребует некоторого межпоточного взаимодействия с использованием MVars или STM.

7 голосов
/ 08 ноября 2011

Так же, как комментарий общего стиля, я бы разделил реакцию сервера на подключение клиента к отдельной функции. Это облегчает чтение на мой взгляд

main = withSocketsDo $ do
    socket <- listenOn port
    accept socket >>= handleClientRequest socket

handleClientRequest socket (handle, host, portno) = do
      hSetBuffering handle LineBuffering
      msg <- hGetLine handle
      putStrLn $ "The client says: " ++ msg
      hClose handle
      sClose socket
      putStrLn "Server is done."

Теперь мы, вероятно, хотим, чтобы цикл продолжался бесконечно, так как большинство серверов работают именно так. Поэтому мы используем вечно (из Control.Monad)

main = withSocketsDo $ do
    socket <- listenOn port
    forever $ accept socket >>= handleClientRequest socket

handleClientRequest socket (handle, host, portno) = do
      hSetBuffering handle LineBuffering
      msg <- hGetLine handle
      putStrLn $ "The client says: " ++ msg
      hClose handle
      sClose socket
      putStrLn "Server is done."

и отсюда, способ использования forkIO становится вполне понятным

main = withSocketsDo $ do
    socket <- listenOn port
    forever $ accept socket >>= forkIO . (handleClientRequest socket)

handleClientRequest socket (handle, host, portno) = do
      hSetBuffering handle LineBuffering
      msg <- hGetLine handle
      putStrLn $ "The client says: " ++ msg
      hClose handle
      sClose socket
      putStrLn "Server is done."
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...