Rapport de synth�se UNIX TP

Banque : TP Unix - Janvier 2002 (c) 2002 Lai Yuk Ngan, R�mi Peyronnet

Choix techniques
Autres difficult�s
Am�liorations
Commentaires personnels
RFC
Le code

I) Les choix techniques

I.a) UDP : conseill� dans le sujet

I.b) Informations en m�moire

Nous avons d�cid� de stocker les informations sur les comptes et les ch�ques dans un tableau en m�moire pour les raisons suivantes :

I.c) Le probl�me d�attente : La solution d�une Banque "stateless"

Probl�me :

On a surtout connu beaucoup de probl�mes dans la conception des transactions qui exigent l�attente des r�ponses. Par exemple, lorsqu�un serveur de ventes demande l�encaissement d�un ch�que �mis par Banque B aupr�s de Banque A, Banque A est oblig�e d�attendre un message de confirmation de la part de Banque B avant de cr�diter le serveur de ventes. En attendant pendant une longue dur�e, ses activit�s risquent d��tre paralys�es.

Solution :

Lorsque l'on a besoin d'une r�ponse, on a �lud� le probl�me en se pla�ant dans un fonctionnement "stateless", et donc en n'ayant pas besoin de retenir quelle r�ponse On attend : tous les �l�ments n�cessaires seront contenus dans la r�ponse. Dans le cas contraire, il aurait fallu d�finir une liste chain�e de r�ponses en attente, ou d�finir un thread diff�rent (avec �ventuellement une connexion TCP avec l'autre banque),...

Pour r�soudre le probl�me de traitement des ch�ques :

Si le ch�que n'est pas dans notre banque, nous envoyons un message � l'autre banque, puis nous continuons comme si de rien n'�tait. La banque se contente donc de recevoir des messages, d'y apporter une r�ponse imm�diate, et de continuer.

II) Autres difficult�s rencontr�es

II.a) SOCKETS

Au d�but, nous avons eu du mal � comprendre comment utiliser les fonctions de cr�ation de sockets qui sont d�finies dans le poly, pages 34 et 35, surtout avec les fonctions strtok( ) et gethostbyname(). Au d�but, ce manque de familiarit� avec certaines fonctions, les types des param�tres, et l�omission de certaines biblioth�ques ont entra�n� beaucoup d�erreurs de compilation.

II.b) CONCEPTION

Nous avons mis beaucoup de temps dans la conception des transactions qui exigent l�attente des r�ponses (notamment pour les fonctions de virement et d�encaissement des ch�ques) .

Exemple : on a besoin pour les ch�ques / virements hors de notre banque d'interroger l'autre banque et d'attendre la r�ponse. Solution "stateless"(voir ci-dessus).

Envoi du relev� R# au client : on a besoin de stocker l'adresse du client, ce qui nous a pos� des probl�mes car le client n�a plus de connexion directe avec la banque lorsque le cheque est encaiss�.Solution : garder en m�moire la derni�re adresse connue du client.

II.c) VERIFICATION : Gestion d�erreurs dans la fonction � Virement �

On aurait pu �laborer la fonction � virement � en exigeant une confirmation d�un virement r�ussi (que lenum�ro de compte � cr�diter est valable) de la part de l�autre banque avant de d�biter le compte �metteur.Faute de temps, nous avons d�cid� de faire la supposition que les banques pourraient �ventuellement garder un � log � de toutes les transactions, qui permettrait la rectification de toutes erreurs. La v�rification sera une am�lioration importante.

II.d) SECURITE

Comment v�rifier si toutes les banques sur le port 3000 sont en fait des banques � l�gitimes � ? Il y a �videmment un probl�me de s�curit� (une banque fictive, par exemple), et ce sera une am�lioration d�impl�menter une proc�dure de v�rification des banques.

III) Les am�liorations

C'est en fait tr�s simple : il suffit d'analyser le fichier de log, puis de "dumper" le contenu des tables. Le contenu des tables remplace alors int�gralement tous les logs pr�c�dents.

IV) Commentaires personnels

Le TP est tr�s long pour le nombre de s�ances qu�on a eu, et le TP exige d�aborder beaucoup de probl�mes de conception (ex. La gestion des listes de donn�es) qui ne sont pas directement lies � la compr�hension du fonctionnement des sockets/des communications. Un TP plus court et focalis� sera appr�ci�.

Plus d�explication sur l�utilisation de nouvelles fonctions de sockets pendant le cours sera appr�ci�.

Il est possible de r�aliser le TP sans passer par la cr�ation de processus, ni de communication interprocessus.

V) RFC Utilis�e

