Haskell:如何使用forkIO使多个客户端连接到服务器?

use*_*455 9 networking haskell

我正在尝试让多个客户端连接到服务器.我设法做的是通过使用服务器将一个客户端连接到服务器:

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."
Run Code Online (Sandbox Code Playgroud)

并为客户:

main = withSocketsDo $ do
  handle <- connectTo "localhost" port
  hSetBuffering handle LineBuffering
  hPutStrLn handle "Hello!"
  hClose handle
Run Code Online (Sandbox Code Playgroud)

这些显然仅用于测试目的;)

现在,我已经读过我需要使用forkIO来使多个客户端连接到这个服务器.但是我无法找到如何使用forkIO或如何管理将要连接的多个客户端.有人可以向我解释我应该做些什么吗?

提前致谢!

ham*_*mar 10

关键是,一旦你接受了使用连接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
Run Code Online (Sandbox Code Playgroud)

请注意,这样服务器会一直运行,直到您终止进程,这是许多服务器的常见行为.实现更优雅的关闭将需要使用MVars或STM进行一些跨线程通信.

  • 请注意,`forkIO`是一个`IO() - > IO ThreadId`,意思是,它返回生成的子进程ID,可以用来杀死你的孩子.不是你真正的孩子.:P (2认同)

Pro*_*bie 7

就像一般风格的评论一样,我将服务器对客户端连接的反应拆分为一个单独的函数.这使我更容易阅读

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."
Run Code Online (Sandbox Code Playgroud)

现在我们可能希望它无限循环,因为这往往是大多数服务器的工作方式.所以我们永远使用(来自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."
Run Code Online (Sandbox Code Playgroud)

从这里开始,使用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."
Run Code Online (Sandbox Code Playgroud)

  • 你不应该在请求处理程序中关闭侦听套接字.套接字需要保持打开以进行其他`accept`调用. (3认同)