ΚΕΦΑΛΑΙΟ 8

ΠΑΡΑΔΕΙΓΜΑΤΑ ΕΦΑΡΜΟΓΩΝ CLIENT-SERVER COMPUTING

Παράδειγμα  1
Ψευδοκώδικες ενός Client και ενός Server
Παράδειγμα 2
Εφαρμογή σε Java
Παράδειγμα 3
Εφαρμογή σε Java
Παράδειγμα 4
Εφαρμογή σε Java
Παράδειγμα 5
Εφαρμογή σε Java
Παράδειγμα 6
Εφαρμογή σε Java
Παράδειγμα 7
Εφαρμογή σε C
Παραλλαγές του παραδείγματος 7
Εφαρμογές σε C
Παράδειγμα 8
Εφαρμογή σε Java
Παραλλαγές του παραδείγματος 8
Εφαρμογές σε Java
Παράδειγμα 9
Εφαρμογή σε C
Παράδειγμα 10
Εφαρμογή σε Java
Παράδειγμα 11
Εφαρμογή σε Java
 

 
 

ΚΕΦΑΛΑΙΟ 8

ΠΑΡΑΔΕΙΓΜΑΤΑ ΕΦΑΡΜΟΓΩΝ CLIENT-SERVER COMPUTING

Παράδειγμα 1

Σ’ αυτό το παράδειγμα παρουσιάζουμε τους ψευδοκώδικες ενός client (ψευδοκώδικας client) και ενός server (ψευδοκώδικας server). Ο client στέλνει αιτήσεις και λαμβάνει πληροφορίες από έναν επιλεγμένο server. O server ακούει τις αιτήσεις από τον client, επεξεργάζεται τα δεδομένα, απαντάει στην αίτηση και στη συνέχεια αναμένει για άλλες αιτήσεις από τον client.

Ψευδοκώδικας client

#include <xti.h>
#include other needed header files
extern int t_errno;
main (int argc, char *argv[])
{
/* Declare data structures needed for operations on the
transport provider endpoint. */
/* Declare the data structure that contains the well-known
address of the server. Both the type and the value of
"server_address" must be transport-specific. This example
assumes that they have already been defined appropriately
(using "typedef" and "#define", for example).
TRANSPORT_ADDRESS server_address = WELL_KNOWN_SERVER_ADDRESS;
/* Declare other data structures. */
/* Open the transport provider, using the appropriate
device
name, and receive a file descriptor in return that denotes
that endpoint. */
fd = t_open(device_name, ... )
/* Have the transport provider bind an arbitrary transport
address
(which is transport-specific) to the endpoint. */
t_bind(fd, NULL, NULL);
/* Allocate the data structure needed in the call to connect
to
the server. */
connection_info = t_alloc(fd, T_CALL, T_ADDR);
/* Fill in the data structure with the well-known address
of the server. */
connection_info->addr.len = sizeof(server_address);
connection_info->addr.buf = &server_address;
/* Connect to the server, passing in its well-known address.
*/
t_connect(fd, connection_info, NULL);
while (true) {
/* Send data to the server. */
t_snd(fd, &data_to_send, sizeof(data_to_send), &flags);
/* Receive data from the server. */
t_rcv(fd, &data_to_send, sizeof(data_to_send), &flags);
/* Time to exit? */
if (time_to_exit == /* some suitable expression that returns
zero if it's not time to disconnect and non-zero otherwise
*/)
break;
}
/* Disconnect */
t_snddis(fd, NULL);
/* Close the transport endpoint. */
t_close(fd);
}
Ψευδοκώδικας server
#include <xti.h>
#include other needed header files
extern int t_errno;
main (int argc, char *argv[])
{
/* Declare data structures needed for operations on the
transport provider endpoint. */
/* Declare the data structure that contains the well-known
address of the server. Both the type and the value of
"server_address" must be transport-specific. This example
assumes that they have already been defined appropriately
(using "typedef" and "#define", for example).
TRANSPORT_ADDRESS server_address = WELL_KNOWN_SERVER_ADDRESS;
/* Declare other data structures. */
/* Open the transport provider, using the device name
assigned
to that transport provider, and receive in return a file
descriptor
denoting that endpoint. */
fd = t_open(device_name, ... );
/* Allocate the bind data structure that will contain
the
well-known transport-specific address of the server. */
requested_binding = t_alloc(fd, T_BIND, T_ADDR);
/* Allocate the bind data structure that will contain
the
actual address assigned by the transport provider. */
actual_binding = t_alloc(fd, T_BIND, T_ADDR);
/* Fill in the bind data structure with the well-known
transport-specific address of the server, and the length
of the queue to hold incoming connection requests. */
requested_binding->addr.len = sizeof(server_address);
requested_binding->addr.buf = &server_address;
requested_binding->qlen = MAX_QUEUE_LENGTH;
/* Bind the well-known transport-specific address of the
server
to the transport endpoint. */
t_bind(fd, requested_binding, actual_binding);
/* Compare the requested address in "requested_binding"
against the actual assigned address in "actual binding".
If they don't match, issue an error and exit. */
if (memcmp(requested_binding->addr.buf,
actual_binding->addr.buf, ADDRESS_LENGTH) != 0 ) {
perror("Unable to bind correct server address.");
exit(1);
}
/* Allocate the data structure needed to record the address
of the next client requesting a connection. */
call = t_alloc(fd, T_CALL, T_ADDR);
/* This server runs forever. */
while (true) {
/* Listen forever for the next connection request from
a client. */
t_listen(fd, call);
/* Open a new file descriptor through which the server
will
complete the connection from the client. By completing
the
connection through "resfd", the server leaves "fd" available
to receive additional connection requests from other clients.
*/
resfd = t_open(device_name, ... );
/* Let the transport provider select an arbitrary transport
address for the new file descriptor. */
t_bind(resfd, NULL, NULL);
/* Accept the connection request that arrived at "fd"
and
attach the server-side of the connection to the responding
file descriptor "resfd". The file descriptor "fd" continues
to be
available to the server to listen for new incoming connection
requests from clients (see "t_listen" at the top of the
loop).
The "call" data structure has the information that the
child server
process (see the "switch" statement below) needs to communicate
with the client at the other end of the connection. */
t_accept(fd, resfd, call);
/* Spawn a child server process. The parent process will
continue
to listen on the original file descriptor "fd" (which
is still
bound to the server's well-known address) for additional
connection
requests. The child process will communicate with the
client
using the "resfd" file descriptor. */
switch (fork()) {
case -1:
perror("Fork of server process to respond to client
request has failed. Server aborting...");
exit(1);
default:
/* This is the code executed by the parent process that
continues to listen on the well-known transport address
for
additional incoming connection requests from clients.
The parent process has no use for the file descriptor
to be
used to communicate with the client (resfd), so it closes
it.
Doing so does not close this file descriptor for the child
process, however, which can still use it. */
t_close(resfd);
case 0:
/* This is the code executed by the child process that
services the request of the client. The child process
has
no further use for the original file descriptor (fd) on
which the connection request arrived, so it closes it.
This does not close the file descriptor for the parent
process, however, which will continue to listen on the
"fd"
file descriptor for new connection requests from clients.
t_close(fd);
/* The child process can now perform some useful service
for
the client. In this example, the server has a very
accurate clock, so it returns the correct time of day
to
the client, then exits. */
gettimeofday(&time_value, &timezone);
t_snd(resfd, &time_value, sizeof(struct timeval),
flags);
t_close(resfd);
exit(0);
}
/* end switch */
}
/* end while */
}
/* end main */
Παράδειγμα 2

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία Client.java και Server.java αντιστοίχως. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ. Prompt> javac Client.java και Prompt> javac Server.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java Server). Παρατηρούμε ότι ο Server τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java Client)

Το αποτέλεσμα όλης αυτής της διαδικασίας είναι η ακόλουθη:

Server.java : Connection 2 received from: ClientX
Server.java : Got I/O streams
Server.java : Sending message "Connection successful"
Client.java : Connected to: ClientX
Client.java : Got I/O Streams
Client.java : Server message: Connection successful
Client.java : Sending message "Thank you."
Client.java : Transmission complete. Closing connection.
Server.java : Client message: Thank you.
Server.java : Transmission complete. Closing socket.
Με άλλα λόγια, βλέπουμε ότι στην αρχή ο Server δέχεται την σύνδεση από τον Client και τον ενημερώνει ότι η σύνδεση επιτεύχθηκε, στέλνοντας το μήνυμα “Connection successful” (Επιτυχημένη σύνδεση). Αφού ο Client λάβει το μήνυμα, ανταποδίδει με το μήνυμα “Thank you” (Ευχαριστώ). Με την αποστολή αυτού του μηνύματος, η επικοινωνία ολοκληρώνεται και διακόπτει την σύνδεση. Ο Server λαμβάνει το μήνυμα του Client και έπειτα τίθεται σε αναμονή για την επόμενη αίτηση από τον Client.

Από εδώ και πέρα, μπορούμε να τρέξουμε τον Clint πολλές φορές, χωρίς να χρειάζεται να τρέχουμε τον Server από την αρχή. Απλά ο Server περιμένει την νέα σύνδεση από τον Client, αφού όμως πρώτα τελειώσει η προηγούμενη σύνδεση.

Κώδικας client παραδείγματος 2

// Set up a Client that will read information sent
// from a Server and display the information.
import java.io.*;
import java.net.*;
public class Client 
   public Client(}
   {
   }
   public void runClient()
   {
      Socket client;
      DataInputStream input;
      DataOutputStream output;
      try {
         // Step 1: Create a Socket to make connection.
         client = new Socket( InetAddress.getLocalHost(), 
                              5000 );
         System.out.println( "Client.java : Connected to: " +
            client.getInetAddress().getHostName() );
         // Step 2: Get the input and output streams.
        input = new DataInputStream (
                     client.getInputStream() );
         output = new DataOutputStream(
                      client.getOutputStream() );
         System.out.println( "\nClient.java : Got I/O Streams\n" );
         // Step 3: Process connection.
         System.out.println( "Client.java : Server message: " +
            input.readUTF() );
         System.out.println(
            "\nClient.java : Sending message \"Thank you.\"\n" );
         output.writeUTF( "Thank you." );
         // Step 4: Close connection.

         System.out.println( "Client.java : Transmission complete. " +
            "Closing connection.\n" );
         client.close();   
      }
      catch ( IOException e ) {
         e.printStackTrace();
      }
   }
   public static void main( String args[] )
   {
      Client c = new Client();

      c.runClient();  
       }
    }
Κώδικας server παραδείγματος 2
// Fig. 16.3: Server.java// Set up a Server that will receive a connection
// from a client, send a string to the client,
// and close the connection.
import java.io.*;
import java.net.*;
public class Server {
   public Server()
   {
   }
   public void runServer()
   {
      ServerSocket server;
      Socket connection;
      DataOutputStream output;
      DataInputStream input;
      int counter = 1;
      try {
         // Step 1: Create a ServerSocket.
         server = new ServerSocket( 5000, 100 );
         while ( true ) {
            // Step 2: Wait for a connection.
            connection = server.accept();
            System.out.println( "Server.java : Connection " + counter +
               " received from: " +
               connection.getInetAddress().getHostName() );
            // Step 3: Get input and output streams.]
            input = new DataInputStream(
                        connection.getInputStream() );
            output = new DataOutputStream 
                        connection.getOutputStream() );
            System.out.println( "\nServer.java : Got I/O streams\n" );
             // Step 4: Process connection.
            System.out.println(
               "Server.java : Sending message \"Connection successful\"\n" );
            output.writeUTF( "Connection successful" );
            System.out.println( "Server.java : Client message: " +
                                input.readUTF() );
                 // Step 5: Close connection.
            System.out.println( "\nServer.java : Transmission complete. " +
                            "Closing socket.\n\n" );
            connection.close();
            ++counter;
         }
      }
      catch ( IOException e ) {
        e.printStackTrace(); 
     }
   }
    public static void main( String args[] )
   {
      Server s = new Server();
      s.runServer();
   }
}
Παράδειγμα 3

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία HttpClient.java και HttpMirror.java αντιστοίχως. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ. Prompt> javac HttpClient.java και Prompt> javac HttpMirror.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java HttpMirror <port> , όπου στο <port> μπορούμε να βάλουμε την πύλη 80). Παρατηρούμε ότι ο Server τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java HttpClient <URL> [<Filename>], όπου στη θέση του <URL> μπορούμε να βάλουμε κάποια Http διεύθυνση, όπως www. ora. com).

Με το που συνδέεται ο Client, μπορεί να συντάξει μία HTTP αίτηση και να την στείλει στον Server. O Server εδώ είναι ένας απλός Web Server. Αυτός στέλνει πίσω την αίτηση του Client, όπως του την έστειλε. Αυτή η υπηρεσία έχει ενδιαφέρον όταν θέλουμε απλά να δούμε την αίτηση του Web Client ή, για παράδειγμα, ποιες πληροφορίες στάλθηκαν όταν υποβλήθηκε μία αίτηση.

Όλη αυτή η διαδικασία μπορεί να αποτυπωθεί ως εξής:

Server: Starting server
Client : Connected to Dimitra:6789
Server: Connected to Dimitra on port 6789 
Client : Hello          {Press Enter}
                        {Press Enter}
        HTTP/1.0 200
        Content-Type: text/plain
        
        Hello
Client : Connection closed by server
Server: Connection to Dimitra closed
Κώδικας client παραδείγματος 3
// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples
import java.io.*;
import java.net.*;
/**
 * This program connects to a Web server and downloads the specified URL
 * from it.  It uses the HTTP protocol directly.
 **/
