Task

Write a server for a minimal text based chat.

People should be able to connect via ‘telnet’, sign on with a nickname, and type messages which will then be seen by all other connected users. Arrivals and departures of chat members should generate appropriate notification messages.




const PORT=23;
 
var users=Dictionary(); // ( handle:socket, ...)
pipe:=Thread.Pipe(); // how server tells thread to connect to user
 
fcn accept(pipe){ // a thread waiting for the server to send a socket
   while(socket:=pipe.read()){
      println("Somebody is connecting ...");
      socket.read();  // telnet stuff
      while(True){    // get credentials
	 reg name;
	 socket.write("Your handle: ");  // bottle neck
	 try{ name = socket.read().text.strip() } catch(IOError){ continue }
	 if(users.holds(name)) socket.write("Handle is already in use.\n");
	 else if(name){
	    users[name] = socket;
	    chat.launch(name,socket);	// thread
	    broadcast(name, "+++ %s arrived +++".fmt(name));
	    break;	// wait for next connection
	 }
      }//while
   }//while
}.launch(pipe); // thread
 
fcn chat(name,socket){		// a thread, one per user
   try{
      socket.write("^D to disconnect\n");
      while(True){
	 message:=socket.read().text.strip();
	 if(message=="\xff\xec") break; // ^D to disconnect.
	 broadcast(name, "%s> %s".fmt(name,message));
      }
   }catch{}  // eg socket pukes
   users.del(name); socket.close(); 
   broadcast(name, "--- %s leaves ---".fmt(name));
}
 
    // Send a message to all users from the given name.
fcn broadcast(name, message){ // called from user thread
   println(message);  // log message to server console
   users.pump(Void,'wrap([(toName,socket)]){
      if(toName != name) try{ socket.write(message + "\n") } catch(IOError){}
   });
}
 
   // Set up the server socket.
server:=Network.TCPServerSocket.open(PORT);
println("Listening on %s:%s".fmt(server.hostname,server.port));
server.listen(pipe);  // Main event loop 