{ "title": "Flertrådede servere", "solutions": "true" }
1 Første har vi ClientManager, der modtager alle indkommende forbindelser fra klienterne.
De beskeder der skal sendes mellem brugerne er tilgængelige gennem ClientManager, da det på server-siden er noget der udveksles mellem de enkelte ClientWorkers. Der er derfor lavet metoder hvormed ClientWorker kan gemme og hente beskeder:
            import java.net.*;
            import java.io.*;
            import java.util.ArrayList;
            
            class ClientManager extends Thread {
              private ArrayList<ClientWorker> workers;
              private int port;
              private boolean stop;
              
              public ClientManager( int port ) {
                this.port = port;
                this.stop = false;
                
                workers = new ArrayList<ClientWorker>();
                messages = new ArrayList<Message>();
              }
              
              public void run() {
                try {
                  ServerSocket server = new ServerSocket( port, 100 );
                  server.setSoTimeout( 100 );
                  System.out.println( "[ClientManager] Server online" );
                  
                  while ( !stop )
                    try {
                      Socket connection = server.accept();
                    
                      ClientWorker worker = new ClientWorker( this, connection );
                      workers.add( worker );
                      worker.start();
                    }
                    catch ( SocketTimeoutException e ) {
                      // ignore timeout
                    }
                  
                  server.close();
                  
                  System.out.println( "[ClientManager] Server offline" );
                }
                catch ( IOException e ) {
                  System.out.println( "[ClientManager] I/O error" );
                }
              }
              
              public void shutdown() {
                stop = true;
              }
              
              /*
               * Messages
               */
              private ArrayList<Message> messages;
              
              public void storeMessage( String to, String message ) {
                messages.add( new Message( to, message ) );
              }
              
              public String getMessage( String userName ) {
                for ( Message message : messages )
                  if ( message.isTo( userName ) ) {
                    messages.remove( message );
                    return message.getMessage();
                  }
                
                return null;
              }
            }
          
Selve Message-klassen er en simpel entitets-klasse.
Bemærk, at oplysningen om hvem beskeden er til, ikke er direkte tilgængelig (information hiding), men kun gennem en konkret forespørgsel om beskeden er til en given bruger:
            public class Message {
              private String to;
              private String message;
              
              public Message( String to, String message ) {
                this.to = to;
                this.message = message;
              }
              
              public boolean isTo( String userName ) {
                return to.equalsIgnoreCase( userName );
              }
              
              public String getMessage() {
                return message;
              }
            }
          
Dernæst har vi ClientWorker, som server-side implementerer protokollen:
            import java.net.*;
            import java.io.*;
            
            class ClientWorker extends Thread {
              private Socket connection;
              private ClientManager manager;
              private String userName;
              
              public ClientWorker( ClientManager manager, Socket connection ) {
                this.manager = manager;
                this.connection = connection;
                
                userName = null;
              }
              
              public void run() {
                System.out.println( "[ClientWorker] New connection from: " +
                                    connection.getInetAddress().getHostAddress() );
              
                try {
                  BufferedWriter writer = new BufferedWriter(
                      new OutputStreamWriter(
                        connection.getOutputStream() ) );
            
                  BufferedReader reader = new BufferedReader(
                      new InputStreamReader(
                        connection.getInputStream() ) );
            
                  while ( true ) {
                    String line = reader.readLine();
                    
                    if ( line == null ) // lost connection!
                      break;
                    
                    String[] tokens = line.split( " " );
                    // eller: String[] tokens = line.split( " ", 3 )
                    //        og dernæst bruge tokens[2] for beskeden
                    
                    String command = tokens[ 0 ].toUpperCase();
                    
                    System.out.println( "[ClientWorker] Received command: " + command );
                    
                    if ( command.equals( "LOGIN" ) ) {
                      userName = tokens[ 1 ];
                      
                    } else if ( command.equals( "MESSAGE" ) ) {
                      String to = tokens[ 1 ];
                      String message = tail( tail( line ) );
                      
                      manager.storeMessage( to, message );
                      
                    } else if ( command.equals( "GET" ) ) {
                      String message = manager.getMessage( userName );
                      
                      if ( message != null )
                        sendLine( writer, message );
                      else
                        sendLine( writer, "no messages" );
                      
                    } else if ( command.equals( "LOGOUT" ) )
                      break;
                  }
                  
                  reader.close();
                  writer.close();
                  
                  System.out.println( "[ClientWorker] Connection closed" );
                }
                catch ( IOException e ) {
                  System.out.println( "[ClientWorker] I/O error" );
                }
              }
              
              private String tail( String text ) {
                int pos = text.indexOf( ' ' );
                
                if ( pos >= 0 )
                  return text.substring( pos + 1 ).trim();
                else
                  return "";
              }
              
              private void sendLine( BufferedWriter writer, String line )
                throws IOException
              {
                System.out.println( "[ClientWorker] sending line: " + line );
                
                writer.write( line );
                writer.newLine();
                writer.flush();
                
                Sleeper.nap();
              }
            }
          