public class HttpClient {
  public static void main(String[] args) {
    try {
      // Check the arguments
      if ((args.length != 1) && (args.length != 2))
        throw new IllegalArgumentException("Wrong number of arguments");
            // Get an output stream to write the URL contents to
      OutputStream to_file;
      if (args.length == 2) to_file = new FileOutputStream(args[1]);
      else to_file = System.out;
      // Now use the URL class to parse the user-specified URL into
      // its various parts: protocol, host, port, filename.  Check the protocol
      URL url = new URL(args[0]);
      String protocol = url.getProtocol();
      if (!protocol.equals("http"))
        throw new IllegalArgumentException("URL must use 'http:' protocol");
      String host = url.getHost();
      int port = url.getPort();
      if (port == -1) port = 80;  // if no port, use the default HTTP port
      String filename = url.getFile();
      // Open a network socket connection to the specified host and port
      Socket socket = new Socket(host, port);
      // Get input and output streams for the socket
      InputStream from_server = socket.getInputStream();
      PrintWriter to_server = 
        new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
      // Send the HTTP GET command to the Web server, specifying the file.
      // This uses an old and very simple version of the HTTP protocol
      to_server.println("GET " + filename);
      to_server.flush();  // Send it right now!
       // Now read the server's response, and write it to the file
      byte[] buffer = new byte[4096];
      int bytes_read;
      while((bytes_read = from_server.read(buffer)) != -1)
        to_file.write(buffer, 0, bytes_read);
      // When the server closes the connection, we close our stuff
      socket.close();
      to_file.close();
    }
    catch (Exception e) {    // Report any errors that arise
      System.err.println(e);
      System.err.println("Usage: java HttpClient  []");
    }
  }
}
Κώδικας server παραδείγματος 3
// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.

// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples
import java.io.*;
import java.net.*;
/**
 * This program is a very simple Web server.  When it receives a HTTP request
 * it sends the request back as the reply.  This can be of interest when
 * you want to see just what a Web client is requesting, or what data is
 * being sent when a form is submitted, for example.
 **/
public class HttpMirror {
  public static void main(String args[]) {
    try {
      // Get the port to listen on
      int port = Integer.parseInt(args[0]);
      // Create a ServerSocket to listen on that port.
      ServerSocket ss = new ServerSocket(port);
      // Now enter an infinite loop, waiting for connections and handling them.
      for(;;) {
        // Wait for a client to connect.  The method will block, and when it
        // returns the socket will be already connected to the client
        Socket client = ss.accept();
        // Get input and output streams to talk to the client from the socket
        BufferedReader in = 
          new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter out =
          new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
        // Start sending our reply, using the HTTP 1.0 protocol
        out.println("HTTP/1.0 200 ");              // Version & status code
        out.println("Content-Type: text/plain");   // The type of data we send
        out.println();                             // End of response headers
        out.flush();
        // Now, read the HTTP request from the client, and send it right
        // back to the client as part of the body of our response.
        // The client doesn't disconnect, so we never get an EOF.
        // It does sends an empty line at the end of the headers, though.  
        // So when we see the empty line, we stop reading.  This means we 
        // don't mirror the contents of POST requests, for example.
        String line;
        while((line = in.readLine()) != null) {
          if (line.length() == 0) break;
          out.println(line);
        }
        // Close the streams and socket, breaking the connection to the client
        out.close();
        in.close();
        client.close();
      } // Loop again, waiting for the next connection
    }
    // If anything goes wrong, print an error message
    catch (Exception e) {
      System.err.println(e);
      System.err.println("Usage: java HttpMirror ");
    }
  }
}


Παράδειγμα 4

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία Client.java και Server.java αντιστοίχως. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ. Prompt> javac Client.java και Prompt> javac Server.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java Server). Παρατηρούμε ότι ο Server τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java Client <hostname> [<port>], όπου <hostname> βάζουμε την ονομασία του Client, π.χ. Dimitra, και, προαιρετικά, όπου <port> βάζουμε την πύλη του Client, π.χ. 6789). Δημιουργείται άμεσα η σύνδεση μεταξύ Client και Server. Ο Client έχει την δυνατότητα να στείλει γραπτά μηνύματα προς τον Server, αφού στο παράθυρο του Client εμφανίζεται ένα prompt (> ). Ο Server λαμβάνει το μήνυμα, το αντιστρέφει και το επιστρέφει πίσω στον Client αντεστραμμένο. Για παράδειγμα, αν ο Client στείλει το μήνυμα «Helloworld», o Server του το επιστρέφει ως «dlrowolleH».

Το αποτέλεσμα όλης αυτής της διαδικασίας είναι η ακόλουθη:

Server.java: Listening on port 6789
Client.java:  Connected to Dimitra/193.92.81.94:6789
Client.java:  Sending message: > Helloworld
Server.java: Client message: Helloworld
Server.java: Reverse message: dlrowolleH
Client.java:  Server message: dlrowolleH
Client.java: >
Αφού ολοκληρωθεί η διαδικασία για το πρώτο μήνυμα, ο Client μπορεί να συνεχίσει την αποστολή και άλλων μηνυμάτων.

Κώδικας Client παραδείγματος 4

// This example is from the book _Java in a Nutshell_ by David Flanagan.
// Written by David Flanagan.  Copyright (c) 1996 O'Reilly & Associates.
// You may study, use, modify, and distribute this example for any purpose.
// This example is provided WITHOUT WARRANTY either expressed or implied.
import java.io.*;
import java.net.*;
public class Client {
    public static final int DEFAULT_PORT = 6789;
    public static void usage() {
        System.out.println("Usage: java Client  []");
        System.exit(0);
    }    
    public static void main(String[] args) {
        int port = DEFAULT_PORT;
        Socket s = null;
         // Parse the port specification
        if ((args.length != 1) && (args.length != 2)) usage();
        if (args.length == 1) port = DEFAULT_PORT;
        else {
            try { port = Integer.parseInt(args[1]); }
            catch (NumberFormatException e) { usage(); }
        }
         try {
            // Create a socket to communicate to the specified host and port
            s = new Socket(args[0], port);
            // Create streams for reading and writing lines of text
            // from and to this socket.
            DataInputStream sin = new DataInputStream(s.getInputStream());
            PrintStream sout = new PrintStream(s.getOutputStream());
            // Create a stream for reading lines of text from the console
            DataInputStream in = new DataInputStream(System.in);
               // Tell the user that we've connected
            System.out.println("Connected to " + s.getInetAddress()
                       + ":"+ s.getPort());
            String line;
            while(true) {
                // print a prompt
                System.out.print("> "); 
                System.out.flush();
                // read a line from the console; check for EOF
                line = in.readLine();
                if (line == null) break;
                // Send it to the server
                sout.println(line);
                // Read a line from the server.  
                line = sin.readLine();
                // Check if connection is closed (i.e. for EOF)
                if (line == null) { 
                    System.out.println("Connection closed by server.");
                    break;
                }
                // And write the line to the console.
                System.out.println(line);
            }
        }
        catch (IOException e) { System.err.println(e); }
        // Always be sure to close the socket
        finally {
            try { if (s != null) s.close(); } catch (IOException e2) { ; }
        }
    }
}
Κώδικας Server παραδείγματος 4
// This example is from the book _Java in a Nutshell_ by David Flanagan.
// Written by David Flanagan.  Copyright (c) 1996 O'Reilly & Associates.
// You may study, use, modify, and distribute this example for any purpose.
// This example is provided WITHOUT WARRANTY either expressed or implied.
import java.io.*;
import java.net.*;
public class Server extends Thread {
    public final static int DEFAULT_PORT = 6789;
    protected int port;
    protected ServerSocket listen_socket;
    // Exit with an error message, when an exception occurs.
    public static void fail(Exception e, String msg) {
        System.err.println(msg + ": " +  e);
        System.exit(1);
    }
      // Create a ServerSocket to listen for connections on;  start the thread.
    public Server(int port) {
        if (port == 0) port = DEFAULT_PORT;
        this.port = port;
        try { listen_socket = new ServerSocket(port); }
        catch (IOException e) { fail(e, "Exception creating server socket"); }
        System.out.println("Server: listening on port " + port);
        this.start();
    }
      // The body of the server thread.  Loop forever, listening for and
    // accepting connections from clients.  For each connection, 
    // create a Connection object to handle communication through the
    // new Socket.
    public void run() {
        try {
            while(true) {
                Socket client_socket = listen_socket.accept();
                Connection c = new Connection(client_socket);
            }
        }
        catch (IOException e) { 
            fail(e, "Exception while listening for connections");
        }
    }
      // Start the server up, listening on an optionally specified port
    public static void main(String[] args) {
        int port = 0;
        if (args.length == 1) {
            try { port = Integer.parseInt(args[0]);  }
            catch (NumberFormatException e) { port = 0; }
        }
        new Server(port);
    }
}
// This class is the thread that handles all communication with a client
class Connection extends Thread {
    protected Socket client;
    protected DataInputStream in;
    protected PrintStream out;
   // Initialize the streams and start the thread
    public Connection(Socket client_socket) {
        client = client_socket;
        try { 
            in = new DataInputStream(client.getInputStream());
            out = new PrintStream(client.getOutputStream());
        }
        catch (IOException e) {
            try { client.close(); } catch (IOException e2) { ; }
            System.err.println("Exception while getting socket streams: " + e);
            return;
        }
        this.start();
    }
 // Provide the service.
    // Read a line, reverse it, send it back.  
    public void run() {
        String line;
        StringBuffer revline;
        int len;
        try {
            for(;;) {
                // read in a line
                line = in.readLine();
                if (line == null) break;
                // reverse it
                len = line.length();
                revline = new StringBuffer(len);
                for(int i = len-1; i >= 0; i--) 
                    revline.insert(len-1-i, line.charAt(i));
                // and write out the reversed line
                out.println(revline);
            }
        }
        catch (IOException e) { ; }
        finally { try {client.close();} catch (IOException e2) {;} }
    }
}
Παράδειγμα 5

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία ClientTest.java και ServerTest.java αντιστοίχως. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ.Prompt> javac ClientTest.java και Prompt> javac ServerTest.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java ServerTest). Παρατηρούμε ότι ο Serverτίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java ClientTest). Από την στιγμή που ο Client θα ξεκινήσει την επικοινωνία με τον Server, μπορεί να διαλέξει ανάμεσα σε τρεις επιλογές: Date, Help και Quit. Αν η αίτηση του ξεκινάει με την λέξη Date, o Server του απαντάει, ενημερώνοντας τον για την τρέχουσα ημερομηνία. Αν η αίτηση του ξεκινάει με την λέξη Help, O Server του στέλνει το μήνυμα ότι μπορεί να χρησιμοποιήσει τις τρεις επιλογές Date, Help και Quit στις αιτήσεις που θέλει να υποβάλλει. Αν η αίτησή του ξεκινάει με την λέξη Quit, o Server διακόπτει την επικοινωνία με τον Client. Τέλος, αν η αίτηση ξεκινάει με κάποια λέξη διαφορετική από αυτές, ο Server ενημερώνει ότι η εντολή του Client δεν είναι κατανοητή.

Στην συνέχεια, παρουσιάζονται τα αποτελέσματα αυτής της εφαρμογής για κάθε μία από τις επιλογές.

Στην περίπτωση της επιλογής Date έχουμε:
Server.java: Listening on port 8001
Server.java: Sending message: “Welcome”
Client.java:  Server message: Welcome
Client.java:  Sending message: “Date”
Server.java: Client message: Date
Server.java: Sending message: “Todays date is ….”
Client.java:  Server message: Todays date is….

Στην περίπτωση της επιλογής Help έχουμε:
Server.java: Listening on port 8001
Server.java: Sending message: “Welcome”
Client.java:  Server message: Welcome
Client.java:  Sending message: “Help”
Server.java: Client message: Help
Server.java: Sending message: “Vocabulary: DATE HELP QUIT”
Client.java:  Server message: Vocabulary: DATE HELP QUIT

Στην περίπτωση της επιλογής Quit έχουμε:
Server.java: Listening on port 8001
Server.java: Sending message: “Welcome”
Client.java:  Server message: Welcome
Client.java:  Sending message: “Quit”
Server.java: Client message: Quit
Server.java: Transmission complete. Closing socket

Στην περίπτωση που η αίτηση του Client δεν ξεκινάει από μια από τις επιλογές αυτές έχουμε:
Server.java: Listening on port 8001
Server.java: Sending message: “Welcome”
Client.java:  Server message: Welcome
Client.java:  Sending message: “Time”
Server.java: Client message: Time
Server.java: Sending message: “ERR: Command ”Time” not understood”
Client.java:  Server message: “ERR: Command ”Time” not understood”

        Φυσικά, όταν ολοκληρωθεί η διαδικασία της πρώτης αίτησης του Client, αυτός έχει την δυνατότητα 
να συνεχίζει να κάνει αιτήσεις, εφόσον δεν αποστέλλει μήνυμα που να ξεκινάει από την λέξη Quit. 
        Ακολουθεί ένα παράδειγμα, στο οποίο ο Client στέλνει τρία μηνύματα, εφόσον, βέβαια, 
