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
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 :
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.
�
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.
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.
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.
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.
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.
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.
[*] 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
/** 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 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);
}
#
# 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
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#
C#b#b D#b#300 H#b#b#100
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
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
cat server.log.restart | awk -F "� " '{ print $2 }' | ./client localhost --nowait
# # 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