[*] d�signe les messages modifi�s par rapport � la RFC du sujet.

 [*] d�signe les messages modifi�s par rapport � la RFC du sujet.

 Messages trait�s : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

 C#nom#mdp                               // Cr�ation d'un compte
 D#nom#somme                             // D�pot sur un compte
 I#nom#mdp                               // Demande du solde du compte
 H#nom#mdp#somme                         // Demande d'�mission d'un ch�que
 P#nochq#nomclient#nomserver#mdpserver   // Demande d'encaissement (cheque 1)
 U#nochq#nomclient#nomserver             // Interrogation d'une banque (cheque 2) [*]
 A#nochq#montant#nomserver#nomclient     // Acceptation du cheque (cheque 3) [*]
 V#banquedest#nomdest#nom#mdp#montant    // Virement (! banquedest = numero !)
 M#nom#mdp                               // Mise � jour de l'adresse du client [*]
 d                                       // Dump [*]

 R�ponses : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

 T#nomdest#somme                         // Transmission interbancaire d'un virement [*]
 Q#nochq                                 // Cheque : format %04%06 idbanque idcheque
 R#nomserver#somme                       // Relev� de paiment d'un cheque
 K#nomemetteur#nochq#montant#OK          // Encaisssment ch�que (montant=0 <=> NOK)
 U#nochq#nomserveur#nomclient            // Interbancaire - cf envoy�s [*]
 A#nochq#montant#nomserveur#nomclient    // Interbancaire - cf envoy�s [*]

 Erreurs / OK : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

 OK
 E#200#Banque indisponible
 E#201#Compte non trouv� ou somme n�gative
 E#202#Mot de passe invalide ou compte non trouv�
 E#203#Nom ou mot de passe vide
 E#204#Compte existant
 E#205#Somme n�gative ou nulle
 E#206#Banque non trouv�e

Le code

server.c : Le code du serveur
client.c : Un client pour envoyer des infos DEBUG
Makefile : Le Makefile
Fichiers d'exemple
Quelques scripts utiles

Server.c

/** Banque : TP Unix - Janvier 2002 (c) 2002 Judy Ngan, R�mi Peyronnet

Bref rapport cot� programmation : 

I) Les choix techniques 

I.a) UDP : conseill� dans le sujet

I.b) Informations en m�moire
 - �crire nos structures sur le disque revient en fait � gerer une mini base de donn�es, 
     ce n'est pas le but du TP.
 - beaucoup plus simple dans une premi�re approche,
 - avons cherch� � �viter les listes cha�n�es,
 - le syst�me adopt� (fichier de log r�analys�) r�pond parfaitement aux besoins.
 
I.c) Banque "stateless"

 Lorsque l'on a besoin d'une r�ponse, on a �lud� le probl�me en se pla�ant dans un 
  fonctionnement "stateless", et donc en n'ayant pas besoin de retenir quelle r�ponse 
  on attend : tous les �l�ments n�cessaires seront contenus dans la r�ponse. 
  Dans le cas contraire, il aurait fallu d�finir une liste chain�e de r�ponses en 
  attente, ou d�finir un thread diff�rent (avec eventuellement une connexion TCP 
  avec l'autre banque),... Un bon exemple de ce syst�me est le traitement des ch�ques. 
  Si le ch�que n'est pas dans notre banque nous envoyons un message � l'autre banque, 
  puis nous continuons comme si de rien n'�tait.

 La banque se contente donc de recevoir des messages, d'y apporter une r�ponse imm�diate, 
  et de continuer.

II) Difficult�s rencontr�es
 
 <bcp de problemes de compilation : a inclure>
 
 + besoin pour les cheques / virements hors de notre banque d'interroger l'autre banque et d'attendre la r�ponse. solution "stateless".
 + envoi du relev� R# au client : besoin de stoquer l'adresse du client.

III) Les am�liorations

 * Faire une petite fonction pour compacter le fichier de log. 
     C'est en fait tr�s simple : il suffit d'analyser le fichier de log, puis de "dumper" 
     le contenu des tables. Le contenu des tables remplace alors int�gralement tous les 
     logs pr�c�dents.
 * Inscription dynamiques de banques.
 * V�rifications lors des virements.
 * Travailler un peu l'aspect s�curit�...
 * ...

IV) Critiques du sujet


V) RFC Utilis�e

[*] d�signe les messages modifi�s par rapport � la RFC du sujet.

Messages trait�s : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

C#nom#mdp                               // Cr�ation d'un compte
D#nom#somme                             // D�pot sur un compte
I#nom#mdp                               // Demande du solde du compte
H#nom#mdp#somme                         // Demande d'�mission d'un ch�que
P#nochq#nomclient#nomserver#mdpserver   // Demande d'encaissement (cheque 1)
U#nochq#nomclient#nomserver             // Interrogation d'une banque (cheque 2) [*]
A#nochq#montant#nomserver#nomclient     // Acceptation du cheque (cheque 3) [*]
V#banquedest#nomdest#nom#mdp#montant    // Virement (! banquedest = numero !)
M#nom#mdp                               // Mise � jour de l'adresse du client [*]
d                                       // Dump [*]

R�ponses : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

T#nomdest#somme                         // Transmission interbancaire d'un virement [*]
Q#nochq                                 // Cheque : format %04%06 idbanque idcheque
R#nomserver#somme                       // Relev� de paiment d'un cheque
K#nomemetteur#nochq#montant#OK          // Encaisssment ch�que (montant=0 <=> NOK)
U#nochq#nomserveur#nomclient            // Interbancaire - cf envoy�s [*]
A#nochq#montant#nomserveur#nomclient    // Interbancaire - cf envoy�s [*]

Erreurs / OK : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OK
E#200#Banque indisponible
E#201#Compte non trouv� ou somme n�gative
E#202#Mot de passe invalide ou compte non trouv�
E#203#Nom ou mot de passe vide
E#204#Compte existant
E#205#Somme n�gative ou nulle
E#206#Banque non trouv�e

********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>

#define PORT 3000                       // Num of the port used
#define NUM_BANQUES 10                  // Max number of banks
#define NUM_BANQUE 42                   // Default id of the bank
#define NOM_BANQUE "TestBank"           // Default name of the bank

#define COMPTES_MIN 5                   // Min number of accounts
#define COMPTES_INC 10                  // Increase the array of accounts by ...
#define CHEQUES_MIN 10                  // Min number of cheques
#define CHEQUES_INC 10                  // Increase the array of cheques by ...

#define ERR_ACCOUNT_NOT_FOUND -2        // Account not found
#define ERR_BAD_PASSWORD -3             // Bad password
#define ERR_BANK_NOT_FOUND -4           // Bank not found


// Global variables

int sd;                                 // Socket descriptor
struct sockaddr_in address;             // Address (server)
struct sockaddr_in addr_client;         // Address (client)
socklen_t size;                         // Size of address

int numero_banque = NUM_BANQUE;         // Id of the current bank
/** Is the bank working ?
 * This var is used to know if we are in the restoring process or not.
 * If we are restoring the previous state, we never send anything.
 */
