I'm trying to get multiple clients to connect to a server. What I've managed to do is connect one client to a server by using for the server:
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."
and for the client:
main = withSocketsDo $ do
handle <- connectTo "localhost" port
hSetBuffering handle LineBuffering
hPutStrLn handle "Hello!"
hClose handle
These are clearly just for testing purposes ;)
Now, I've read that I need to use forkIO to enable multiple clients to connect to this one server. However I haven't been able to find how I should use forkIO or how to manage the several clients that will connect. Can someone explain to me what I should do?
Thanks in advance!
The key is that once you've accepted a connection using accept
, you'll want to fork a new thread to handle the connection while the main thread goes back to listening. So something like this should do the trick.
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
Note that this way the server keeps running until you kill the process, which is common behavior for many servers. Implementing more graceful shutdown will require some cross-thread communication using MVars or STM.
Just as a general style comment, I'd split the reaction of the server to the client's connection into a separate function. This makes it easier to read in my opinion
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."
now we probably want it to loop indefinitely, as that tends to be how most servers work. So we use forever (from 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."
and from here, the manner in which to use forkIO becomes quite clear
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."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With