συνδεθεί με τον Server.
Server.java: Listening on port 8001
Server.java: Sending message: “Welcome”
Client.java:  Server message: Welcome
Client.java:  Sending message: “Date”
Server.java: Client message: Date 
Server.java: Sending message: “Todays date is ….”
Client.java:  Server message: Todays date is….
Client.java:  Sending message: “Time”
Server.java: Client message: Time
Server.java: Sending message: “ERR: Command ”Time” not understood”
Client.java:  Server message: “ERR: Command ”Time” not understood”
Client.java:  Sending message: “Quit”
Server.java: Client message: Quit
Server.java: Transmission complete. Closing socket
Κώδικας client παραδείγματος 5
// From Mastering Java 1.1 2nd ed. by Sybex page 714
// Revised by John Phillips on 11/17/98
import java.io.*;
import java.net.*;
public class ClientTest
{
  public static void main( String[] args )
  {
    String welcome, response;
    Client client;
    BufferedReader reader;
    PrintWriter writer;
        client = new Client( "localhost", 8001 );
  try
    {
      reader = new BufferedReader( new InputStreamReader( client.in ) );
      writer = new PrintWriter( new OutputStreamWriter( client.out ) );
      welcome = reader.readLine();
      System.out.println( "Server says: " + welcome );
      System.out.println( "HELP" );
      writer.println( "HELP" );
      writer.flush();
      response = reader.readLine();
      System.out.println( "Server responds: " + response );
      System.out.println( "HELP" );
      writer.println( "HELLO" );
      writer.flush();
      response = reader.readLine();
      System.out.println( "Server responds: " + response );
       System.out.println( "DATE" );
      writer.println( "DATE" );
      writer.flush();
      response = reader.readLine();
      System.out.println( "Server responds: " + response );
        System.out.println( "QUIT" );
      writer.println( "QUIT" );
      writer.flush();
    }
    catch( IOException e )
    {
      System.out.println( "IOException in client.in readln()" );
      System.out.println( e );
    }
    try
    {
      Thread.sleep( 2000 ); 
    }
    catch( Exception ignored )
    {
    }
  }
}
// **********************************
class Client
{
  // make input and output streams available to user classes
  public InputStream in;
  public OutputStream out;
  // the socket itself remains ours though...
  private Socket client;
  public Client( String host, int port )
  {
    try
    {
      client = new Socket( host, port );
      System.out.println( "Client socket: " + client );
      out = client.getOutputStream();
      in = client.getInputStream();
    }
    catch( IOException e )
    {
      System.out.println( "IOExc: " + e );
    }
  }
}
Κώδικας server παραδείγματος 5
// From Mastering Java 1.1 2nd ed. by Sybex page 712
// Revised by John Phillips on 11/17/98
import java.util.*;
import java.io.*;
import java.net.*;
public class ServerTest
{
  final static int SERVER_PORT = 8001;
  public static void main( String[] args )
  {
    Server server;
    String clientRequest;
    BufferedReader reader;
    PrintWriter writer;
    server = new Server( SERVER_PORT );
    reader = new BufferedReader( new InputStreamReader( server.in ) );
    writer = new PrintWriter( new OutputStreamWriter( server.out ) );
        // send initial string to client
    writer.println( "Java Test Server v.001 " + new Date() );
    writer.flush();
   while( true )
    {
      try
      {
        // get what client has to say ...
        clientRequest = reader.readLine();
        System.out.println( "Client says: " + clientRequest );
        if( clientRequest.startsWith( "HELP" ) )
        {
          writer.println( "Vocabulary: DATE HELP QUIT" );
          writer.flush();
        }
        else if( clientRequest.startsWith( "DATE" ) )
        {
          writer.println( "Todays date is " + new Date() );
          writer.flush();
        }
        else if( clientRequest.startsWith( "QUIT" ) )
        {
          System.exit( 0 );
        }
        else
        {
          writer.println( "ERR: Command '" + clientRequest + "' not understood." );
          writer.flush();
        }
      }
      catch( IOException e )
      {
        System.out.println( "IOEx in server " + e );
      }
      } // end while
  } // end main
} // end class
// ****************************************
class Server
{
  private ServerSocket server;
  private Socket socket;
 public InputStream in;
  public OutputStream out;
  public Server( int port )
  {
    try
    {
      server = new ServerSocket( port );
      System.out.println( "ServerSocket before accept: " + server );
      System.out.println( "John's Java server v.001, on-line!" );
      // wait for a client to connect to our port
      socket = server.accept();
      System.out.println( "ServerSocket after accept: " + server );
      in  = socket.getInputStream();
      out = socket.getOutputStream();
    }
    catch( IOException e )
    {
      System.out.println( "Server constructor IOEx: " + e );
    }
  }
}
Παράδειγμα 6

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία GenericClient.java και Server.java αντιστοίχως. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ. Prompt> javac GenericClient.java και Prompt> javac Server.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java Server <servicename> <port>, όπου στη θέση του <servicename> βάζουμε το όνομα της υπηρεσίας που θέλουμε να μας προσφέρει ο Server, και στη θέση του <port> βάζουμε την πύλη του Client, π.χ. Dimitra). Στην εφαρμογή μας, ο Server μπορεί να προσφέρει τέσσερις υπηρεσίες, των οποίων τα ονόματα (servicenames) είναι τα εξής: Server$Time, Server$Reverse, Server$HTTPMirror και Server$UniqueID. Για τον τρόπο λειτουργίας κάθε μιας από τις υπηρεσίες αυτές θα γίνει αναφορά παρακάτω. Ο Server, λοιπόν, τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java GenericClient <hostname> <port>, όπου <hostname> βάζουμε την ονομασία του Client, π.χ. Dimitra, και, προαιρετικά, όπου <port> βάζουμε την πύλη του Client, π.χ. 6789).

Αρχίζοντας την αναφορά μας σε κάθε μία από τις υπηρεσίες που προσφέρει ο Server στον Client, θα δούμε τι προσφέρει η υπηρεσία Server$Time στον Client. Κατ’ αρχήν, για να γίνει δυνατή η πρόσβαση στην υπηρεσία Server$Time, τρέχουμε το πρόγραμμα του Server μέσω της Java, δηλώνοντας: prompt> java Server Server$Time 6789. Μ’ αυτόν τον τρόπο θέτουμε τον Server σε αναμονή για να προσφέρει στον Client την υπηρεσία αυτή. Με το που συνδέεται ο Client, ενημερώνεται αμέσως για την τρέχουσα ώρα στον Server. Μετά την ενημέρωση αυτή διακόπτεται η μεταξύ τους επικοινωνία.

Όλη αυτή η διαδικασία μπορεί να αποτυπωθεί ως εξής:
Server: [Date/Time] Starting server
Server: [Date/Time] Starting connection manager. Max connections: 10
Server: [Date/Time] Starting service Server$Time on port 6789
Client : Connected to Dimitra:6789
Server: Connected to Dimitra on port 6789 for service Server$Time
Client : Date/Time
Client : Connection closed by server
Server: Connection to Dimitra closed
Για να γίνει δυνατή η πρόσβαση στην υπηρεσία Server$Reverse, τρέχουμε το πρόγραμμα του Server μέσω της Java, δηλώνοντας: prompt> java Server Server$Reverse 6789. Μ’ αυτόν τον τρόπο θέτουμε τον Server σε αναμονή για να προσφέρει στον Client την υπηρεσία αυτή. Με το που συνδέεται ο Client, λαμβάνει την δυνατότητα να συντάξει γραπτά μηνύματα. Ο Server αντιστρέφει το μήνυμα και το στέλνει στον Client αντεστραμμένο. Για παράδειγμα, αν ο Client συντάξει το μήνυμα “Hello”, θα λάβει ως απάντηση το μήνυμα “olleH”. Ο τερματισμός της υπηρεσίας αυτής επέρχεται, όταν ο Client στείλει το μήνυμα “.”.
Όλη αυτή η διαδικασία μπορεί να αποτυπωθεί ως εξής:
Server: [Date/Time] Starting server
Server: [Date/Time] Starting connection manager. Max connections: 10
Server: [Date/Time] Starting service Server$Reverse on port 6789
Client : Connected to Dimitra:6789
Server: Connected to Dimitra on port 6789 for service Server$Reverse
Client : Welcome to the line reversal server.
         Enter lines. Enter a ‘.’ on a line by itself
         > Hello        {Press Enter}
Client : olleH
         > .
Client : Connection closed by server
Server: Connection to Dimitra closed
Για να γίνει δυνατή η πρόσβαση στην υπηρεσία Server$HTTPMirror, τρέχουμε το πρόγραμμα του Server μέσω της Java, δηλώνοντας: prompt> java Server Server$HTTPMirror 6789. Μ’ αυτόν τον τρόπο θέτουμε τον Server σε αναμονή για να προσφέρει στον Client την υπηρεσία αυτή. Με το που συνδέεται ο Client, μπορεί να συντάξει μία HTTP αίτηση και να την στείλει στον Server. O Server εδώ είναι ένας απλός Web Server. Αυτός στέλνει πίσω την αίτηση του Client, όπως του την έστειλε. Αυτή η υπηρεσία έχει ενδιαφέρον όταν θέλουμε απλά να δούμε την αίτηση του Web Client ή, για παράδειγμα, ποιες πληροφορίες στάλθηκαν όταν υποβλήθηκε μία αίτηση.
Όλη αυτή η διαδικασία μπορεί να αποτυπωθεί ως εξής:
Server: [Date/Time] Starting server
Server: [Date/Time] Starting connection manager. Max connections: 10
Server: [Date/Time] Starting service Server$HTTPMirror on port 6789
Client : Connected to Dimitra:6789
Server: Connected to Dimitra on port 6789 for service Server$HTTPMirror
Client : Hello          {Press Enter}
                        {Press Enter}
        HTTP/1.0 200
        Content-Type: text/plain
        
        Hello
Client : Connection closed by server
Server: Connection to Dimitra closed
Για να γίνει δυνατή η πρόσβαση στην υπηρεσία Server$UniqueID, τρέχουμε το πρόγραμμα του Server μέσω της Java, δηλώνοντας: prompt> java Server Server$UniqueID 6789. Μ’ αυτόν τον τρόπο θέτουμε τον Server σε αναμονή για να προσφέρει στον Client την υπηρεσία αυτή. Με το που συνδέεται ο Client, μπορεί να δει με ποια σειρά συνδέθηκε. Ο πρώτος Client που θα συνδεθεί θα λάβει το μήνυμα “You are client #: 0” (“Eίσαι ο client #: 0) Ο δεύτερος Client που θα συνδεθεί θα λάβει το μήνυμα “You are client #: 1” (“Eίσαι ο client #: 1).
Όλη αυτή η διαδικασία μπορεί να αποτυπωθεί ως εξής:
Server: [Date/Time] Starting server
Server: [Date/Time] Starting connection manager. Max connections: 10
Server: [Date/Time] Starting service Server$UniqueID on port 6789
Client : Connected to Dimitra:6789
Server: Connected to Dimitra on port 6789 for service Server$UniqueID
Client : You are client #: 0
Client : Connection closed by server
Server: Connection to Dimitra closed
Παρατηρήσεις: [Sun Feb 11 12:03:45 GMT+02:00 2000] Δηλαδή: ο Server ξεκίνησε να λειτουργεί την Κυριακή, 11 Φεβρουαρίου του 2000 και ώρα 12:03:45, σε περιοχή που διαφέρει κατά δύο ώρες από την ζώνη του Greenwich. Κώδικας client παραδείγματος 6
// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples
import java.io.*;
import java.net.*;
/**
 * This program connects to a server at a specified host and port.
 * It reads text from the console and sends it to the server.
 * It reads text from the server and sends it to the console.
 **/
public class GenericClient {
  public static void main(String[] args) throws IOException {
    try {
      // Check the number of arguments
      if (args.length != 2) 
        throw new IllegalArgumentException("Wrong number of arguments");
      // Parse the host and port specifications
      String host = args[0];
      int port = Integer.parseInt(args[1]);
            // Connect to the specified host and port
      Socket s = new Socket(host, port);
    // Set up streams for reading from and writing to the server.
      // The from_server stream is final for use in the anonymous class below
      final Reader from_server = new InputStreamReader(s.getInputStream());
      PrintWriter to_server = 
        new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
        // Set up streams for reading from and writing to the console
      // The to_user stream is final for use in the anonymous class below.
      BufferedReader from_user = 
        new BufferedReader(new InputStreamReader(System.in));
      final PrintWriter to_user =
        new PrintWriter(new OutputStreamWriter(System.out));
            // Tell the user that we've connected
      to_user.println("Connected to " + s.getInetAddress() + ":"+ s.getPort());
      to_user.flush();
            // Create a thread that gets output from the server and displays 
      // it to the user.  We use a separate thread for this so that we can
      // receive asynchronous output
      Thread t = new Thread() {
        public void run() {
          char[] buffer = new char[1024];
          int chars_read;
          try { 
            while((chars_read = from_server.read(buffer)) != -1) {
              to_user.write(buffer, 0, chars_read);
              to_user.flush();
            }
          } 
          catch (IOException e) { to_user.println(e); }
    // When the server closes the connection, the loop above will end.
          // Tell the user what happened, and call System.exit(), causing
          // the main thread to exit along with this one.
          to_user.println("Connection closed by server.");
          to_user.flush();
          System.exit(0);
        }
      };
            // We set the priority of the server-to-user thread above to be one
      // level higher than the main thread.  We shouldn't have to do this, but
      // on some operating systems, output sent to the console doesn't appear
      // when a thread at the same priority level is blocked waiting for
      // input from the console.
      t.setPriority(Thread.currentThread().getPriority() + 1);
      // Now start the server-to-user thread
      t.start();
      // And in parallel, read the user's input and pass it on to the server.
      String line;
      while((line = from_user.readLine()) != null) {
        to_server.println(line);
        to_server.flush();
      }
      // If the user types a Ctrl-D (Unix) or Ctrl-Z (Windows) to end their
      // input, we'll get and EOF, and the loop above will exit.  When this
      // happens, we stop the server-to-user thread and close the socket.
      t.stop();
      s.close();
      to_user.println("Connection closed by client.");
      to_user.flush();
    }
    // If anything goes wrong, print an error message
    catch (Exception e) { 
      System.err.println(e);
      System.err.println("Usage: java GenericClient  ");
    }
  }
}
Κώδικας server παραδείγματος 6
This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples
import java.io.*;
import java.net.*;
import java.util.*;
/**
 * This class is a generic framework for a flexible, multi-threaded server.
 * It listens on any number of specified ports, and, when it receives a 
 * connection on a port, passes input and output streams to a specified Service
 * object which provides the actual service.  It can limit the number of
 * concurrent connections, and logs activity to a specified stream.
 **/