int online = 0;                         

char buf[256];                          // A very often used buffer...


// Structures -------------------------------------------------------------

/** Account
 * This struct describe an account :
 * - numero : should be the same as the index of comptes[].
 * - nom    : name of the customer.
 * - mdp    : password.
 * - solde  : balance of the account.
 * - addr   : last known address of the client, 
 *              this is usefull for the R# message.
 * compte_max describes the number of accounts.
 * compte_capa describes the capacity of the array.
 */
struct compte
{
  int numero;
  char * nom;
  char * mdp;
  double solde;
  struct sockaddr_in addr;
} * comptes;
int compte_max = -1;
int compte_capa = COMPTES_MIN;

/** Cheques
 * This struct describes a cheques. All cheques are stored, event if
 * there is no more to do with it (archive). The struct is :
 * - numero     : should be the same as the index of cheques[].
 * - num_compte : id of the emitter account.
 * - montant    : amount of money on that cheque.
 * - debite     : is this cheque still valid ?
 * cheque_max describes the current number of cheques.
 * cheque_capa describes the capacity of the array.
 */
struct cheque
{
  int numero;
  int num_compte;
  double montant;
  short debite;
} * cheques;
int cheque_max = -1;
int cheque_capa = CHEQUES_MIN;

/** Bank
 * This struct describe the other banks.
 * @warning This array is static. See NUM_BANQUES.
 * - num  : id of the bank. (nothing to do with the index this time)
 * - addr : network address of this bank.
 * @see ajouter_banque to add a bank
 */
struct banque
{
  int num;
  struct sockaddr_in addr;
} banques[NUM_BANQUES];
int banque_max = -1;



// Usefull functions -------------------------------------------------------

/** Compares two string without crashing if one is NULL.
 * @param s1 the first string
 * @param s2 the second string
 * @return the same value as strcmp(s1,s2)
 */
int strnullcmp(const char * s1, const char * s2)
{
  if (s1 == NULL) { return -2; }
  if (s2 == NULL) { return  2; }
  return strcmp(s1, s2);
}

/** Get the account number corresponding to the name of the customer
 * @param nom name of the customer
 * @return the account number (ie the index in comptes[])
 *         or a negative value if it fails.
 */
int get_compte_numero(char * nom)
{
  int i = 0;
  while ( (i < compte_max) && (!(strnullcmp(nom, comptes[i].nom) == 0)) ) 
  {
    i++;
  }
   
  if (strnullcmp(nom, comptes[i].nom) == 0) 
  {
    return i;
  }
  else
  {
    return ERR_ACCOUNT_NOT_FOUND;
  }
}


/** Retrieves the number of an account according to the name of the customer
 * and check the validity of the password.
 * @param nom name of the customer
 * @param mdp password
 * @return the account number or a negative value if it fails.
 * Note : if it fails, an error message is sent to the client.
 */
