LEZIONE del 4 Marzo

Nella lezione precedente è stato presentato un esempio di server iterativo costituito da una routine principale main che rappresenta l'ossatura del server e la subroutine guess_action, da realizzarsi a parte nel file action.c, che ne implementa la specifica funzionalità. Per effettuare la compilazione bisogna utilizzare il comando

gcc -o server -lnsl server.c action.c

dove action.c è il file che contiene il codice della subroutine. Per l'utilizzo del server procedere nel seguente modo.

A questo punto il server produrrà un "banner" di avvenuta connessione permettendo all'utente TELNET di dialogare con la routine eseguita remotamente dal server.

Implementazione della Routine eseguita dal Server

L'implementazione della routine eseguita dal server si basa sul tipo particolare di protocollo che realizza lo scambio di informazioni con il client. Nel caso specifico, il protocollo utilizzato è uno dei protocolli applicativi per internet più semplici e più diffusi, e prende il nome di protocollo TELNET. In esso il flusso dei dati è ottenuto con uno stream bidirezionale che simula il funzionamento di un terminale seriale alfanumerico ed è immediatamente implementabile sul protocollo TCP, ottenuto con le socket stream.

Se si vuole che una routine possa utilizzare direttamente questo tipo di protocollo è sufficiente definire come parametri di comunicazione le due variabili intere in e out, la prima per sostituire lo standard input ":stdin" e la seconda per lo standard output "out" ed applicare su di esse le procedure

Si tenga presente che tutte le operazioni di lettura e/o scrittura devono essere realizzate esplicitamente su buffer che non siano quelli definiti implicitamente per lo standard input e output, altrimenti i messaggi non potranno essere inoltrati dalla socket.

LA SUBROUTINE GUESS_ACTION

Il primo esempio di routine caratterizza un semplice programma che copia la riga corrente dell'input (in forma di stringa) nella riga di output, per un numero massimo MAXCOUNT di copie, a meno che non vengano introdotti i comandi

Di seguito il codice C.

#define    MAXLEN    80
#define    MAXCOUNT  10
#define    TRUE      1

void guess_action(int in, int out)
{
  char  cmd[MAXLEN], msg[MAXLEN];
  int   count = 0;

  sprintf(msg, "benvenuto in questo server\n\n");
  write(out, msg, strlen(msg));

  while (TRUE) {
    read(in, cmd, MAXLEN);
    if (cmd[0] == 'C') count = count - 5;
    else if (cmd[0] == 'Q') count = MAXCOUNT - 1;
    else { strcpy(msg, cmd);
           write(out, msg, strlen(msg));
         }
    printf("count %d\n", count);
    count++;
    if (count == MAXCOUNT) exit(0);
  }
}

Il secondo esempio, invece, mostra il caso di un programma che simula il gioco di indovinare una parola proposta dal server, a partire da un database di parole predefinito e selezionata casualmente, mediante una serie ripetuta di proposte del cliente, fatte digitando un carattere da tastiera. Ovviamente, è consentito un limite massimo di errori.

#include <errno.h>

char *word[] = {
#include "words"
};

#define NUM_OF_WORDS (sizeof(word)/sizeof(word[0]))
#define MAXLEN   80
#define MAXTOSS  6
#define NULL     0
  
void guess_action(int in, int out)
{
  char *whole,
        part[MAXLEN],     /* parola parziale */
        guess[MAXLEN],
        msg[MAXLEN];

  int toss = MAXTOSS;
  int game = 'I';         /* incomplete */

  int i, OK, wsize;
  char hostname[MAXLEN];
  unsigned seed;

  gethostname(hostname, MAXLEN);
  sprintf(msg, "Looking for a word on host %s:\n\n", hostname);
  write(out,msg,strlen(msg));

          /* scegli una parola a caso */

  seed = time(NULL);
  srand(seed);
  whole = word[rand() % NUM_OF_WORDS];
  wsize = strlen(whole);

  printf("\n\n guess server chose word: %s \n\n", whole);

          /* all'inizio non ci sono lettere indovinate */

  for ( i=0; i < wsize; i++) part[i] = '-';
  part[wsize]='\0';


  sprintf(msg, " %s  %d\n", part, toss);
  write(out, msg, strlen(msg));

          /* leggi la proposta del cliente */

  while (game == 'I') {
    while (read(in, guess, MAXLEN) < 0) {
      if (errno != EINTR) {
	perror("while reading");
	exit(4);                        /* se altro errore termina */
      }
      printf("Restarting read\n");      /* se interrotto da una signal */
    }

         /* aggiorna la parola parziale */

    OK = 0;
    for (i=0; i ;lt; wsize; i++) {
      if (guess[0] == whole[i]) {
	OK = 1;
	part[i] = whole[i];
      }
    }

    if (! OK) toss--;
    if (strcmp(whole, part) == 0) game = 'W';	  /* il cliente vince */
      else if (toss == 0) {
             game = 'L';	                  /* il cliente perde */
             strcpy(part, whole);                 /* mostra la soluzione */
            }
    sprintf(msg, " %s  %d\n", part, toss);
    write(out, msg, strlen(msg));
  }
}
In quest'ultimo esempio proposto, il file testuale contenente le parole incognite deve essere realizzato con una lista di stringhe che, per questo devono essere quotate, separate l'una dall'altra dalla virgola. Si consiglia, per semplicità, di utilizzare una riga diversa per ciascuna parola.