public class Server {
  /**
   * A main() method for running the server as a standalone program.
   * The command-line arguments to the program should be pairs of servicenames
   * and port numbers.  For each pair, the program will dynamically load the 
   * named Service class, instantiate it, and tell the server to provide that
   * Service on the specified port.  The special -control argument should be
   * followed by a password and port, and will start special server control
   * service running on the specified port, protected by the specified 
   * password.
   **/
  public static void main(String[] args) {
    try {
      if (args.length < 2)  // Check number of arguments
        throw new IllegalArgumentException("Must start at least one service");
            // Create a Server object that uses standard out as its log and
      // has a limit of ten concurrent connections at once.
      Server s = new Server(System.out, 10);
  // Parse the argument list
      int i = 0;
      while(i < args.length) {
        if (args[i].equals("-control")) {    // Handle the -control argument
          i++;
          String password = args[i++];
          int port = Integer.parseInt(args[i++]);
          s.addService(new Control(s, password), port);  // add control service
        } 
        else {
          // Otherwise start a named service on the specified port.
          // Dynamically load and instantiate a class that implements Service.
          String serviceName = args[i++];
          Class serviceClass = Class.forName(serviceName); // dynamic load
          Service service = (Service)serviceClass.newInstance(); // instantiate
          int port = Integer.parseInt(args[i++]);
          s.addService(service, port);
        }
      }
    }

    catch (Exception e) { // Display a message if anything goes wrong
      System.err.println("Server: " + e);
      System.err.println("Usage: java Server [-control  ] " +
                         "[  ... ]");
      System.exit(1);
    }
  }
     // This is the state for the server
  ConnectionManager connectionManager; // The ConnectionManager object
  Hashtable services;                  // The current services and their ports
  ThreadGroup threadGroup;             // The threadgroup for all our threads
  PrintWriter logStream;               // Where we send our logging output to
/**
   * This is the Server() constructor.  It must be passed a stream 
   * to send log output to (may be null), and the limit on the number of
   * concurrent connections.  It creates and starts a ConnectionManager 
   * thread which enforces this limit on connections.
   **/
  public Server(OutputStream logStream, int maxConnections) { 
    setLogStream(logStream);
    log("Starting server");
    threadGroup = new ThreadGroup("Server");
    connectionManager = new ConnectionManager(threadGroup, maxConnections);
    connectionManager.start();
    services = new Hashtable();
  }
  /** 
   * A public method to set the current logging stream.  Pass null
   * to turn logging off
   **/
  public void setLogStream(OutputStream out) {
    if (out != null) logStream = new PrintWriter(new OutputStreamWriter(out));
    else logStream = null;
  }
  /** Write the specified string to the log */
  protected synchronized void log(String s) { 
    if (logStream != null) {
      logStream.println("[" + new Date() + "] " + s);
      logStream.flush();
    }
  }
  /** Write the specified object to the log */
  protected void log(Object o) { log(o.toString()); }
  /**
   * This method makes the server start providing a new service.
   * It runs the specified Service object on the specified port.
   **/
  public void addService(Service service, int port) throws IOException {
    Integer key = new Integer(port);  // the hashtable key
    // Check whether a service is already on that port
    if (services.get(key) != null) 
      throw new IllegalArgumentException("Port " + port + " already in use.");
    // Create a Listener object to listen for connections on the port
    Listener listener = new Listener(threadGroup, port, service);
    // Store it in the hashtable
    services.put(key, listener);
    // Log it
    log("Starting service " + service.getClass().getName() + 
        " on port " + port);
    // Start the listener running.
    listener.start();
  }
  /**
   * This method makes the server stop providing a service on a port.
   * It does not terminate any pending connections to that service, merely
   * causes the server to stop accepting new connections
   **/
  public void removeService(int port) {
    Integer key = new Integer(port);  // hashtable key
    // Look up the Listener object for the port in the hashtable of services
    final Listener listener = (Listener) services.get(key);
    if (listener == null) return;
    // Ask the listener to stop
    listener.pleaseStop();
    // Remove it from the hashtable
    services.remove(key);
    // And log it.
    log("Stopping service " + listener.service.getClass().getName() + 
        " on port " + port);
  }
  /** 
   * This nested Thread subclass is a "listener".  It listens for connections
   * on a specified port (using a ServerSocket) and when it gets a connection
   * request, it calls a method of the ConnectionManager to accept (or reject)
   * the connection.  There is one Listener for each Service being provided
   * by the Server.  The Listener passes the Server object to the 
   * ConnectionManager, but doesn't do anything with it itself.
   */
  public class Listener extends Thread {
    ServerSocket listen_socket;   // The socket we listen for connections on
    int port;                     // The port we're listening on
    Service service;              // The service to provide on that port
    boolean stop = false;         // Whether we've been asked to stop
    /**
     * The Listener constructor creates a thread for itself in the specified
     * threadgroup.  It creates a ServerSocket to listen for connections
     * on the specified port.  It arranges for the ServerSocket to be
     * interruptible, so that services can be removed from the server.
     **/
    public Listener(ThreadGroup group, int port, Service service) 
         throws IOException {
      super(group, "Listener:" + port);      
      listen_socket = new ServerSocket(port);
      // give the socket a non-zero timeout so accept() can be interrupted
      listen_socket.setSoTimeout(600000);
      this.port = port;
      this.service = service;
    }
    /** This is the nice way to get a Listener to stop accepting connections */
    public void pleaseStop() {
      this.stop = true;   // set the stop flag
      this.interrupt();   // and make the accept() call stop blocking
    }
        /**
     * A Listener is a Thread, and this is its body.
     * Wait for connection requests, accept them, and pass the socket on
     * to the ConnectionManager object of this server
     **/
    public void run() {
      while(!stop) {      // loop until we're asked to stop.
        try {
          Socket client = listen_socket.accept();
          connectionManager.addConnection(client, service);
        } 
        catch (InterruptedIOException e) {} 
        catch (IOException e) {log(e);}
      }
    }
  }
  /**
   * This nested class  manages client connections for the server.  
   * It maintains a list of current connections and enforces the
   * maximum connection limit.  It creates a separate thread (one per
   * server) that sits around and wait()s to be notify()'d that a connection 
   * has terminated.  When this happens, it updates the list of connections.
   **/
  public class ConnectionManager extends Thread {
    int maxConnections;  // The maximum number of allowed connections
    Vector connections;  // The current list of connections

