This is a minor extension of EchoServer: when started, it expects an integer command line argument maxClients, denoting the maximal number of concurrently served clients. If more clients connect, they are just informed that the server is busy and disconnected.

module EchoServer2 where

import POSIX
import Counter

port = Port 12345

root w = do
    env = new posix w
    clients = new counter
    case parse (env.argv ! 1) of
       Right n -> env.inet.tcp.listen port (server n clients env.stdout)
       _ -> env.stdout.write "usage: EchoServer n, where n is number of concurrent clients\n"
            env.exit 1

server maxClients clients logfile sock = class

   n := 1

   p = show sock.remoteHost

   log str = logfile.write ('[':str ++ "]\n")

   echo str = action
      sock.outFile.write (show n ++"> "++str)
      n := n+1

   close = request
      log (p ++ " closing")
      result ()

   neterror str = action log ("Neterror: "++str)

   established = action
      cl <- clients.value
      if cl < maxClients then
         log ("Connected from " ++ p)
         sock.inFile.installR echo
         sock.outFile.write "Server busy"
         log ("Refused " ++ p)

   result Connection{..}

The differences to EchoServer are the following:

We need to keep track of the number of served clients. For this we use a Counter object, created by the root object.

When a new client connects, the counter value is retrieved and only if this value is small enough is an echo action installed; otherwise the client is informed that the server is busy and the socket closed. When a client closes the connection, the counter is decreased.

A Counter object is appropriate here, since several server objects, each interacting with one client, interacts with it. In contrast, the line number local to each server object is maintained in a simple integer state variable.