int numero_compte_mdp(char * nom, char * mdp)
{
  int num_compte;
  // Get the account number
  num_compte = get_compte_numero(nom);
  if ( (num_compte >= 0) )
  {
   if (strnullcmp(comptes[num_compte].mdp, mdp) == 0)
   {
     return num_compte;
   }
   else
   {
    // Bad password
    sprintf(buf,"E#202#Mot de passe invalide ou compte non trouv�");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
    return ERR_BAD_PASSWORD;
   }
  }
  else
  {
    // Erreur
    sprintf(buf,"E#202#Mot de passe invalide ou compte non trouv�");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
    return ERR_ACCOUNT_NOT_FOUND;
  }
}


/** Dumps the content of comptes and cheques to the screen.
 * Debugging purpose only.
 */
void dump()
{
  int i=0;
  printf("Comptes ---------------------------\n");
  while (i<=compte_max)
  {
    printf("c#%d#%s#%s#%f [%s %5i] \n",
      comptes[i].numero,
      comptes[i].nom,comptes[i].mdp,
      comptes[i].solde, 
      (char *)inet_ntoa(comptes[i].addr.sin_addr), 
      (int)comptes[i].addr.sin_port);
    i++;
  }
  i=0;
  printf("Cheques ---------------------------\n");
  while (i<=cheque_max)
  {
    printf("h#%d#%d#%.2f#%d\n",
      cheques[i].numero,
      cheques[i].num_compte,
      cheques[i].montant,
      cheques[i].debite);
    i++;
  }
  printf("-------------------------------------\n");
}

/** Retrieves the index of the bank according to the id.
 * @param numbanque id of the bank
 * @return index of the bank in banques[] of a negative value if it fails.
 * If the bank is not found, an error message is sent.
 */
int num_banque (int numbanque)
{
  int i;
  for (i=0;i<=banque_max;i++)
  {
    if (banques[i].num == numbanque) return i;
  }
  sprintf(buf, "E#206#Banque non trouv�e");
  printf("> %s\n",buf);
  if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  return ERR_BANK_NOT_FOUND;
}

/** Add a bank in banques[]
 * @param numero id of the bank
 * @param adresse DNS address of the bank.
 */
void ajouterbanque(int numero, char * adresse)
{
 struct hostent * host_server;

 printf("Banque %04d : %s\n",numero,adresse);
 host_server = gethostbyname(adresse);
 if (host_server == NULL)
 {
   printf("Error : hostname");
   exit(2);
 }
 banque_max++;
 banques[banque_max].addr.sin_port = ntohs(PORT);
 banques[banque_max].addr.sin_family = AF_INET;
 banques[banque_max].addr.sin_addr = *(struct in_addr *)host_server->h_addr;
 banques[banque_max].num = numero;
}

/** Send a message to an account
 * @param buf message to be sent
 * @param nom name of the customer to be sent the message
 */
void sendtoclient(char * buf, char * nom)
{
  int noclient;
  noclient = get_compte_numero(nom);
  if (noclient >= 0)
  {
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&comptes[noclient].addr, sizeof(comptes[noclient].addr));  
  }
}



// Functions actions ------------------------------------------------------

/** Creation of an account
 * @param nom name of the customer
 * @param mdp password used
 * The bank checks if the password is not empty and 
 *  if an account with the same name does not exist.
 */