    /** 
     * Create a ConnectionManager in the specified thread group to enforce
     * the specified maximum connection limit.  Make it a daemon thread so
     * the interpreter won't wait around for it to exit.
     **/
    public ConnectionManager(ThreadGroup group, int maxConnections) { 
      super(group, "ConnectionManager");
      this.setDaemon(true); 
      this.maxConnections = maxConnections;
      connections = new Vector(maxConnections);
      log("Starting connection manager.  Max connections: " + maxConnections);
    }
    /**
     * This is the method that Listener objects call when they accept a
     * connection from a client.  It either creates a Connection object 
     * for the connection and adds it to the list of current connections,
     * or, if the limit on connections has been reached, it closes the 
     * connection. 
     **/
      synchronized void addConnection(Socket s, Service service) {
      // If the connection limit has been reached
      if (connections.size() >= maxConnections) {
        try {
          PrintWriter out = new PrintWriter(s.getOutputStream());
          // Then tell the client it is being rejected.
          out.println("Connection refused; " +
                      "server has reached maximum number of clients.");
          out.flush();
          // And close the connection to the rejected client.
          s.close();
          // And log it, of course
          log("Connection refused to " + s.getInetAddress().getHostAddress() +
              ":" + s.getPort() + ": max connections reached.");
        } catch (IOException e) {log(e);}
      }
      else {  // Otherwise, if the limit has not been reached
        // Create a Connection thread to handle this connection
        Connection c = new Connection(s, service);
        // Add it to the list of current connections
        connections.addElement(c);
        // Log this new connection
        log("Connected to " + s.getInetAddress().getHostAddress() +
            ":" + s.getPort() + " on port " + s.getLocalPort() +
            " for service " + service.getClass().getName());
        // And start the Connection thread running to provide the service
        c.start();
      }
    }
          /**
     * A Connection object calls this method just before it exits.
     * This method uses notify() to tell the ConnectionManager thread
     * to wake up and delete the thread that has exited.
     **/
    public synchronized void endConnection() { this.notify(); }
 /** Change the current connection limit */
    public synchronized void setMaxConnections(int max) { maxConnections=max; }
   /** 
     * Output the current list of connections to the specified stream.
     * This method is used by the Control service defined below.
     **/
    public synchronized void printConnections(PrintWriter out) {
      for(int i = 0; i < connections.size(); i++) {
        Connection c = (Connection)connections.elementAt(i);
        out.println("CONNECTED TO " + 
                    c.client.getInetAddress().getHostAddress() + ":" +
                    c.client.getPort() + " ON PORT " + c.client.getLocalPort()+
                    " FOR SERVICE " + c.service.getClass().getName());
      }
    }
    /**
     * The ConnectionManager is a thread, and this is the body of that
     * thread.  While the ConnectionManager methods above are called by other
     * threads, this method is run in its own thread.  The job of this thread
     * is to keep the list of connections up to date by removing connections
     * that are no longer alive.  It uses wait() to block until notify()'d by
     * the endConnection() method.
     **/
    public void run() {
      while(true) {  // infinite loop
        // Check through the list of connections, removing dead ones
        for(int i = 0; i < connections.size(); i++) {
          Connection c = (Connection)connections.elementAt(i);
          if (!c.isAlive()) {
            connections.removeElementAt(i);
            log("Connection to " + c.client.getInetAddress().getHostAddress() +
                ":" + c.client.getPort() + " closed.");
          }
        }
        // Now wait to be notify()'d that a connection has exited
        // When we wake up we'll check the list of connections again.
        try { synchronized(this) { this.wait(); } }
        catch(InterruptedException e) {}
      }
    }
  }
  /**
   * This class is a subclass of Thread that handles an individual connection
   * between a client and a Service provided by this server.  Because each
   * such connection has a thread of its own, each Service can have multiple
   * connections pending at once.  Despite all the other threads in use, this 
   * is the key feature that makes this a multi-threaded server implementation.
   **/
  public class Connection extends Thread {
    Socket client;     // The socket to talk to the client through
    Service service;   // The service being provided to that client
    /**
     * This constructor just saves some state and calls the superclass
     * constructor to create a thread to handle the connection.  Connection
     * objects are created by Listener threads.  These threads are part of
     * the server's ThreadGroup, so all Connection threads are part of that
     * group, too.
     **/
    public Connection(Socket client, Service service) {
      super("Server.Connection:" + client.getInetAddress().getHostAddress() +
            ":" + client.getPort());
      this.client = client;
      this.service = service;
    }
    /**
     * This is the body of each and every Connection thread.
     * All it does is pass the client input and output streams to the 
     * serve() method of the specified Service object.  That method
     * is responsible for reading from and writing to those streams to
     * provide the actual service.  Recall that the Service object has been
     * passed from the Server.addService() method to a Listener object
     * to the ConnectionManager.addConnection() to this Connection object,
     * and is now finally getting used to provide the service.
     * Note that just before this thread exits it calls the 
     * ConnectionManager.endConnection() method to wake up the 
     * ConnectionManager thread so that it can remove this Connection
     * from its list of active connections.
     **/
    public void run() {
      try { 
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
        service.serve(in, out);
      } 
      catch (IOException e) {log(e);}
      finally { connectionManager.endConnection(); }
    }
  }
  /**
   * Here is the Service interface that we have seen so much of.
   * It defines only a single method which is invoked to provide the service.
   * serve() will be passed an input stream and an output stream to the client.
   * It should do whatever it wants with them, and should close them before
   * returning.
   *
   * All connections through the same port to this service share a single
   * Service object.  Thus, any state local to an individual connection must 
   * be stored in local variables within the serve() method.  State that should
   * be global to all connections on the same port should be stored in
   * instance variables of the Service class.  If the same Service is running 
   * on more than one port, there will typically be different Service instances
   * for each port.  Data that should be global to all connections on any port
   * should be stored in static variables.
   *
   * Note that implementations of this interface must have a no-argument 
   * constructor if they are to be dynamically instantiated by the main()
   * method of the Server class.
   **/
  public interface Service {
    public void serve(InputStream in, OutputStream out) throws IOException;
  }
  /**
   * A very simple service.  It displays the current time on the server
   * to the client, and closes the connection.
   **/
  public static class Time implements Service {
    public void serve(InputStream i, OutputStream o) throws IOException {
      PrintWriter out = new PrintWriter(new OutputStreamWriter(o));
      out.println(new Date());
      out.close();
      i.close();
    }
  }
  /**
   * This is another example service.  It reads lines of input from the
   * client, and sends them back, reversed.  It also displays a welcome
   * message and instructions, and closes the connection when the user 
   * enters a '.' on a line by itself.
   **/
  public static class Reverse implements Service {
    public void serve(InputStream i, OutputStream o) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(i));
      PrintWriter out = 
        new PrintWriter(new BufferedWriter(new OutputStreamWriter(o)));
      out.println("Welcome to the line reversal server.");
      out.println("Enter lines.  End with a '.' on a line by itself");
      for(;;) {
        out.print("> ");
        out.flush();
        String line = in.readLine();
        if ((line == null) || line.equals(".")) break;
        for(int j = line.length()-1; j >= 0; j--)
          out.print(line.charAt(j));
        out.println();
      }
      out.close();
      in.close();
    }
  }
  /**
   * This service is an HTTP mirror, just like the HttpMirror class
   * implemented earlier in this chapter.  It echos back the client's
   * HTTP request
   **/
  public static class HTTPMirror implements Service {
    public void serve(InputStream i, OutputStream o) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(i));
      PrintWriter out = new PrintWriter(new OutputStreamWriter(o));
      out.println("HTTP/1.0 200 ");
      out.println("Content-Type: text/plain");
      out.println();
      String line;
      while((line = in.readLine()) != null) {
        if (line.length() == 0) break;
        out.println(line);
      }
      out.close();
      in.close();
    }
  }
  /**
   * This service demonstrates how to maintain state across connections
   * by saving it in instance variables and using synchronized access to
   * those variables.  It maintains a count of how many clients have connected
   * and tells each client what number it is
   **/
  public static class UniqueID implements Service {
    public int id=0;
    public synchronized int nextId() { return id++; }
    public void serve(InputStream i, OutputStream o) throws IOException {
      PrintWriter out = new PrintWriter(new OutputStreamWriter(o));
      out.println("You are client #: " + nextId());
      out.close();
      i.close();
    }
  }
  /**
   * This is a non-trivial service.  It implements a command-based protocol
   * that gives password-protected runtime control over the operation of the 
   * server.  See the main() method of the Server class to see how this
   * service is started.  
   *
   * The recognized commands are:
   *   password:   give password; authorization is required for most commands
   *   add:        dynamically add a named service on a specified port
   *   remove:     dynamically remove the service running on a specified port
   *   max:        change the current maximum connection limit.
   *   status:     display current services, connections, and connection limit
   *   help:       display a help message
   *   quit:       disconnect
   *
   * This service displays a prompt, and sends all of its output to the user
   * in capital letters.  Only one client is allowed to connect to this service
   * at a time.
   **/
  public static class Control implements Service {
    Server server;             // The server we control
    String password;           // The password we require
    boolean connected = false; // Whether a client is already connected to us
    /**
     * Create a new Control service.  It will control the specified Server
     * object, and will require the specified password for authorization
     * Note that this Service does not have a no argument constructor, which
     * means that it cannot be dynamically instantiated and added as the other,
     * generic services above can be.
     **/
    public Control(Server server, String password) {
      this.server = server;
      this.password = password;
    }
    /**
     * This is the serve method that provides the service.  It reads a line 
     * the client, and uses java.util.StringTokenizer to parse it into
     * commands and arguments.  It does various things depending on the 
     * command.

     **/
    public void serve(InputStream i, OutputStream o) throws IOException {
      // Setup the streams
      BufferedReader in = new BufferedReader(new InputStreamReader(i));
      PrintWriter out = new PrintWriter(new OutputStreamWriter(o));
      String line;
      boolean authorized = false;  // Has the user has given the password yet?
      int num;
      // If there is already a client connected to this service, display a
      // message to this client and close the connection.  We use a 
      // synchronized block to prevent a race condition.
      synchronized(this) { 
        if (connected) { 
          out.println("ONLY ONE CONTROL CONNECTION ALLOWED AT A TIME.");
          out.close();
          return;
        }
        else connected = true;
      }
      for(;;) {  // infinite loop
        out.print("> ");           // Display a prompt
        out.flush();               // Make it appear right away
        line = in.readLine();      // Get the user's input
        if (line == null) break;   // Quit if we get EOF.
        try {
          // Use a StringTokenizer to parse the user's command
          StringTokenizer t = new StringTokenizer(line);
          if (!t.hasMoreTokens()) continue;  // if input was blank line
          // Get the first word of the input and convert to lower case
          String command = t.nextToken().toLowerCase(); 
          // Now compare it to each of the possible commands, doing the
          // appropriate thing for each command
          if (command.equals("password")) {       // Password command
            String p = t.nextToken();             // Get the next word of input
            if (p.equals(this.password)) {        // Does it equal the password
              out.println("OK");                  // Say so
              authorized = true;                  // Grant authorization
            }
            else out.println("INVALID PASSWORD"); // Otherwise fail
          }
          else if (command.equals("add")) {       // Add Service command
            // Check whether password has been given
            if (!authorized) out.println("PASSWORD REQUIRED"); 
            else {
              // Get the name of the service and try to dynamically load
              // and instantiate it.  Exceptions will be handled below
              String serviceName = t.nextToken();
              Class serviceClass = Class.forName(serviceName);
              Service service;
              try { service = (Service) serviceClass.newInstance(); }
              catch (NoSuchMethodError e) {
                throw new IllegalArgumentException("Service must have a " +
                                                   "no-argument constructor");
              }
              int port = Integer.parseInt(t.nextToken());
              // If no exceptions occurred, add the service
              server.addService(service, port);
              out.println("SERVICE ADDED");      // acknowledge
            }
          }
          else if (command.equals("remove")) {   // Remove service command
            if (!authorized) out.println("PASSWORD REQUIRED");
            else {
              int port = Integer.parseInt(t.nextToken());  // get port
              server.removeService(port);     // remove the service on it
              out.println("SERVICE REMOVED"); // acknowledge
            }
          }
          else if (command.equals("max")) {      // Set max connection limit
            if (!authorized) out.println("PASSWORD REQUIRED");
            else {
              int max = Integer.parseInt(t.nextToken());        // get limit
              server.connectionManager.setMaxConnections(max);  // set limit
              out.println("MAX CONNECTIONS CHANGED");           // acknowledge
            }
          }
          else if (command.equals("status")) {    // Status Display command
            if (!authorized) out.println("PASSWORD REQUIRED");
            else {
              // Display a list of all services currently running
              Enumeration keys = server.services.keys();  
              while(keys.hasMoreElements()) {
                Integer port = (Integer) keys.nextElement();
                Listener listener = (Listener)server.services.get(port);
                out.println("SERVICE " + listener.service.getClass().getName()+
                            " ON PORT " + port);
              }
              // Display a list of all current connections
              server.connectionManager.printConnections(out);
              // Display the current connection limit
              out.println("MAX CONNECTIONS: " + 
                          server.connectionManager.maxConnections);
            }
          }
          else if (command.equals("help")) {            // Help command
            // Display command syntax.  Password not required
            out.println("COMMANDS:\n" + 
                        "\tpassword \n" +
                        "\tadd  \n" +
                        "\tremove \n" +
                        "\tmax \n" +
                        "\tstatus\n" +
                        "\thelp\n" + 
                        "\tquit");
          }
          else if (command.equals("quit")) break;    // Quit command.  Exit.
          else out.println("UNRECOGNIZED COMMAND");  // Unknown command error
        }
        catch (Exception e) {
          // If an exception occurred during the command, print an error
          // message, then output details of the exception.
          out.println("EXCEPTION WHILE PARSING OR EXECUTING COMMAND:");
          out.println(e);
        }
      }
      // Finally, when the loop command loop ends, close the streams
      // and set our connected flag to false so that other clients can
      // now connect.
      out.close();
      in.close();
      connected = false;
    }    
  }
}
Παράδειγμα 7

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία client.c και server.c αντιστοίχως. Μέσω της γλώσσας C, μεταγλωττίζουμε τα προγράμματα με τον C Compiler. Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. server <port>, όπου <port> βάζουμε την πύλη του Server, π.χ 6789). Παρατηρούμε ότι ο Server τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. client <hostname> <port>, όπου <hostname> βάζουμε την ονομασία του Client, π.χ. Dimitra, και όπου <port> βάζουμε την πύλη του Client, π.χ. 6789). Εάν όλα λειτουργήσουν σωστά, το αποτέλεσμα στο παράθυρο του client θα είναι:

Connection request from 6789
Hello, World!
Κώδικας Client παραδείγματος 7
/*
* An example client for "Hello, World!" server
*/
#include                 /* perror() */
#include                /* atoi() */
#include 
#include 
#include                /* read() */
#include 
#include 
#include 
int main(int argc, char *argv[])
{
    int clientSocket,
        remotePort,
        status = 0;
    struct hostent *hostPtr = NULL;
    struct sockaddr_in serverName = { 0 };
    char buffer[256] = "";
    char *remoteHost = NULL;
    if (3 != argc)
    {
        fprintf(stderr, "Usage: %s  \n",
            argv[0]);
        exit(1);
    }
    remoteHost = argv[1];
    remotePort = atoi(argv[2]);
    clientSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == clientSocket)
    {
        perror("socket()");
        exit(1);
    }
    /*
     * need to resolve the remote server name or IP address
     */
    hostPtr = gethostbyname(remoteHost);
    if (NULL == hostPtr)
    {
        hostPtr = gethostbyaddr(remoteHost, strlen(remoteHost),
            AF_INET);
        if (NULL == hostPtr)
        {
            perror("Error resolving server address ");
            exit(1);
        }
    }
    serverName.sin_family = AF_INET;
    serverName.sin_port = htons(remotePort);
    (void) memcpy(&serverName.sin_addr, hostPtr->h_addr,
        hostPtr->h_length);
    status = connect(clientSocket, (struct sockaddr*) &serverName,
        sizeof(serverName));
    if (-1 == status)
    {
        perror("connect()");
        exit(1);
    }
    /*
     * Client application specific code goes here
     *
     * e.g. receive messages from server, respond, etc.
     */
    while (0 < (status = read(clientSocket, buffer,
        sizeof(buffer) - 1)))
    {
        printf("%d: %s", status, buffer);
    }
    if (-1 == status)
    {
        perror("read()");
    }
    close(clientSocket);
    return 0;
}


Κώδικας Server παραδείγματος 7

/*
 * Simple "Hello, World!" server
*/
#include        /* */
#include        /* exit() */
#include        /* memset(), memcpy() */
#include        /* uname() */
#include 
#include        /* socket(), bind(), listen(),
                                           accept() */
#include 
#include 
#include 
#include        /* fork(), write(), close() */
/*
 * prototypes
 */
int _GetHostName(char *buffer, int length);
/*
 * constants
 */
const char MESSAGE[] = "Hello, World!\n";
const int BACK_LOG = 5;
int main(int argc, char *argv[])
{
    int serverSocket = 0,
        on = 0,
        port = 0,
        status = 0,
        childPid = 0;
    struct hostent *hostPtr = NULL;
    char hostname[80] = "";
    struct sockaddr_in serverName = { 0 };
    if (2 != argc)
    {
        fprintf(stderr, "Usage: %s \n", argv[0]);
        exit(1);
    }
    port = atoi(argv[1]);
    serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == serverSocket)
    {
        perror("socket()");
        exit(1);
    }
    /*
     * turn off bind address checking, and allow port numbers
     * to be reused - otherwise the TIME_WAIT phenomenon will
     * prevent binding to these address.port combinations for
     * (2 * MSL) seconds.
     */
    on = 1;
    status = setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,
        (const char *) &on, sizeof(on));
    if (-1 == status)
    {
        perror("setsockopt(...,SO_REUSEADDR,...)");

    }
    /*
     * when connection is closed, there is a need to linger to ensure
     * all data is transmitted, so turn this on also
     */
    {
        struct linger linger = { 0 };
        linger.l_onoff = 1;
        linger.l_linger = 30;
        status = setsockopt(serverSocket, SOL_SOCKET, SO_LINGER,
            (const char *) &linger, sizeof(linger));
        if (-1 == status)
        {
            perror("setsockopt(...,SO_LINGER,...)");
        }
    }
    /*
     * find out who I am
     */
    status = _GetHostName(hostname, sizeof(hostname));
    if (-1 == status)
    {
        perror("_GetHostName()");
        exit(1);
    }
    hostPtr = gethostbyname(hostname);
    if (NULL == hostPtr)
    {
        perror("gethostbyname()");
        exit(1);
    }
    (void) memset(&serverName, 0, sizeof(serverName));
    (void) memcpy(&serverName.sin_addr, hostPtr->h_addr,
       hostPtr->h_length);
    /*
     * to allow server be contactable on any of its
     * IP addresses, uncomment the following line of code:
     *
     * serverName.sin_addr.s_addr = htonl(INADDR_ANY);
     */
    serverName.sin_family = AF_INET;
    serverName.sin_port = htons(port); /* network-order */
    status = bind(serverSocket, (struct sockaddr *) &serverName,
        sizeof(serverName));
    if (-1 == status)
    {
        perror("bind()");
        exit(1);
    }
    status = listen(serverSocket, BACK_LOG);
    if (-1 == status)
    {
        perror("listen()");
        exit(1);
    }
    for (;;)
    {
        struct sockaddr_in clientName = { 0 };
        int slaveSocket, clientLength = sizeof(clientName);
        (void) memset(&clientName, 0, sizeof(clientName));
        slaveSocket = accept(serverSocket,
            (struct sockaddr *) &clientName, &clientLength);
        if (-1 == slaveSocket)
        {
            perror("accept()");
            exit(1);
        }
        childPid = fork();
        switch (childPid)
        {
        case -1: /* ERROR */
            perror("fork()");
            exit(1);
        case 0: /* child process */
            close(serverSocket);
            if (-1 == getpeername(slaveSocket,
                (struct sockaddr *) &clientName, &clientLength))
            {
                perror("getpeername()");
            }
            else
            {
                printf("Connection request from %s\n",
                    inet_ntoa(clientName.sin_addr));
            }
            /*
             * Server application specific code goes here,
             *
             * e.g. perform some action, respond to client etc.
             */
            write(slaveSocket, MESSAGE, strlen(MESSAGE));
            close(slaveSocket);
            exit(0);
        default: /* parent process */
            close(slaveSocket);
        }
    }
    return 0;
}
/*
 * Local replacement of gethostname() to aid portability
 */
