Université du Québec à Montréal INF3270 – Téléinformatique
Travail de session (Parties 1 et 2)
par:
Nicola Grenon GREN30077307
Mardi 13 juin 2006
Implémentation:
J'ai choisi l'implémentation en Java naturellement parce qu'il m'est plus familier, mais aussi pour sa facilité de modularisation. Ainsi, il était manifeste que le code de l'émetteur et du récepteur allaient nécessiter beaucoup de fonctions similaires. Donc, réunir tout ce qui était commun dans une classe (Trame.java) était l'étape naturelle à suivre.
Ainsi, dans Emetteur.java et Recepteur.java il n'y a que la logique d'exécution telle que décrite dans l'énoncé du travail pratique. (Quoi demander, quoi faire, combien de fois le répéter, jusqu'à quand, etc.) Alors que dans Trame.java, il y a tous les outils nécessaires à ces deux classes primaires.
Trame.java contient tout d'abord le code commun à chaque instance de la classe, qu'elle serve à lire sur le BUS ou à y écrire. On parle ici des méthodes d'accès classiques (get/set) et des méthodes d'analyse (FCScheck, isInfo, isAck, isNack, etc.) qui permettent de rapidement identifier la nature d'une trame et son état. Il y a ensuite un lot de fonctions utilitaires (static) pour simuler l'environnement: bin2txt, txt2bin, int2bin servent à générer du binaire en format texte standard et inversement. Il y a ensuite les fonctions de gestion de la trame: stuff/destuff et mecanicFCS. J'ai pu utiliser la même fonction pour la génération du code FCS et la vérification de celui-ci puisque c'est le même procédé pour un cas comme l'autre. Finalement il y a writeOnBUS et readOnBUS qui exécutent les étapes préparant les données à être écrites sur le BUS ou les extrayant du stream lu sur le BUS.
Pour terminer il y a BUS.java qui sert de simulateur du BUS en offrant des fonctions statiques pour lire et écrire sur le BUS simulé, en l'occurrence un fichier appelé bus.
En ayant ainsi divisé le code, la modularité acquise me permettrait facilement de changer le média de communication en modifiant essentiellement la classe de BUS. De même, cet assemblage permettrait aussi de compléter une classe plus réaliste d'émetteur-récepteur utilisant en même temps toutes les fonctions de Trame.java plutôt que de se limiter à un scénario précis comme c'est le cas dans le programme actuel.
Exécution:
L'exécution est simple, il suffit de lancer les classes Emetteur et Recepteur de façon classique en Java. Chacune de ces classes contiennent un main et leur exécution est ensuite robuste et auto explicite.
J'ai ajouté aux contraintes du travail quelques items de robustesse, comme par exemple l'émetteur va automatiquement détecter les caractères non encodables selon son évaluation (ASCII tout bête) et les remplacer par des
«?». Dans le même esprit, l'Emetteur et le Recepteur s'assurent de la validité de l'adresse. Ils ont aussi la notion de l'adresse de broadcast (255) grâce à un test supplémentaire dans la fonction aMoi(adresse). Finalement, j'ai ajouté quelques vérifications de cas d'Exception afin de survivre à des erreurs hors de notre contrôle. Ainsi, à la fin de mes tests, je n'ai plus trouvé de cas de plantage inattendu.
Traces:
Fonctionnement normal:
(Noter que j'ai pesé une fois de trop sur ENTRÉE de chaque côté pour montrer la robustesse) (J'ai ajouté des bits avant et après les fanions. Les bits lus sont uniquement entre les fanions)
EMETTEUR:
---
Entrez l'adresse de destination svp: 34 Entrez le message svp:
Bonjour, comment ca va?
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Peser ENTREE pour lire une trame.
Reception confirmee!
Appuyez sur une touche pour continuer...
RECEPTEUR:
---
Entrez notre adresse svp: 34 Peser ENTREE pour lire une trame.
Message recu!
Message: Bonjour, comment ca va?
Peser ENTREE pour lire une trame.
Message recu!
Controle
Peser ENTREE pour lire une trame.
Fonctionnement anormal:
(le message envoyé a été corrompu la première fois au niveau des bits internes et la seconde fois il manquait un fanion)
EMETTEUR:
---
Entrez l'adresse de destination svp: abc Entrez l'adresse de destination svp: 1234 Entrez l'adresse de destination svp: 200 Entrez le message svp:
Il etait une fois une marchande de foie.
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Reception confirmee!
Appuyez sur une touche pour continuer...
RECEPTEUR:
---
Entrez notre adresse svp: 200 Peser ENTREE pour lire une trame.
Une trame endommagee (bad FCS) a ete recue...
Peser ENTREE pour lire une trame.
Une trame endommagee (bad FCS) a ete recue...
Peser ENTREE pour lire une trame.
Message recu!
Message: Il etait une fois une marchande de foie.
Peser ENTREE pour lire une trame.
Fonctionnement anormal:
(le message envoyé a été corrompu la première fois, puis on voit que le récepteur ne réagit plus (pas la bonne adresse – La réaction est obligatoire sur le bad FCS parce que l'adresse elle-même est encapsulée dans le FCScheck, donc on peut pas savoir à qui la trame était destinée.)
EMETTEUR:
---
Entrez l'adresse de destination svp: 35 Entrez le message svp:
rien du tout
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Peser ENTREE pour lire une trame.
RECEPTEUR:
---
Entrez notre adresse svp: 123 Peser ENTREE pour lire une trame.
Une trame endommagee (bad FCS) a ete recue...
Peser ENTREE pour lire une trame.
Peser ENTREE pour lire une trame.
Fonctionnement anormal:
(le message de retour a été corrompu la première fois (il n'y a pas de mécanisme de renvoie de la confirmation, mais au moins l'émetteur n'accepte que les message valides...))
EMETTEUR:
---
Entrez l'adresse de destination svp: 255 Entrez le message svp:
A tout le monde!
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Peser ENTREE pour lire une trame.
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Reception confirmee!
Appuyez sur une touche pour continuer...
RECEPTEUR:
---
Entrez notre adresse svp: 123 Peser ENTREE pour lire une trame.
Message recu!
Message: A tout le monde!
Peser ENTREE pour lire une trame.
Une trame endommagee (bad FCS) a ete recue...
Peser ENTREE pour lire une trame.
Message recu!
Message: A tout le monde!
Peser ENTREE pour lire une trame.
Fonctionnement normal:
(Gestion des caractères spéciaux...)
EMETTEUR:
---
Entrez l'adresse de destination svp: 34 Entrez le message svp:
Bonjour, allô, comment ça va? àéü hoho!
Le message a ete envoye sur le bus.
Peser ENTREE pour lire une trame.
Reception confirmee!
Appuyez sur une touche pour continuer...
RECEPTEUR:
---
Entrez notre adresse svp:
Entrez notre adresse svp: 34 Peser ENTREE pour lire une trame.
Message recu!
Message: Bonjour, all?, comment ?a va? ??? hoho!
Peser ENTREE pour lire une tram
Code source:
Le travail a été réalisé en Java 1.5, sous 4 classes.
• Emetteur.java: Le code simple de l'émetteur
• Recepteur.java: Le code simple du récepteur
• Trame.java: Représentation logique d'une trame, outils d'analyse internes (dynamique) et outils de manipulation externe (statique).
• Bus.java: Émulation logique d'un BUS de data à l'aide d'un fichier texte appelé «bus».
/*
* INF3270 - Travail pratique - Partie 1 et 2 *
* par: explicite Grenon * GREN30077303 *
* 4 juin 2006 *
* Classe Emetteur. Contient un main pouvant créer une trame et la transmettre.
* De même qu'en vérifier la réception.
*/
// Importations import java.io.*;
public class Emetteur {
// Fonction principale
public static void main(String[] args) {
// Variables
Trame ecriture = new Trame(); // Contient la trame OUT Trame lecture = new Trame(); // Contient la trame IN int adresse;
String s;
// On s'identifie ...
System.out.println("EMETTEUR:\n---\n");
// On détermine l'adresse de destination
adresse = Trame.lireAdresse("Entrez l'adresse de destination svp: ");
ecriture.setAdresse(adresse);
// On note le message à envoyer (les données)
ecriture.setDonnees(Trame.lireMessage("Entrez le message svp: "));
// On prépare la trame à l'expédition ecriture.prepare();
// On boucle tant que le message n'a pas été reçu par le destinataire do {
// On envoie le message sur le bus ecriture.writeOnBUS();
System.out.println("Le message a ete envoye sur le bus.");
/* On boucle jusqu'à réception d'un message:
- M'étant adressé - Valide
- Du bon numéro de séquence - de ack/nack
*/
do {
System.out.println("Peser ENTREE pour lire une trame.");
try {
BufferedReader reader = new BufferedReader (
new InputStreamReader (System.in));
s = reader.readLine();
} catch (Exception e) {};
lecture.readOnBUS();
} while (
!lecture.fcsCheck() || // Message valide !lecture.aMoi(adresse) || // M'étant destiné
!((lecture.isAck() && lecture.equals(ecriture)) ||// Ack+Seq lecture.isNack()) // Ou alors Nack
);
} while (!lecture.isAck());
// Message bien reçu.
System.out.println("Reception confirmee!");
} }
/*
* INF3270 - Travail pratique - Partie 1 et 2 *
* par: explicite Grenon * GREN30077303 *
*
* Classe Recepteur. Contient un main pouvant lire une trame et la décoder.
* De même qu'en confirmer ou infirmer la réception.
*/
// Importations import java.io.*;
public class Recepteur {
// Fonction principale
public static void main(String[] args) {
// Variables
Trame ecriture = new Trame(); // Contient la trame OUT Trame lecture = new Trame(); // Contient la trame IN int adresse;
String s;
// On s'identifie ...
System.out.println("RECEPTEUR:\n---\n");
// On détermine notre adresse
adresse = Trame.lireAdresse("Entrez notre adresse svp: ");
// On boucle indéfiniement ...
while (true) {
/* On boucle jusqu'à réception d'un message:
- Valide
- M'étant adressé */
boolean ok; // Résultat du check du FCS do {
System.out.println("Peser ENTREE pour lire une trame.");
try {
BufferedReader reader = new BufferedReader (
new InputStreamReader (System.in));
s = reader.readLine();
} catch (Exception e) {};
lecture.readOnBUS(); // Lecture du BUS
/* On teste le FCS *** On ne peut pas confirmer les adresses...
donc on répond toujours et vers le broadcast. ***/
ok = lecture.fcsCheck();
if (!ok) {
System.out.println("Une trame endommagee (bad FCS) a ete recue...");
ecriture.setAdresse(255);
ecriture.setDonnees("");
ecriture.prepare(lecture, false); // Nack ! ecriture.writeOnBUS();
}
} while (
!ok || // Message valide !lecture.aMoi(adresse) // M'étant destiné );
// Message bien reçu.
System.out.println("Message recu!");
if (lecture.isInfo()) {
System.out.println("Message: " + lecture.getDonnees());
ecriture.setAdresse(255);
ecriture.setDonnees("");
ecriture.prepare(lecture, true); // Ack ! ecriture.writeOnBUS();
} else
System.out.println("Controle");
} } }
/*
* INF3270 - Travail pratique - Partie 1 et 2 *
* par: explicite Grenon * GREN30077303 *
* 4 juin 2006 *
* Classe Trame. Contient la définition d'une trame de même que * tous les outils (static) de travail avec une trame.
*/
// Importations
import java.util.Random; // Pour le numéro de séquence import java.io.*; // ReadLine
public class Trame {
// Constantes
final static String FANION = "01111110";
final static int FCS_LENGTH = 16;
final static String POLYNOME = "1100000001111";
// Variables
private String adresse, commande, donnees, fcs, stream;
// Constructeur public Trame() { }
// Méthodes d'accès
public void setAdresse(int valeur) { adresse = int2bin(valeur);
}
public void setDonnees(String txt) { donnees = txt2bin(txt);
}
public String getDonnees() { return bin2txt(donnees);
}
private String getSeq() { if (isInfo())
return commande.substring(1,4);
else if (isNack())
return commande.substring(5,8);
else
// Pour le Ack, il faut extraire la valeur
return int2bin((Integer.parseInt(commande.substring(5,8),2) + 8 - ((donnees.length() / 8) % 8))
% 8,3);
}
// Méthodes de vérification
public boolean aMoi(int a) { // Destiné à mon adresse?
return adresse.equals(int2bin(a)) ||
adresse.equals("11111111"); // Broadcast ! }
public boolean fcsCheck() { // Réussi le fcs Check?
if (fcs == "") return false; // Pas de trame!
return Integer.parseInt(
mecanicFCS(adresse + commande + donnees + fcs),2) == 0;
}
public boolean isInfo() { // Est une trame d'infos?
return commande.charAt(0) == '0';
}
public boolean isAck() { // Est une trame d'Ack?
return commande.charAt(0) == '1' && commande.charAt(4) == '0';
}
public boolean isNack() { // Est une trame de Nack?
return commande.charAt(0) == '1' && commande.charAt(4) == '1';
}
public boolean equals(Trame autre) {// A le même # de séquence?
// Ne vérifie que si les numéro de séquence correspondent ...
return getSeq().equals(autre.getSeq());
}
// Méthodes servant à préparer les trames à l'envoie
public void prepare() { // Trame d'informations Random generator = new Random();
int seq = generator.nextInt(8);
commande = "0" + int2bin(seq,3) + "1"
+ int2bin((seq + donnees.length()/8)%8,3);
}
public void prepare(Trame autre, boolean ack) { // Trame de contrôle if (ack) { // Ack
commande = "10000" + autre.getSeq();
} else { // Nack try {
commande = "10011" + autre.getSeq(); // Tentative d'obtenir } catch (Exception e) { // le numéro de SEQ commande = "10011000"; // mais les infos sont } // peut-être invalides.
} }
// Accès au BUS
public void writeOnBUS() {
// Préparation de la chaîne String temp = "";
temp += adresse;
temp += commande;
if (isInfo()) temp += donnees; // Trames d'info?
for (int i = 0; i<FCS_LENGTH; i++) temp += "0"; // Initialisation FCS
// Évaluation du FCS fcs = mecanicFCS(temp);
temp = temp.substring(0,temp.length()-FCS_LENGTH) + fcs;
// Construction du stream (stuffing) stream = "";
stream += FANION;
stream += stuff(temp,5);
stream += FANION;
// Transmission Bus.send(stream);
}
public void readOnBUS() { // Réception
String temp = Bus.receive();
// On ne conserve que la partie délimitée par les deux fanions int premier = temp.indexOf(FANION,0);
int second = temp.indexOf(FANION,premier+8);
// Initialisation stream = "";
adresse = "";
commande = "00000000";
donnees = "";
fcs = "";
// Est pas une chaîne complète if (second != -1) {
stream = temp.substring(premier,second+8);
// De-stuffing
temp = destuff(stream.substring(8,stream.length()-8),5);
// Découpage
if (temp.length() >= 16+FCS_LENGTH) { // Chaîne assez longue adresse = temp.substring(0,8);
commande = temp.substring(8,16);
if (temp.length() > 16+FCS_LENGTH) // Contient des données.
donnees = temp.substring(16,temp.length()-FCS_LENGTH);
fcs = temp.substring(temp.length()-FCS_LENGTH,temp.length());
} } }
/*** Section statique ***/
// Méthode permettant de lire une adresse sur la console public static int lireAdresse(String msg) {
String ligne; // Contiendra le texte lu en console int adr; // Valeur de l'adresse
do {
// Lecture sur la console System.out.print(msg);
try {
BufferedReader reader = new BufferedReader (
new InputStreamReader (System.in));
ligne = reader.readLine();
} catch (Exception e) {ligne = "";};
// Vérification de la validité de l'entrée adr = -1;
try {adr = Integer.parseInt(ligne);}
catch (NumberFormatException e) {};
} while (adr < 1 || adr > 255);
return adr;
}
// Méthode permettant de lire le message sur la console public static String lireMessage(String msg) {
String ligne; // Contiendra le texte lu en console
// Lecture sur la console System.out.println(msg);
try {
BufferedReader reader = new BufferedReader (
new InputStreamReader (System.in));
ligne = reader.readLine();
} catch (Exception e) {ligne = "";};
return ligne;
}
// Méthode permettant de traduire une chaîne de texte en String binaire private static String txt2bin(String txt) {
String bin = "";
int tmp;
for (int i = 0, max = txt.length(); i<max; i++) { tmp = (int) txt.charAt(i);
if (tmp < 256)
bin += int2bin(tmp);
else
bin += "00111111"; // Pas encodable sur 8 bits --> «?»
}
return bin;
}
// Méthode permettant de traduire un String binaire en chaîne de texte private static String bin2txt(String bin) {
String txt = "";
for (int i = 0, max = bin.length(); i<max; i+=8)
txt += (char) Integer.parseInt(bin.substring(i,i+8),2);
return txt;
}
// Méthode permettant de traduire un int en String binaire sur 8 bits.
private static String int2bin(int x) { String s = Integer.toBinaryString(x);
for (int i = 0, max = 8-s.length(); i<max; i++) s = "0" + s;
return s;
}
private static String int2bin(int x, int l) { // Pour longueur < 8 if (l > 8) l = 8;
return int2bin(x).substring(8-l,8);
}
// Méthode permettant de 'stuffer' un String binaire private static String stuff(String in, int s) {
// s = nombre de '1' max de suite String out = "";
char c; // bit actuel int n = 0; // Compteur de '1'
for (int i = 0, max = in.length(); i<max; i++) { c = in.charAt(i);
out += c;
if (c == '0') n = 0;
else { n++;
if (n == s) { // On insère un '0' de stuffing ! n = 0;
out += '0';
} } }
return out;
}
// Méthode permettant de 'dé-stuffer' un String binaire private static String destuff(String in, int s) {
// s = nombre de '1' max de suite String out = "";
char c; // bit actuel;
int n = 0; // Compteur de '1'
for (int i = 0, max = in.length(); i<max; i++) { c = in.charAt(i);
out += c;
if (c == '0') n = 0;
else { n++;
if (n == s) { // On coupe le prochain bit ! n = 0;
i++;
} } }
return out;
}
// Méthode appliquant la mécanique du FCS
private static String mecanicFCS(String chaine) {
int debut = chaine.indexOf("1"), // index du premier '1' fin = chaine.length()-FCS_LENGTH, // index de fin des données i,max ; // Compteurs
char[] sequence = chaine.toCharArray();
while (debut != -1 && debut < fin) {
// On boucle sur la longueur du FCS
for (i = 0, max = POLYNOME.length(); i<max; i++)
if (sequence[debut+i] == POLYNOME.charAt(i)) sequence[debut+i] = '0'; // XOR ==
else
sequence[debut+i] = '1'; // XOR <>
// On recalcule où est rendu le premier '1'
while (sequence[debut] == '0' && debut < fin) debut++;
}
return new String(sequence, chaine.length()-FCS_LENGTH, FCS_LENGTH);
} }
/*
* INF3270 - Travail pratique - Partie 1 et 2 *
* par: explicite Grenon * GREN30077303 *
* 4 juin 2006 *
* Classe Bus. Simule un BUS par un fichier texte.
*/
import java.io.*;
public class Bus {
final static String FICHIER = "bus";
// Pour écrire une trame sur le BUS static void send(String message) {
try {
PrintWriter aCreer = new PrintWriter(new FileWriter(FICHIER));
aCreer.println(message);
aCreer.close();
} catch (Exception e) {
System.out.println("BUS non disponible!");
} }
// Pour lire une trame sur le BUS static String receive() {
String ligne = "";
try {
BufferedReader entree = new BufferedReader(new FileReader (FICHIER));
ligne = entree.readLine();
entree.close();
} catch (Exception e) {
System.out.println("BUS non disponible!");
}
return ligne;
} }