void creation(char * nom, char * mdp)
{
  void * temp;
  int i;
  
  // Check the args.
  if ((nom == NULL) || (mdp == NULL) || (strnullcmp(nom,"")==0) || (strnullcmp (mdp,"")==0))
  {
    sprintf(buf,"E#203#Nom ou mot de passe vide");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
    return;    
  }
  
  // Check the name is not used.
  i=0;
  while(i <= compte_max)
  {
    if (strcmp(nom,comptes[i].nom) == 0)
    {
     sprintf(buf, "E#204#Compte existant");
     printf("> %s\n",buf);
     if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
     return;
    }
    i++;
  }
  if (i < compte_max) return;
  
  // Check if the array is able to add an account and reallocate if necessary
  if ((compte_max+1) == compte_capa)
  {
     printf("[MEM] Reallocating compte table...\n");
     compte_capa += COMPTES_INC;
     if ( (temp = realloc(comptes,sizeof(struct compte)*compte_capa)) == NULL)
     {
       printf("FAILED !!");
       compte_capa -= COMPTES_INC;
     }
     else
     {
       comptes = temp;
     };
     
  }
  
  // Add the account
  if (compte_max < compte_capa)
  {
   // D�but du code utile...
   compte_max++;
   comptes[compte_max].numero = compte_max;
   comptes[compte_max].nom = strdup(nom);
   comptes[compte_max].mdp = strdup(mdp);
   comptes[compte_max].solde = 0;
   comptes[compte_max].addr = addr_client;
   // Fin du code utile...
   sprintf(buf, "OK");
   printf("> %s\n",buf);
   if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
  else
  {
    sprintf(buf,"E#200#Banque indisponible");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
}


/** Credit an account
 * @param nom name of the customer
 * @param somme amount to add
 * The bank checks for negative values.
 */ 
void depot(char * nom, double somme)
{
  int num_compte;
  num_compte = get_compte_numero(nom);
  if ( (num_compte != -1) && (somme > 0) )
  {
    comptes[num_compte].solde+=somme;
    sprintf(buf,"OK");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
  else
  {
    // Not found or negative value.
    sprintf(buf,"E#201#Compte non trouv� ou somme n�gative");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
}


/** Ask for information.
 * Return the customer the balance of his account
 * @param nom name of the customer
 * @param mdp password
 */
void consultation(char * nom, char * mdp)
{
  int num_compte;
  num_compte = numero_compte_mdp(nom,mdp);
  if ( (num_compte >= 0) )
  {
   sprintf(buf, "S#%s#%.2f",NOM_BANQUE,comptes[num_compte].solde);
   printf("> %s\n",buf);
   if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
  // We don't have to send an error message since numero_compte_mdp has done it.
}


/** Transfer money from an account to another.
 * @warning This function is designed for an account in another bank,
 *           but it should work for the same bank.
 * @warning No checks !
 * @warning Currently, the number of the bank is used, not the name.
 * @param banque id of the dest bank
 * @param nomdest name of the dest customer
 * @param nom name of the customer that makes the transfer
 * @param mdp password of the customer
 * @param somme amount of money transfered.
 * The bank checks for negative values.
 */
void virement(char * banque, char * nomdest, char * nom, char * mdp, double somme)
{
  int nbanque, num_compte;
  // TODO : num�ro ou nom de la banque ??
  nbanque = num_banque(strtod(banque,NULL));
  num_compte = numero_compte_mdp(nom,mdp);
  if (somme <= 0)
  {
    sprintf(buf, "E#205#Somme n�gative ou nulle");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
    return;
  }
  
  if (num_compte >= 0)
  {
   if (nbanque >= 0)
   {
    // TODO : Debit� sans v�rification
    comptes[num_compte].solde-=somme;
    sprintf(buf, "T#%s#%f",nomdest,somme);
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&banques[nbanque].addr, sizeof(banques[nbanque].addr));  
   }
  }
}


/** Update the client address
 * @param nom name of the customer
 * @param mdp password of the customer
 * Hidden param : addr_client.
 */
void mise_a_jour(char * nom, char * mdp)
{
  int ncompte;
  ncompte = numero_compte_mdp(nom, mdp);
  if (ncompte >= 0)
  {
    comptes[ncompte].addr = addr_client;
    sprintf(buf, "OK");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
}


/** Creates a cheque
 * @param nom name of the customer
 * @param mdp password of the customer
 * @param somme amount of money
 * The array of cheques is dynamically updated.
 * The Cheque format is : %04%06 idbank, idcheque
 */
void creation_cheque(char * nom, char * mdp, double somme)
{
  int num_compte;
  void * temp;

  // Checks if the array is able to add a new cheque.
  if (cheque_max == cheque_capa)
  {
     printf("[MEM] Reallocating cheque table...\n");
     cheque_capa += CHEQUES_INC;
     if ( (temp = realloc(cheques,sizeof(struct cheque)*cheque_capa)) == NULL)
     {
       printf("FAILED !!");
       cheque_capa -= CHEQUES_INC;
     }
     else
     {
       cheques = temp;
     };
  }
  
  // Gets the id of the account.
  num_compte = numero_compte_mdp(nom,mdp);
  if ( ( num_compte >= 0 ) && ( somme > 0 ))
  {
    cheque_max++;

    if (cheque_max < cheque_capa)
    {
     cheques[cheque_max].numero = cheque_max;
     cheques[cheque_max].num_compte = num_compte;
     cheques[cheque_max].montant = somme;
     cheques[cheque_max].debite = 0;
     sprintf(buf, "Q#%04d%06d",numero_banque,cheque_max);
     printf("> %s\n",buf);
     if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));    
    }
    else
    {
    sprintf(buf,"E#000#Banque indisponible");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
    }
  }
  else
  {
    // Erreur : compte non trouve ou some negative
  }
}



/** Treat the cheque
 * @param cheque number of the local cheque
 * @param nomserver name of the server (not used)
 * @param num_compte id of the account.
 * @return the value of the cheque 
 *           or -1 if the account is not in this bank.
 * If the customer pay, we send a R# message.
 */
double traite_cheque(int cheque, char * nomserver, int num_compte)
{
 if ((cheque <= cheque_max) && (cheque >= 0) )
 {
  if (cheques[cheque].debite == 0)
  {
    if (num_compte != -1) comptes[num_compte].solde+=cheques[cheque].montant;
    // Avertir le client qu'il a �t� d�bit�
    sprintf(buf, "R#%s#%.2f",nomserver,cheques[cheque].montant);
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&comptes[cheques[cheque].num_compte].addr, sizeof(addr_client));
    comptes[cheques[cheque].num_compte].solde-=cheques[cheque].montant;
    cheques[cheque].debite = 1;
    return cheques[cheque].montant;
  }
  else
  {
    return -1;
  }
 }
 else
 {
  //  Mauvais numero
  return -1;
 }
}

/** Pay a check (first step)
 * @param nomclient name of the customer of the server (not used)
 * @param no_cheque id of the cheque (in the %04%06 format)
 * @param nomserver name of the customer (here the customer of the bank is the server...)
 * @param mdp_server password of the customer (why ?)
 * If the cheque belongs to this bank we treat it immediatly and return a response to the server,
 *  else we ask the corresponding bank.
 */
void demande_paiement_cheque(char *nomclient, char *no_cheque, char *nomserver, char *mdp_serveur)
{  
  int num_compte, nbanque;
  int banque, cheque;
  num_compte = numero_compte_mdp(nomserver,mdp_serveur);
  if (num_compte>=0)
  {
    // Checks the bank
    sscanf(no_cheque,"%04d%06d",&banque,&cheque);
    if (banque == numero_banque)
    {
       // It is this bank !
      if (traite_cheque(cheque,nomserver, num_compte) > 0)
      {
        // OK
        sprintf(buf, "K#%s#%s#%.2f#OK",comptes[cheques[cheque].num_compte].nom,no_cheque,cheques[cheque].montant);
        printf("> %s\n",buf);
        if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
      }
      else
      {
        // NOK
        sprintf(buf, "K#%s#%s#%.2f#NOK",comptes[cheques[cheque].num_compte].nom,no_cheque,0.0);
        printf("> %s\n",buf);
        if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
      }
    }
    else
    {
     // Not this bank. C'est pas nous U#ncheque#nom#ip#port
     nbanque=num_banque(banque);
     sprintf(buf, "U#%s#%s#%s",no_cheque,nomserver,nomclient);
     printf("> %s\n",buf);
     if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&banques[nbanque].addr, sizeof(banques[nbanque].addr));  
    }
  }
  else
  {
    // Erreur : compte non trouve ou some negative
    sprintf(buf, "K#Bad#0#0.00#NOK");
    printf("> %s\n",buf);
    if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
} 

/** A bank asked another for a cheque (step 2)
 * @param nochq id of the cheque
 * @param nomserver name of the server (used in the response to the client and to the bank)
 * @param nomclient name of the customer (used in the response to the client and to the bank)
 * This function always sends a A# message (OK/NOK) to the bank.
 */
void interrogation_cheque(char * nochq, char * nomserver, char * nomclient)
{
  int banque, cheque;
  sscanf(nochq,"%04d%06d",&banque,&cheque);
  if (banque == numero_banque)
  {
    if (traite_cheque(cheque,nomserver,-1) > 0)
    {
      // OK
      sprintf(buf, "A#%s#%.2f#%s#%s",nochq,cheques[cheque].montant, nomserver, nomclient);
      printf("> %s\n",buf);
      if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
      return;
    }
  }
  // Error
  sprintf(buf, "A#%s#%.2f#%s#%s#NOK",nochq,0.0,nomserver,nomclient);
  printf("> %s\n",buf);
  if (online) sendto(sd, buf , 256, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
}

/** A bank returned us the acceptation of a cheque (step 3)
 * @param nochq id of the cheque
 * @param montant amount of the cheque 
 * @param nomserver name of our customer
 * @param nomclient name of the customer of the server (just transfered)
 * This function always send a K# response to the server (OK/NOK).
 * The error is detected by the montant arg, and not by OK/NOK of the A# message.
 */
void acceptation_cheque(char * nochq, double montant, char * nomserver, char * nomclient)
{
  if (montant > 0)
  {
   depot(nomserver, montant);
   sprintf(buf, "K#%s#%s#%.2f#OK",nomclient,nochq,montant);
   printf("> %s\n",buf);
   sendtoclient(buf,nomserver);
  }
  else
  {
   // Error
   sprintf(buf, "K#%s#%s#%.2f#NOK",nomclient,nochq,montant);
   printf("> %s\n",buf);
   sendtoclient(buf,nomserver);
  }
}


// Processing function -------------------------------------------------

/** Process a command line 
 * @param buf line to process
 * @warning This function is not able to process several lines.
 *            buf <b>must</b> contain only one instruction
 */
void process(char * buf)
{
  char instruction;
  char *arg1, *arg2, *arg3, *arg4, *arg5;

  // Process the data
  if (strcmp(buf,"OK") == 0) { return; }
  
  // Suppress the \n
  if (buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]='\0';
  
  instruction = buf[0];
  if (buf[1] != '#') 
  {
    printf("| Warning : bad format.\n");
  }

  // Split the line
  //  We _must_ use argX to have the correct order with the strtok
  //  We don't need (and should not!) free these arg
  strtok(buf,"#");
  arg1 = strtok(NULL,"#");
  arg2 = strtok(NULL,"#");
  arg3 = strtok(NULL,"#");
  arg4 = strtok(NULL,"#");
  arg5 = strtok(NULL,"#");

  switch(instruction)
  {
    case 'C' :  // Cr�ation d'un compte
      // C#nom#mdp
      creation(arg1, arg2);
      break;
    case 'T' :   
    case 'D' : // D�pot sur le compte
      // D#nom#somme
      depot(arg1, strtod(arg2,NULL));
      break;
    case 'I' : // Consultation du solde du compte
      // I#nom#mdp
      consultation(arg1, arg2);
      break;
    case 'H' : // Demande de cr�ation d'un ch�que
      // H#nom#mdp#somme
      creation_cheque(arg1, arg2, strtod(arg3,NULL));
      break;
    case 'P' : // D�pot d'un cheque
      // P#nochq#nomclient#nomserver#mdpserver
      demande_paiement_cheque(arg1, arg2, arg3, arg4);
      break;
    case 'U' : // Interrogation d�pot de cheque d'une autre banque
      // U#nochq#nomclient#nomserver
      interrogation_cheque(arg1,arg2,arg3);
      break;
    case 'A' : // Acceptation d�pot ch�que
      // A#nochq#montant#nomserver#nomclient
      acceptation_cheque(arg1,strtod(arg2,NULL),arg3,arg4);
      break;
    case 'V' : // Demande de virement
      // V#banquedest#nomdest#nom#mdp#montant
      virement(arg1,arg2,arg3,arg4,strtod(arg5,NULL));
      break;
    case 'M' : // Mise a jour endroit
      // M#nom#mdp
      mise_a_jour(arg1,arg2);
      break;
    case 'd' : // Dump // TEMP !
      // d
      dump();
      break;
    default :  
      printf("| Inconnu\n");
      break;  
  }
}


// Fonction principale ----------------------------------------------------

/** How To use :
 * ./server <numero server> <restore>
 * - numero_server tells which bank we are
 * - restore restores ste previous state according to server.log
 */ 
int main(int argn, char ** argv)
{
 char buf[256], s1[20], *s2,s3[256];
 int n, port;
 FILE * flog;

 // Read the command line
 if (argn >= 2)
 {
   numero_banque = atoi(argv[1]);
 }
 printf("Banque courante : %d\n", numero_banque);
   
 // Allocation m�moire
 comptes = (struct compte *) malloc(sizeof(struct compte)*compte_capa);
 cheques = (struct cheque *) malloc(sizeof(struct cheque)*cheque_capa);

 // Creates the socket
 sd=socket(AF_INET, SOCK_DGRAM, 0);
 if (sd == -1)
 {
   perror("Error while creating socket.");
   exit(2);
 }

 // Address - Bind
 address.sin_port = htons(PORT);
 address.sin_addr.s_addr = INADDR_ANY;
 address.sin_family = AF_INET;
 if (  bind (sd, (struct sockaddr *)&address, sizeof(address))  )
 {
   perror("Error while binding the socket.");
   exit(3);
 }
 
 // Creation des banques
 ajouterbanque(12, "shinji.via.ecp.fr");
 ajouterbanque(42, "peyronnet.via.ecp.fr");
 ajouterbanque(2,"puma.cti.ecp.fr");
 //ajouterbanque(13, "localhost");

 // Ouverture fichier
 if (argn >= 3)
 {
   // We parse the server log to restore the state of the bank.
   // This mecanism prevents us to manage a complicated "database" system.
   flog = fopen("server.log","r+");
   while (!feof(flog))
   {
     // Reconstructs the addr_client address.
     if (fgets(buf,256,flog) == NULL) continue;
     sscanf(buf,"[%s %d] (%d) $ %s",s1,&port,&n, s3);
     s2=strtok(buf,"�"); s2=strtok(NULL,"�");
     if (s2 != NULL)
     {
      s2+=1;
      s2[strlen(s2)-1]='\0';
      addr_client.sin_port = port;
      addr_client.sin_family = AF_INET;
      // Uniquement pour des Linux moooooortels : inet_aton(s1,&addr_client.sin_addr);
      addr_client.sin_addr.s_addr=inet_addr(s1);
      printf("(%s-%d) {%d} | %s\n",s1,port,n,s2);
      process(s2);
     }
     else
     {
       printf("Pb NULL : %s",buf);
     }
     // TODO
   }
 }
 else
 {
  flog = fopen("server.log","w");
 }

 
 printf("Banque pr�te.\n\n");
 online = 1;
 
 // Main loop
 while (1)
 {
  // Receive the data
  memset(buf, 0, sizeof(buf));
  // Attention : pointeur sur la taille, car retour d'information
  size = sizeof(addr_client);
  n = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addr_client, &size);
  if (n == -1)
  {
   perror("Erreur");
  }
  else
  {
  // TODO : Traiter ligne par ligne, si buf est sur plusieurs lignes...
 
  // Print the data
  printf("[%s %5i] (%i) � %s\n", (char *)inet_ntoa(addr_client.sin_addr), (int)addr_client.sin_port, n, buf);
  fprintf(flog,"[%s %5i] (%i) � %s\n", inet_ntoa(addr_client.sin_addr), (int)addr_client.sin_port, n, buf);  fflush(flog);

  process(buf);
    
  // if ((strtok(NULL,"#") != NULL)) { printf ("extra info"); }
  // Re-send the data to the client
  // sendto(sd, buf, n, 0, (struct sockaddr *)&addr_client, sizeof(addr_client));
  }
 }
 
 // TODO : Jamais atteint...
 fclose(flog);
 free(cheques);
 free(comptes);
 
 exit(0);
}

client.c

/** client udp (c) 2002 Judy Ngan, R�mi Peyronnet
 * Simple client program to send data to an UDP server.
 * !!! TESTING PURPOSE ONLY !!!
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#define PORT 3000

int main(int argn, char ** argv)
{

  int sd;
  //struct sockaddr_in address;
  struct sockaddr_in addr_server;
  struct hostent * host_server;
  socklen_t size;
  char buf[256];
  int n;

  if (argn < 2)
  {
    printf("Use : test_client  <server> [--dontwait]\n");
    exit(1);
  }
  

  // Creates the socket
  sd=socket(AF_INET, SOCK_DGRAM, 0);
  if (sd == -1)
  {
    perror("Error while creating socket.");
    exit(2);
  }

  // Address - Server
  host_server = gethostbyname(argv[1]);
  if (host_server == NULL)
  {
    printf("Error : hostname");
    exit(2);
  }
  addr_server.sin_port = ntohs(PORT);
  addr_server.sin_family = AF_INET;
  addr_server.sin_addr = *(struct in_addr *)host_server->h_addr;
  printf("Server : %s\n",argv[1]);

  while (!feof(stdin))
  {
    buf[0]='\0';
    fscanf(stdin,"%255s",buf);
    // WARNING %s s'arrete a espace ! mieux fgets(buf,255,stdin)
    if (strlen(buf) != 0) 
    {
      sendto(sd, buf, strlen(buf), 0, (struct sockaddr *)&addr_server, sizeof(addr_server));
    }
  }

  if (argn == 2) 
  {
    
  while(1)
  {
    size = sizeof(addr_server);
    n = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addr_server, &size);
    if (n < 0) 
    {
       //exit(1);
    }
    else
    {
        printf("%s\n",buf);
    }
  }

  }
  exit(0);
}

Makefile

#
# TP Unix 17/12/2001
#

all: server client

# -lsocket -lnsl pour le cti
# -lnsl -lbind pour linux, ou rien
# et rien pour cygwin 

server: server.c
    gcc -o server server.c #-lnsl -ggdb

client: client.c
    gcc -o client client.c #-lnsl -lbind

clean:
    rm -f client
    rm -f server
  
sun: client.c server.c
    gcc -o server server.c -lsocket -lnsl -ggdb
    gcc -o client client.c -lsocket -lnsl
 

Exemples

peyronnet.in

C#a#a
D#a#200
I#a#a
H#a#a#100
#V#12#b#a#a#100
H#a#a#50
P#a#0042000000#a#a
P#b#0012000000#a#a
d#

shinji.in

C#b#b
D#b#300
H#b#b#100

test.in

C#toto#mdp
D#toto#200F
I#toto#mdp
D#toto#12.42
I#toto#badmdp

C#server#server
D#server#100
I#server#server

H#toto#mdp#100
H#toto#mdp#12
H#toto#badmdp#13
H#toto#mdp#14


I#toto#mdp
I#server#server
P#dummy#0042000001#server#server
I#toto#mdp
I#server#server

V#12#shinji#toto#mdp#50

overflow.in

C#testa#mdp
C#testz#mdp
C#teste#mdp
C#testr#mdp
C#testt#mdp
C#testu#mdp
C#testy#mdp
C#testu#mdp
C#testi#mdp
C#testo#mdp
C#testp#mdp
C#testq#mdp
C#tests#mdp
C#testd#mdp
C#testf#mdp
C#testg#mdp
C#testh#mdp
C#testj#mdp
C#testk#mdp
C#testl#mdp
C#testm#mdp
C#testw#mdp
C#testx#mdp
I#testu#mdp

Quelques scripts utiles

restore.sh

cat server.log.restart | awk -F "� " '{ print $2 }' | ./client localhost --nowait

start_server.sh

#
# Lance le server
#
# rebalance le server.log 
# - s'il existe
# - si aucun argument n'est passe

LOG=server.log
LOG2=server.log.restart


rm -f $LOG2
mv -f $LOG $LOG2
./server

exit 0

# marche pas

if test -f $LOG
then
 if test -z $1 
 then
  echo Server restarting...
  rm -f $LOG2
  mv -f $LOG $LOG2
  #./server &
  ./restore.sh
  ./server
 else 
  echo Server starting...
  ./server
 fi
else
 echo Server starting...
 ./server
fi