int _GetHostName(char *buffer, int length)
{
    struct utsname sysname = { 0 };
    int status = 0;
    status = uname(&sysname);
    if (-1 != status)
    {
        strncpy(buffer, sysname.nodename, length);
    }
    return (status);
}
Παραλλαγές του παραδείγματος 7
Παράδειγμα Εφαρμογής Ερωτήσεων-Αποκρίσεων μεταξύ Client και Server
Ας ξεκινήσουμε την ανάλυση του παραδείγματος ενσωματώνοντας τον client και τον server σε ένα πρόγραμμα. Όταν γίνει κατανοητό το πως λειτουργούν μεταξύ τους, θα διαιρέσουμε τον κώδικα για τον Client και τον Server. Στο ακόλουθο πρόγραμμα ο client προσομοιώνεται από την  client συνάρτηση. Η κύρια ρουτίνα προσομοιώνει τον server.

/* local test of client-server code */
#include 
#include 
#include 
char name[256] = "";
char buffer[256] = "";
void client(char *buffer)
{
 printf("%s", buffer);
 fgets(buffer, 256, stdin);
}
 int main(int argc, char *argv[])
{
 int year, age;
 sprintf(buffer, "Please enter your name: ");
 client(buffer);
 strcpy(name, buffer);
 sprintf(buffer, "Hi, %sPlease enter your year of birth: ", name);
 client(buffer);
 year = atoi(buffer);
 age = 2000 - year;
 sprintf(buffer, "Your approximate age is %d.\nEnter q to quit: ", age);
 client(buffer);
 return(0);
}
        
Αν αντιγράψουμε το παραπάνω κώδικα στον editor, τον σώσουμε ως localtest.c, τον μεταγλωττίσουμε με τον C Compiler και τον τρέξουμε μέσω της γλώσσας C θα πάρουμε τα ακόλουθα αποτελέσματα:
Please enter your name: John
Hi, John
Please enter your year of birth: 1960
Your approximate age is 40.
Enter q to quit: q

Δηλαδή, o server στέλνει το μήνυμα “Please enter your name” (Παρακαλώ δώσε το όνομά σου) στον client. Αυτός εισάγει το όνομά του από το πληκτρολόγιο και το αποστέλλει. Μετά ο server ζητάει το έτος γέννησης. Ο client το αποστέλλει  ως αλφαριθμητικό και ο server, αφού πρώτα το μετατρέψει σε αριθμό το αφαιρεί από το έτος 2000. Έτσι στέλνει πίσω στον client την ηλικία του. Για να διακοπεί η επικοινωνία πρέπει ο client να εισάγει μέσω του πληκτρολογίου το γράμμα ‘q’.
Ας προχωρήσουμε τώρα σε μια πραγματική επικοινωνία client-server. Εισάγουμε δηλώσεις στο πρόγραμμα server.c αλλάζοντας την αρχή των δηλώσεων του main σε: 
int main(int argc, char *argv[])
{
int i, year, age;
char name[256] = "";
char buffer[256] = "";
char null_buffer[256] = "";
    int serverSocket = 0,

Αντικαθιστούμε τον κώδικα που αναφέρεται στην υπηρεσία που προσφέρει ο server με τα ακόλουθα:

/*
* Server application specific code goes here,
* e.g. perform some action, respond to client etc.
*/
sprintf(buffer, "Please enter your name: ");
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
/* get name */
read(slaveSocket, buffer, sizeof(buffer));
strcpy(name, buffer);
sprintf(buffer, "Hi, %sPlease enter your year of birth: ", name);
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
/* get year of birth */
read(slaveSocket, buffer, sizeof(buffer));
year = atoi(buffer);
age = 2000 - year;
sprintf(buffer, "Your approximate age is %d.\nEnter q to quit: ", age);
write(slaveSocket, buffer, strlen(buffer));
close(slaveSocket);
exit(0);

        Ο κώδικας αυτός είναι σχεδόν ίδιος με αυτόν της προηγούμενης εφαρμογής, με την εξαίρεση ότι διαβάζουμε και γράφουμε slaveSocket  αντί να καλούμε την συνάρτηση client. SlaveSocket θεωρείται η σύνδεση μέσω του διαύλου που ενώνει τον server και τον client. 
Ο κώδικας του client είναι απλός. Εισάγουμε δηλώσεις στο πρόγραμμα client.c αλλάζοντας την αρχή τον δηλώσεων του main σε:
read 
client.c
int main(int argc, char *argv[])
{
  int i;
  int clientSocket,

Αντικαθιστούμε τον κώδικα που αναφέρεται στις ενέργειες του client με τα ακόλουθα:
/*
* Client application specific code goes here
* e.g. receive messages from server, respond, etc.
* Receive and respond until server stops sending messages
*/
while (0 < (status = read(clientSocket, buffer, sizeof(buffer))))
  {
    printf("%s", buffer);
    for (i = 0; i <= 255; i++) buffer[i] = 0;
    fgets(buffer, 256, stdin);
    write(clientSocket, buffer, strlen(buffer));
  }
    close(clientSocket);
    return 0;
  }

Επίσης, ο κώδικας αυτός είναι σχεδόν ίδιος με αυτόν της προηγούμενης εφαρμογής. Η κύρια διαφορά είναι η χρήση του clientSocket, η διαφοροποίηση του τέλους του slaveSocket στον Server και η while δήλωση για τον έλεγχο του προγράμματος. Η while δήλωση κλείνει όταν ο server σταματήσει να στέλνει μηνύματα.
Μετά τις αλλαγές αυτές, μεταγλωττίζουμε πάλι τα προγράμματα client.c και server.c και τα τρέχουμε ξανά όπως προηγουμένως. Αυτή την φορά το αποτέλεσμα στο παράθυρο του client θα είναι: 
Connection request from dimitra
Please enter your name: John
Hi, John.
Please enter your year of birth: 1960
Your approximate age is 40.
Enter q to quit: q
Πρόγραμμα Συνομιλίας (Chat Program)
        Τέλος, θα αναφερθούμε σε ένα πρόγραμμα συνομιλίας για ανταλλαγή μηνυμάτων μεταξύ χρηστών. Είναι σε στοιχειώδης μορφή, διότι επιτρέπει μόνο εναλλασσόμενες γραμμές μεταξύ κάθε ατόμου και απαιτεί από τον server να διατηρεί ένα παράθυρο ανοιχτό. Αλλά δείχνει πώς διεξάγεται ο διάλογος αυτός και θα μπορούσε να εξελιχθεί σε ένα πολύ πρακτικό πρόγραμμα.
Εισάγουμε δηλώσεις στο πρόγραμμα server.c αλλάζοντας την αρχή των δηλώσεων του main σε:
 int main(int argc, char *argv[])
{
  char buffer[256] = "";
  int i, serverquit = 1, clientquit = 1;
    int serverSocket = 0,

Αντικαθιστούμε τον κώδικα που αναφέρεται στην υπηρεσία που προσφέρει ο server με τα ακόλουθα:
/*
* Server application specific code goes here,
* e.g. perform some action, respond to client etc.
*/
printf("Send q to quit.\n");
sprintf(buffer, "Hi, %s\nS: Please start chat. Send q to quit.\n", inet_ntoa(clientName.sin_addr));
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
while (serverquit != 0 && clientquit != 0)
{
 status = 0;
 while (status == 0)
  status = read(slaveSocket, buffer, sizeof(buffer));
 clientquit = strcmp(buffer, "q\n");
 if (clientquit != 0)
 {
  printf("C: %s", buffer);
  for (i = 0; i <= 255; i++) buffer[i] = 0;
  printf("S: ");
  fgets(buffer, 256, stdin);
  serverquit  = strcmp(buffer, "q\n");
  write(slaveSocket, buffer, strlen(buffer));
  for (i = 0; i <= 255; i++) buffer[i] = 0;
 }
}
printf("Goodbye\n");
close(slaveSocket);
exit(0);

Εισάγουμε δηλώσεις στο πρόγραμμα client.c αλλάζοντας την αρχή τον δηλώσεων του main σε:
int main(int argc, char *argv[])
{
 int i, serverquit = 1, clientquit = 1;
    int clientSocket,

Αντικαθιστούμε τον κώδικα που αναφέρεται στις ενέργειες του client με τα ακόλουθα:
/*
* Client application specific code goes here
* e.g. receive messages from server, respond, etc.
*/
while (serverquit != 0 && clientquit != 0)
{
  status = 0;
  while (status == 0)
    status = read(clientSocket, buffer, sizeof(buffer));
  serverquit = strcmp(buffer, "q\n");

  if (serverquit != 0)
  {
    printf("S: %s", buffer);
    for (i = 0; i <= 255; i++) buffer[i] = 0;
    printf("C: ");
    fgets(buffer, 256, stdin);
    clientquit = strcmp(buffer, "q\n");
    write(clientSocket, buffer, strlen(buffer));
    for (i = 0; i <= 255; i++) buffer[i] = 0;
   }
 }
 printf("Goodbye\n");
 close(clientSocket);
 return 0;
 }

Μετά τις αλλαγές αυτές, μεταγλωττίζουμε πάλι τα προγράμματα client.c και server.c και τα τρέχουμε ξανά όπως προηγουμένως. Οι κώδικες του client και του server είναι σχεδόν ίδιοι με το προηγούμενο παράδειγμα. Υπάρχουν δύο βασικές αλλαγές. Η πρώτη είναι ο έλεγχος για το αν κάθε πλευρά έχει εισάγει το γράμμα ‘q’ για να παραιτηθεί ή όχι. Η δεύτερη είναι ο βρόγχος που χρησιμοποιείται και που περιμένει για ανταπόκριση από το άλλο τμήμα της αλληλοσύνδεσης. Η συνάρτηση read  επιστρέφει το πλήθος των χαρακτήρων  του μηνύματος που αποστέλλεται κάθε φορά. Αν το πλήθος των χαρακτήρων είναι μηδέν, τότε το άλλο τμήμα της αλληλοσύνδεσης έχει στείλει μήνυμα.
Ακολουθεί ένα παράδειγμα όπως αποτυπώνεται από τον server:
Connection request from dimitra
Send q to quit.
C: Hi server
S: Hi client
C: Bye server
S: Bye client
Goodbye
Ακολουθεί ένα παράδειγμα όπως αποτυπώνεται από τον client:
S: Hi, dimitra
S: Please start chat. Send q to quit.
C: Hi server
S: Hi client
C: Bye server
S: Bye client
C: q
Goodbye


Παράδειγμα 8

Το παράδειγμα που ακολουθεί θυμίζει τα δημοφιλή στα παιδιά αστεία της ακόλουθης μορφής:
     Server: "Knock knock!"
     Client: "Who's there?"
     Server: "Dexter."
     Client: "Dexter who?"
     Server: "Dexter halls with boughs of holly."
     Client: "Groan."
Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία KnockKnockClient.java και KnockKnockServer.java αντιστοίχως. Επιπλέον θα χρειαστεί και το πρόγραμμα KnockKnockProtocol.java, το οποίο αποτελεί το πρωτόκολλο επικοινωνίας μεταξύ client και server. Σ’ αυτό θα αναφερθούμε στην συνέχεια. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε και τα τρία προγράμματα με τον java compiler (δηλ. Prompt> javac KnockKnockClient.java και Prompt> javac KnockKnockServer.java και Prompt> javac KnockKnockProtocol.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java KnockKnockServer). Παρατηρούμε ότι ο Server τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java KnockKnockClient). Από την στιγμή που επιτυγχάνεται η σύνδεσή του, ακολουθείται η ακόλουθη διαδικασία:

Ο Server αρχίζει τον διάλογο με την φράση "Knock! Knock!" και περιμένει από τον Client να απαντήσει. Όταν αυτός απαντήσει, ο Server περνάει την απάντηση στο πρόγραμμα KnockKnockProtocol.java για να προσδιορίσει ποιο θα είναι το επόμενο κατάλληλο μήνυμα από μέρους του. Με λίγα λόγια, το πρόγραμμα KnockKnockProtocol.java περιέχει τους κανόνες που πρέπει να ακολουθήσουν τα δύο συμβαλλόμενα μέρη στον διάλογό τους. Αναγνωρίζει σε ποιο σημείο βρίσκεται κάθε στιγμή ο διάλογος και προσφέρει στον Server την κατάλληλη ανταπόκριση μετά την λήψη του μηνύματος του Client. Περιέχει δηλαδή το σύνολο των δυνατών ανταποκρίσεων και ελέγχει αν ο Client δίνει την κατάλληλη απάντηση ή όχι. Για παράδειγμα, δεν θα μπορούσε ο Client να απαντήσει με την φράση “Dexter who?”, όταν ο Server λέει “Knock, Knock!”. Αντίθετα, ως σωστή απάντηση θεωρείται η φράση “Who’s there?” . Αν όμως πράγματι ο Client δώσει αυτή τη λάθος απάντηση, τότε ο Server ενημερώνεται από το KnockKnockProtocol ότι πρέπει να ανταποδώσει με την απάντηση “Try again. Knock! Knock!”, πράγμα που κάνει..

Παρακάτω ακολουθεί ένας διάλογος, στον οποίο ο Client καταφέρνει να δίνει κάθε φορά την κατάλληλη απάντηση:

Server: Knock! Knock!
Client: Who's there?
Server: Turnip
Client: Turnip who?
Server: Turnip the heat, it's cold in here! Want another? (y/n)
Στο τέλος o Server ρωτάει τον Client, εάν θέλει να συνεχίσει με καινούργιο διάλογο. Εάν πληκτρολογήσει ‘y’, o Server ξεκινάει πάλι με την φράση “Knock, Knock!”. Εάν, όμως πληκτρολογήσει ‘n’, τότε ο Server χαιρετάει, λέγοντας “Bye” και διακόπτεται αυτόματα η επικοινωνία. Εάν τελικά πληκτρολογήσει ‘y’, ο επόμενος διάλογος στον οποίο ο Client καταφέρνει να δίνει κάθε φορά την κατάλληλη απάντηση θα είναι ο εξής:
Server: Knock! Knock!
Client: Who's there?
Server: Little Old Lady
Client: Little Old Lady Who?
Server  :I didn't know you could yodel! Want another? (y/n)
Τώρα, θα ακολουθήσει ένας διάλογος, στον οποίο ο Client δίνει κάποιες λανθασμένες απαντήσεις.
Server: Knock! Knock!
Client: Hello!
Server: You're supposed to say "Who's there?"!
Client: Who's there?
Server: Turnip
Client:  Okey   
Server: You're supposed to say "Turnip who?"!
Client: Turnip who?
Server: Turnip the heat, it's cold in here! Want another? (y/n)
Client: n
Server: Bye
Δηλαδή, κάθε φορά που ο client δίνει μία λανθασμένη απάντηση, το KnockKnockProtocol ενημερώνει τον Server ότι πρέπει να απαντήσει με μία φράση της μορφής “You're supposed to say ”……….”!

Στο παράδειγμα που ήδη αναλύσαμε, ο KnockKnockServer έχει σχεδιαστεί για να ακούει και να χειρίζεται τις αιτήσεις ενός μόνο KnockKnockClient. Αν, δηλαδή, ένας άλλος KnockKnockClient επιχειρήσει να ξεκινήσει τον διάλογο με τον ίδιο KnockKnockServer θα πρέπει να περιμένει το τέλος της σύνδεσης του προηγούμενου KnockKnockClient.

Παραλλαγές του παραδείγματος 8

Δυνατότητα διαλόγου μεταξύ ενός Server και Πολλών Clients.

Μία παραλλαγή της παραπάνω εφαρμογής έχει να κάνει με ένα Server, ο οποίος έχει τη δυνατότητα να επικοινωνεί και να διαλέγεται, όχι μόνο με έναν, αλλά με πολλούς Clients. Για τον σκοπό αυτό, το πρόγραμμα του Server έχει την μορφή του προγράμματος KKMultiServer.java. Το σημείο του προγράμματος που επιτρέπει την επικοινωνία και με άλλους Clients είναι το ακόλουθο:

 while (true) {
              accept a connection ;
              create a thread to deal with the client ;
          end while
Στην εφαρμογή αυτή απαραίτητη είναι η ύπαρξη του προγράμματος KKMultiServerThread.java. Μ’ αυτό δίνεται η δυνατότητα στον KKMultiServer να διατηρεί την επικοινωνία με τους άλλους Clients και να επιλέγει εκείνον, με τον οποίο πρέπει να διαλεχθεί.

Για να εκτελεστεί η εφαρμογή αυτή ακολουθούμε τα ακόλουθα βήματα:

Σώζουμε τα προγράμματα του KKMultiServer και του KKMultiServerThread, που ακολουθούν στην συνέχεια, ως αρχεία KKMultiServer.java και KKMultiServerThread.java αντιστοίχως. Ο κώδικας του Client είναι ο ίδιος με αυτόν που χρησιμοποιήσαμε προηγουμένως. Μετά την μεταγλώττιση των προγραμμάτων, τρέχουμε, πρώτα σε ένα παράθυρο το KKMultiServer.java, θέτοντας σε αναμονή τον KKMultiServer. Στη συνέχεια, ανοίγουμε νέα παράθυρα για κάθε νέο Client, που θέλει να ανοίξει διάλογο και τρέχουμε το πρόγραμμα KnockKnockClient.java σε καθένα από αυτά. Από εδώ και πέρα, ακολουθείται η διαδικασία που περιγράψαμε και στην περίπτωση με τον KnockKnockServer.

KnockKnockClient.java

import java.io.*;
import java.net.*;
public class KnockKnockClient {
    public static void main(String[] args) throws IOException {
        Socket kkSocket = null;
        PrintWriter out = null;
        BufferedReader in = null;
        try {
            kkSocket = new Socket("dimitra", 6789);
            out = new PrintWriter(kkSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: dimitra.");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to: dimitra.");
            System.exit(1);
        }
        BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
        String fromServer;
        String fromUser;
        while ((fromServer = in.readLine()) != null) {
            System.out.println("Server: " + fromServer);
            if (fromServer.equals("Bye."))
                break;
            fromUser = stdIn.readLine();
            if (fromUser != null) {
                System.out.println("Client: " + fromUser);
                out.println(fromUser);
            }
        }
        out.close();
        in.close();
        stdIn.close();
        kkSocket.close();
    }
}
KnockKnockServer.java
import java.net.*;
import java.io.*;
public class KnockKnockServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(6789);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 6789.");
            System.exit(1);
        }
        Socket clientSocket = null;
        try {
            clientSocket = serverSocket.accept();
        } catch (IOException e) {
            System.err.println("Accept failed.");
            System.exit(1);
        }
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                clientSocket.getInputStream()));
        String inputLine, outputLine;
        KnockKnockProtocol kkp = new KnockKnockProtocol();
        outputLine = kkp.processInput(null);
        out.println(outputLine);
        while ((inputLine = in.readLine()) != null) {
             outputLine = kkp.processInput(inputLine);
             out.println(outputLine);
             if (outputLine.equals("Bye."))
                break;
        }
        out.close();
        in.close();
        clientSocket.close();
        serverSocket.close();
    }
}
KnockKnockProtocol.java
import java.net.*;
import java.io.*;
public class KnockKnockProtocol {
    private static final int WAITING = 0;
    private static final int SENTKNOCKKNOCK = 1;
    private static final int SENTCLUE = 2;
    private static final int ANOTHER = 3;
    private static final int NUMJOKES = 5;
    private int state = WAITING;
    private int currentJoke = 0;
    private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who", "Who" };
    private String[] answers = { "Turnip the heat, it's cold in here!",
                                 "I didn't know you could yodel!",
                                 "Bless you!",
                                 "Is there an owl in here?",
                                 "Is there an echo in here?" };
    public String processInput(String theInput) {
        String theOutput = null;
        if (state == WAITING) {
            theOutput = "Knock! Knock!";
            state = SENTKNOCKKNOCK;
        } else if (state == SENTKNOCKKNOCK) {
            if (theInput.equalsIgnoreCase("Who's there?")) {
                theOutput = clues[currentJoke];
                state = SENTCLUE;
            } else {
                theOutput = "You're supposed to say \"Who's there?\"! " +
                            "Try again. Knock! Knock!";
            }
        } else if (state == SENTCLUE) {
            if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) {
                theOutput = answers[currentJoke] + " Want another? (y/n)";
                state = ANOTHER;
            } else {
                theOutput = "You're supposed to say \"" + 
                            clues[currentJoke] + 
                            " who?\"" + 
                            "! Try again. Knock! Knock!";
                state = SENTKNOCKKNOCK;
            }
        } else if (state == ANOTHER) {
            if (theInput.equalsIgnoreCase("y")) {
                theOutput = "Knock! Knock!";
                if (currentJoke == (NUMJOKES - 1))
                    currentJoke = 0;
                else
                    currentJoke++;
                state = SENTKNOCKKNOCK;
            } else {
                theOutput = "Bye.";
                state = WAITING;
            }
        }
        return theOutput;
    }
}
KKMultiServer.java
import java.net.*;
import java.io.*;
public class KKMultiServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        boolean listening = true;
        try {
            serverSocket = new ServerSocket(6789);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 6789.");
            System.exit(-1);
        }
        while (listening)
            new KKMultiServerThread(serverSocket.accept()).start();
        serverSocket.close();
    }
}
KKMultiServerTread.java
import java.net.*;
import java.io.*;
public class KKMultiServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        boolean listening = true;
        try {
            serverSocket = new ServerSocket(6789);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 6789.");
            System.exit(-1);
        }
        while (listening)
            new KKMultiServerThread(serverSocket.accept()).start();
        serverSocket.close();
    }
}
Παράδειγμα 9