På klient-siden, har SimpleClient:
            import java.net.*;
            import java.io.*;
            
            public class SimpleClient extends Thread {
              public void run() {
                try {              

                  Socket connection = new Socket( "127.0.0.1", 6010 );
                  System.out.println( "[Client] Connection open" );
                
                  BufferedWriter writer = new BufferedWriter(
                      new OutputStreamWriter(
                        connection.getOutputStream() ) );
            
                  BufferedReader reader = new BufferedReader(
                      new InputStreamReader(
                        connection.getInputStream() ) );
            
                  sendLine( writer, "LOGIN viggo" );
                  sendLine( writer, "MESSAGE niels Hej, hvordan går det?" );
                  // LOGOUT udkommenteret, så vi ikke skal til at lave en ny forbindelse til serveren
                  //sendLine( writer, "LOGOUT" );
                  
                  sendLine( writer, "LOGIN niels" );
                  sendLine( writer, "GET" );
                  System.out.println( "[Client] " + reader.readLine() );
                  sendLine( writer, "GET" );
                  System.out.println( "[Client] " + reader.readLine() );
                  sendLine( writer, "LOGOUT" );
                  
                  writer.close();
                  reader.close();
                  connection.close();
                  
                  System.out.println( "[Client] Connection closed" );
                }
                catch ( IOException e ) {
                  System.out.println( "[Client] Connection failed" );
                }
              }
              
              private void sendLine( BufferedWriter writer, String line )
                throws IOException
              {
                System.out.println( "[SimpleClient] sending line: " + line );
                
                writer.write( line );
                writer.newLine();
                writer.flush();
                
                Sleeper.nap();
              }
            }
          
Og endelig har vi Main, der kører server og klient i hver deres tråd:
            public class Main {
            
              public static void main(String[] args) {
                ClientManager manager = new ClientManager( 6010 );
                manager.start();
                
                Sleeper.nap(); // være sikker på at serveren er online
                
                SimpleClient client = new SimpleClient();
                client.start();
                
                Sleeper.sleep( 2.0 );
                manager.shutdown();
              }
            }
          
            [ClientManager] Server online
            [Client] Connection open
            [SimpleClient] sending line: LOGIN viggo
            [ClientWorker] New connection from: 127.0.0.1
            [ClientWorker] Received command: LOGIN
            [SimpleClient] sending line: MESSAGE niels Hej, hvordan går det?
            [ClientWorker] Received command: MESSAGE
            [SimpleClient] sending line: LOGIN niels
            [ClientWorker] Received command: LOGIN
            [SimpleClient] sending line: GET
            [ClientWorker] Received command: GET
            [ClientWorker] sending line: Hej, hvordan går det?
            [Client] Hej, hvordan går det?
            [SimpleClient] sending line: GET
            [ClientWorker] Received command: GET
            [ClientWorker] sending line: no messages
            [Client] no messages
            [SimpleClient] sending line: LOGOUT
            [ClientWorker] Received command: LOGOUT
            [ClientWorker] Connection closed
            [Client] Connection closed
            [ClientManager] Server offline