Το προγράμματα του clientκαι του server που ακολουθούν επεξηγούν πώς χρησιμοποιείται η ρουτίνα gettransient. Ο client κάνει μία RPC κλήση προς τον server, δίνοντάς του ένα προσωρινό πρόγραμμα αριθμών. Μετά περιμένει για να λάβει μία απάντηση από τον server σ’ αυτό το πρόγραμμα Ο server καταγράφει το πρόγραμμα EXAMPLEPROG, ώστε να μπορέσει να λάβει την RPC κλήση, ενημερώνοντάς το για το επιστρεφόμενο πρόγραμμα αριθμών. Σε κάποιο τυχαίο χρονικό διάστημα (σ’ αυτό το παράδειγμα, λαμβάνοντας ένα ALRM σήμα), στέλνει μία RPC απάντηση, χρησιμοποιώντας το πρόγραμμα αριθμών που έλαβε νωρίτερα.

Κώδικας Client παραδείγματος 9

/* client    */
#include  
#include  
int callback,();
char hostname[256];
main() 
{
    int x, ans, s;
    SVCXPRT *xprt;
    gethostname(hostname, sizeof(hostname));
    s = RPC_ANYSOCK;
    x = gettransient(IPPROTO_UDP, 1, &s);
    fprintf(stderr, "client gets prognum %d\n", x);
    if ((xprt = svcudp_create(s)) == NULL) 
    {
        fprintf(stderr, "rpc_server: svcudp_create\n");
        exit(1);
    }
    /* protocol is 0 - gettransient does registering */ 
    (void)svc_register(xprt, x, 1, callback,, 0);
    ans = callrpc(hostname, EXAMPLEPROG, EXAMPLEVERS, 
                  EXAMPLEPROC_callback, xdr_int, &x, xdr_void, 0);
    if ((enum clnt_stat) ans != RPC_SUCCESS) 
    {
        fprintf(stderr, "call: ");
        clnt_perrno(ans);
        fprintf(stderr, "\n");
    }
    svc_run();
    fprintf(stderr, "error: svc_run shouldn't return\n");
}
callback,(rqstp, transp) 
    register struct svc_req *rqstp;
    register SVCXPRT *transp;
{
    switch (rqstp->rq_proc) 
    {
        case 0: 
            if (!svc_sendreply(transp, xdr_void, 0)) 
            {
                fprintf(stderr, "err: exampleprog\n ");
                return (1);
            }
            return (0);
        case 1: 
            if (!svc_getargs(transp, xdr_void, 0)) 
            {
                svcerr_decode(transp);
                return (1);
            }
            fprintf(stderr, "client got callback,\n");
            if (!svc_sendreply(transp, xdr_void, 0)) 
            {
                fprintf(stderr, "err: exampleprog") ;
                return (1);
            }
    }
}
Κώδικας Server παραδείγματος 9
/* server     */ 
#include  
#include  
#include  
char *getnewprog();
char hostname[256];
int docallback,();
int pnum;             /* program number for callback, routine */ 
main() 
{
    gethostname(hostname, sizeof(hostname));
    registerrpc(EXAMPLEPROG, EXAMPLEVERS, 
                EXAMPLEPROC_callback,, getnewprog, xdr_int, xdr_void);
    fprintf(stderr, "server going into svc_run\n");
    signal(SIGALRM, docallback,);
    alarm(10);
    svc_run();
    fprintf(stderr, "error: svc_run shouldn't return\n");
}
char * 
getnewprog(pnump) 
    char *pnump;
{
    pnum = *(int *)pnump;
    return NULL;
}
docallback,() 
{
    int ans;
    ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0, xdr_void, 0);
    if (ans != 0) 
    {
         fprintf(stderr, "server: ");
         clnt_perrno(ans);
         fprintf(stderr, "\n");
    }
}


Παράδειγμα 10

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία QuoteClient.java και QuoteServer.java αντιστοίχως. Επίσης σώζουμε το αρχείο QuoteServerThread.java, καθώς και το αρχείο one-liners.txt, που είναι ένα αρχείο κειμένου. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ. Prompt> javac :QuoteClient.java και Prompt> javac QuoteServer.java και Prompt> javac QuoteServerThread.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java QuoteServer). Παρατηρούμε ότι ο Server δημιουργεί μια σύνδεση, δίνοντας στον client το port, το οποίο θα χρησιμοποιήσει για να μπορέσει να λάβει το περιεχόμενο του κειμένου one-lines.txt. Τώρα δίνει δίνει ένα χρονικό διάστημα στον Client για να συνδεθεί. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java Client <hostname> [<port>], όπου <hostname> βάζουμε την ονομασία του Client, π.χ. Dimitra, και όπου <port> βάζουμε το port που δίνει ο Server, π.χ. 1081). Όταν αυτός συνδεθεί στο συγκεκριμένο port, λαμβάνει το περιεχόμενο του κειμένου και τερματίζεται η σύνδεση. Το αποτέλεσμα όλης αυτής της διαδικασίας είναι η ακόλουθη:

Server: Server Socket 1081
Client: 
Quote of the moment:1  Life is wonderful. Without it we'd all be dead.
Quote of the moment: 2  Daddy, why doesn't this magnet pick up this floppy disk?
Quote of the moment: 3  Give me ambiguity or give me something else.
Quote of the moment: 4  I.R.S.: We've got what it takes to take what you've got!
Quote of the moment: 5  We are born naked, wet and hungry. Then things get worse.
Quote of the moment: 6  Make it idiot proof and someone will make a better idiot.
Quote of the moment: 7  He who laughs last thinks slowest!
Quote of the moment: 8  Always remember you're unique, just like everyone else.
Quote of the moment: 9  "More hay, Trigger?" "No thanks, Roy, I'm stuffed!"
Quote of the moment: 10 A flashlight is a case for holding dead batteries.
Quote of the moment: 11 Lottery: A tax on people who are bad at math.
Quote of the moment: 12 Error, no keyboard - press F1 to continue.
Quote of the moment: 13 There's too much blood in my caffeine system.
Quote of the moment: 14 Artificial Intelligence usually beats real stupidity.
Quote of the moment: 15 Hard work has a future payoff. Laziness pays off now.
Quote of the moment: 16 "Very funny, Scotty. Now beam down my clothes."
Quote of the moment: 17 Puritanism: The haunting fear that someone, somewhere may be happy.
Quote of the moment: 18 Consciousness: that annoying time between naps.
Quote of the moment: 19 Don't take life too seriously, you won't get out alive.
Quote of the moment: 20 I don't suffer from insanity. I enjoy every minute of it.
Quote of the moment: No more quotes. Goodbye
QuoteClient.java.
///////////// Fix comments!!! 
// QuoteClient -- repeatedly request quotes from the "quote server".
// If quote server doesn't reply within 1 millisecond, print error
// and try again. Quit after 10 errors.
//
// This example was modified from the one appearing at:
//
//     http://www.seas.upenn.edu/~ross/book/apps/sockettcp.htm
/////////////////////////////////////////////////////////////////////
import java.io.*;
import java.net.*;
import java.util.*;
public class QuoteClient 
{
  public static void main(String[] args) throws IOException 
  {
   // First, get server's hostname and port number from command line:
    if (args.length != 2) 
    {
      System.out.println("Usage: java QuoteClient  ");
      return;
    }
    InetAddress address = InetAddress.getByName(args[0]);
    int port = Integer.parseInt(args[1]);
  // Get a datagram socket for two-way communication:
    DatagramSocket socket = new DatagramSocket();
   // Set the "timeout" for waiting to read from the socket:
    socket.setSoTimeout(1); // 1 millisecond timeout
    int errorCount = 0; // We'll count the number of errors with this
   // Client loops "forever" or until twenty errors occur:
    while(true)
    {
     // Send request (we don't care about the contents of the packet):
      try
      {
////////// Changes should occur inside this loop:
//////////    random client delay; 
//////////    alternating "ack" and "nak" messages sent to server;
//////////    random send failure
        byte[] buf = new byte[256]; // empty byte array of size 256
        DatagramPacket packet = 
             new DatagramPacket(buf, buf.length, address, port);
        socket.send(packet);
       // Get response from server
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
       // Display response:
        String received = new String(packet.getData());
        System.out.println("Quote of the Moment: " + received);
///////// Should not be necessary to change things beyond this point
       } catch(Exception e) 
         { 
           System.out.println("   Client error: " + e); 
          // Quit if twenty errors:
           if (++errorCount >= 20)
           {
             System.out.println("   Too many errors; halting client");
             break; // break out of while loop
           }
         }
    } // end while
    socket.close();  // close the socket before exiting
  } // end main
} // end class QuoteClient
QuoteServer.java
//////////// Nothing in here has to be changed
// QuoteServer -- this class 'drives' the QuoteThreadServer, where
//   all the work gets done.
//
// This example was modified from the one appearing at:
//
//     http://www.seas.upenn.edu/~ross/book/apps/sockettcp.htm
/////////////////////////////////////////////////////////////////////
public class QuoteServer 
{
  public static void main(String[] args) throws java.io.IOException 
  {
    new QuoteServerThread().start();
  }
}
QuoteServerThread.java
///////////// Fix comments!!!!
// QuoteServerThread -- this thread class runs the quotation server.
//
// Open a file of quotations ("one-liners.txt").
// When a datagram packet is received from a client, send the
// next line of this file back to the client via another datagram.
//
// For testing purposes, generate a random delay between 0 and 10 ms
// before reading from socket (so client will sometimes "time out").
//
// This example was modified from the one appearing at:
//
//     http://www.seas.upenn.edu/~ross/book/apps/sockettcp.htm
/////////////////////////////////////////////////////////////////////
import java.io.*;
import java.net.*;
import java.util.*;
public class QuoteServerThread extends Thread 
{
  protected DatagramSocket socket = null; // sending and receiving done here
  protected BufferedReader in = null;     // use to read from one-liners file
  protected boolean moreQuotes = true;    // false when we hit end of " " "
// Zero-parameter constructor for objects of class QuoteServerThread:
  public QuoteServerThread() throws IOException 
  {
      this("QuoteServerThread");
  }
// One-parameter constructor for objects of class QuoteServerThread:
  public QuoteServerThread(String name) throws IOException 
  {
    super(name);  // Call the Thread constructor
    socket = new DatagramSocket();  // initialize socket
////////////// You may want to alter the initial time out (to
////////////// allow enough time to start up the client!):
    socket.setSoTimeout(10000);  // quit if no requests in 10 seconds
   // Print out the server's port number:
    System.out.println("Server socket = " + socket.getLocalPort());
   // Open the "one-liners" file; check for file errors:
    try 
    {
      in = new BufferedReader(new FileReader("one-liners.txt"));
    } catch (FileNotFoundException e) 
      {
        System.err.println("Could not open quote file.");
        throw(e); // throw this exception up to next level
      }
  } // end QuoteServerThread
  public void run() 
  {
   // As long as we haven't hit end of the one-liners file:
    int errorCount = 0;
    while (moreQuotes) 
    {
     // Generate a random delay (for testing purposes):
      try 
      {
        sleep((int)(Math.random()*11)); // delay between 0 and 10 ms
 /////////// Changes go inside this loop:
///////////      inspect received packet to see if it holds
///////////          "ack" or "nak"; print message indicating which.
///////////      alter timeout to something smaller (e.g., 1ms) 
///////////          once client has successfully sent a packet
///////////      randomly fail to send packets to client
        byte[] buf = new byte[256];  // received datagram goes here
       // Receive request
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
       // Create response:
        String dString = getNextQuote();
        buf = dString.getBytes();
       // Send the response to the client at "address" and "port"
        InetAddress address = packet.getAddress();
        int port = packet.getPort();
        packet = new DatagramPacket(buf, buf.length, address, port);
        socket.send(packet);
/////////// It should not be necessary to change anything beyond here
      } catch (IOException e) 
        {
         // Most likely this will happen if "moreQuotes" closed the file:
          System.out.println("   Server error: " + e);
          if (++errorCount >= 20)
          { moreQuotes = false;}
        }
        catch (Exception e) 
        {
         // Most likely this will happen if server "times out" while
         // waiting for message on socket:
          System.out.println("   Server error: " + e);
          if (++errorCount >= 20)
          { moreQuotes = false;}
        }
    } // end while
    socket.close();
  } // end run
  protected String getNextQuote() 
  {
    String returnValue = null;  // a quote or else a status message
    try 
    {
     // Try to read next quote from the file:
      if ((returnValue = in.readLine()) == null) 
      {
        in.close();
         moreQuotes = false;
        returnValue = "No more quotes. Goodbye.";
      }
    } catch (IOException e) 
      {
        returnValue = "IOException occurred in server.";
      }
    return returnValue;
  } // end getNextQuote
} // end class QuoteServerThread
One-liners.txt
1  Life is wonderful. Without it we'd all be dead.
2  Daddy, why doesn't this magnet pick up this floppy disk?
3  Give me ambiguity or give me something else.
4  I.R.S.: We've got what it takes to take what you've got!
5  We are born naked, wet and hungry. Then things get worse.
6  Make it idiot proof and someone will make a better idiot.
7  He who laughs last thinks slowest!
8  Always remember you're unique, just like everyone else.
9  "More hay, Trigger?" "No thanks, Roy, I'm stuffed!"
10 A flashlight is a case for holding dead batteries.
11 Lottery: A tax on people who are bad at math.
12 Error, no keyboard - press F1 to continue.
13 There's too much blood in my caffeine system.
14 Artificial Intelligence usually beats real stupidity.
15 Hard work has a future payoff. Laziness pays off now.
16 "Very funny, Scotty. Now beam down my clothes."
17 Puritanism: The haunting fear that someone, somewhere may be happy.
18 Consciousness: that annoying time between naps.
19 Don't take life too seriously, you won't get out alive.
20 I don't suffer from insanity. I enjoy every minute of it.
 

 
 
 

Παράδειγμα 11

Για να τρέξει το παράδειγμα, παίρνουμε τον κώδικα του client και τον κώδικα του server, όπως αυτοί φαίνονται παρακάτω, σώζοντας τους ως αρχεία SimpleDatagramClient.java και SimpleDatagramServer.java αντιστοίχως. Μέσω του JDK1.2 (Java Development Kit) μεταγλωττίζουμε τα προγράμματα με τον java compiler (δηλ. Prompt> javac SimpleDatagramClient.java και Prompt> javac SimpleDatagramServer.java). Από την στιγμή που διαπιστώνουμε ότι δεν υπάρχουν λάθη, τρέχουμε τα προγράμματα ως εξής:

Πρώτα τρέχουμε σε ένα παράθυρο την εφαρμογή του Server (δηλ. Prompt> java SimpleDatagramServer). Παρατηρούμε ότι ο Server τίθεται σε αναμονή περιμένοντας την αίτηση του Client. Μετά, ανοίγουμε ένα άλλο παράθυρο για να τρέξουμε το πρόγραμμα του Client (δηλ. Prompt> java SimpleDatagramClient). Ο Client στέλνει ένα γραπτό μήνυμα προς τον Server, ο τελευταίος το λαμβάνει, το μετατρέπει σε αλφαριθμητικό και το επιστρέφει πίσω. Έτσι στο παράθυρο του Client εμφανίζεται ξανά το αρχικό του μήνυμα. Ένα παράδειγμα αυτής της διαδικασίας στο παράθυρο του Client είναι το ακόλουθο:

Client:  > Hello
Client: Hello
Client: > Who are you?
Client: Who are you?
Η διαδικασία αποστολής γραπτών μηνυμάτων από τον Client ολοκληρώνεται όταν πληκτρολογήσει δύο φορές συνεχόμενα το πλήκτρο [ENTER]. Έτσι τελειώνει η εφαρμογή.

Κώδικας Client παραδείγματος 11

import java.io.*;
     import java.net.*;
     public class SimpleDatagramClient
     {
         private DatagramSocket socket = null;
         private DatagramPacket recvPacket, sendPacket;
         private int hostPort;
         public static void main(String[] args)
         {
             DatagramSocket socket = null;
             DatagramPacket recvPacket, sendPacket;
             try
             {
                 socket = new DatagramSocket();
                 InetAddress hostAddress = InetAddress.getByName("localhost");
                 DataInputStream userData = new DataInputStream( System.in );
                 while (socket != null)
                 {
                     String userString = userData.readLine();
                     if (userString == null || userString.equals(""))
                         return;
                     byte sendbuf[] = new byte[ userString.length() ];
                     userString.getBytes(0, userString.length(), sendbuf, 0);
                     sendPacket = new DatagramPacket(
                         sendbuf, sendbuf.length, hostAddress, 4545 );
                     socket.send( sendPacket );
                     recvPacket= new DatagramPacket(new byte[512], 512);
                     socket.receive(recvPacket);
                     System.out.write(recvPacket.getData(), 0,
                         recvPacket.getLength());
                     System.out.print("\n");
                 }
             }
             catch (SocketException se)
             {
                 System.out.println("Error in SimpleDatagramClient: " + se);
             }
             catch (IOException ioe)
             {
                 System.out.println("Error in SimpleDatagramClient: " + ioe);
             }
         }
     }


Κώδικάς Server παραδείγματος 11

import java.io.*;
     import java.net.*;
     public class SimpleDatagramServer
     {
         public static void main(String[] args)
         {
             DatagramSocket socket = null;
             DatagramPacket recvPacket, sendPacket;
             try
             {
                 socket = new DatagramSocket(4545);
                 while (socket != null)
                 {
                     recvPacket= new DatagramPacket(new byte[512], 512);
                     socket.receive(recvPacket);
                     sendPacket = new DatagramPacket(
                         recvPacket.getData(), recvPacket.getLength(),
                         recvPacket.getAddress(), recvPacket.getPort() );
                     socket.send( sendPacket );
                 }
             }
             catch (SocketException se)
             {
                 System.out.println("Error in SimpleDatagramServer: " + se);
             }
             catch (IOException ioe)
             {
                 System.out.println("Error in SimpleDatagramServer: " + ioe);
             }
         }
     }