réseau en C#
Alain Casali
alain.casali@univmed.fr
C# c’est quoi ?
• Langage de programmation créé en 2002 (1° beta en
2000)
• Basé sur le framework .Net de Microsoft
• Un peu de C++ et de Java
• Facilités de développement grâce aux briques
logicielles
C# Comment ça marche ?
• Basé sur .Net Framework
Programme C# Programme Java
.Net Framework Machine virtuelle Java
Système d’exploitation
Différences (apports) par rapport au C++
• Orientation objet prononcée (tout dérive de la classe
object),
• Unicode comme code des caractères,
• Libération automatique de la mémoire,
• « fin » des pointeurs,
• Remplacement des pointeurs par des références,
• Passage des arguments par référence et non plus pas adresse,
• Nouvelle boucle (foreach),
• Disparition de l’héritage multiple au profit d’interface,
• Langage intropsectif.
Qui utilise C# ?
• Microsoft avec l’architecture .NET
• Linux, Mac OS X et Solaris grâce à Novel qui participe
activement au projet Open Source Mono
(http://www.mono‐project.com/).
Beaucoup d’applications Gnome sont développées en C#
grâce à GTK #
Les programmes développés grâce à C# sont
«portables» sur ces plateformes sans recompilation
!
• Actuellement Mono 2.0 (prise en charge
complète de .Net Framework 2.0 plus
quelques fonctions de .Net 3.5).
Question performances, ça donne quoi ?
Source : http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=all
Organisation du cours et des TPs
• 4 séances de cours
• 6 séances de TPs
– 2 séances prises en main de C#
– 1 séance et ½ : client réseau
– 2 séance et ½ : serveur réseau
• Test :
– ½ h : contrôle de connaissance
– 2h : test « classique » (mise en pratique des connaissances
acquises)
• 1 avant projet
Plan du cours
1. Initiation à C#
2. Programmation réseau « de base » en C#
3. Programmation réseau « avancée» en C#
Partie 1 : Initiation à C#
Types primitifs
• Orientation objet prononcée ⇨ tous les types
primitifs sont définis dans des classes
• Chaque classe contient des données membres
et des fonctions membres qui lui sont
spécifiques
Les entiers (1)
Type Classe associé Commentaire
byte System.Byte Codé sur 1 octet, Type non signé
0 ‐> 255
sbyte System.SByte Codé sur 1 octet, Type signé
‐128 ‐> 127
short System.Int16 Codé sur 2 octets, Type signé
‐215 ‐> 215‐1
ushort System.UInt16 Codé sur 2 octets, Type non signé
0 ‐> 216‐1
int System.Int32 Codé sur 4 octets, Type signé
‐231‐> 231‐1
uint System.UInt32 Codé sur 4 octets, Type non signé
0 ‐> 232‐1
long System.Int64 Codé sur 8 octets, Type signé
‐263 ‐> 263‐1
ulong System.UInt64 Codé sur 8 octets, Type non signé
0 ‐> 264‐1
Les entiers (2)
• int i = 3;
• Si un entier commence par ‘0x’ ou par ‘0X’, alors il
est codé à l’aide d’une représentation hexadécimale.
• Si un entier se termine par la lettre ‘l’ ou ‘L’, alors
c’est un entier long. Attention de le pas confondre la
lettre ‘l’ avec le chiffre ‘1’.
long i = 3l;
• la représentation octale à disparue en C#
Les booléens
Type Classe associé Commentaire
bool System.Boolean Ne prend que pour valeur true
ou false.
Attention, pas de 0 ou de 1
comme en C++
• Les variables peuvent contenir des valeurs
« classiques », mais dans ce cas le séparateur entre la
partie entière et la partie décimale est le caractère ‘.’ ou
sous forme scientifique. Dans ce cas, c’est la lettre ‘e’ ou
‘E’ qui représente l’exposant (toujours une puissance de
10).
float Pi = 3.14f;
Les caractères
Type Classe associé Commentaire
char System.Char Codé sur 2 octets
En C#, on perd la
compatibilité entre les byte
et les char du C++, l’un étant
codé sur 2 octet, l’autre sur
un seul !
On utilise le système Unicode
Entrée : la jolie chaine <cr>
Sortie : ⇨ on évite d’utiliser Read()
l
a
jolie chaine
Les tableaux (1)
• Déclaration
type[] NomDuTableau;
NomDuTableau fait référence vers un tableau de
type à une dimension. Ce tableau n’est pas encore
alloué et sa taille n’est pas fixée.
• Allocation
NomDuTableau = new type [Taille];
• Exemple avec un tableau de 10 entiers
– C++ : int TabInt [10];
– C# : int[] TabInt = new int [10];
• Avec plusieurs dimensions
int [,] MatriceInt = new int [2,2];
Les tableaux (2)
• Accès à la i° case se fait grâce à la notation entre []
(comme en C++)
• Initialisation du tableau
– Une case après l’autre
TabInt[0] = 0;
TabInt[1] = 1; …
– Utilisation d’un type énumératif
int[] TabInt = {0, 1, …}
Dans ce cas, on ne précise pas la taille du tableau créé.
• Suppression : rien à faire, l’objet est
automatiquement supprimé dès que la variable n’a
plus de portée. On peut aussi l’affecter à null.
Parcours d’un tableau (1)
type[] Tab = new type [Taille];
On veut parcourir le tableau Tab et afficher chacun de ses
éléments. C’est une donnée membre
1. utilisation de la boucle for
for (int i = 0; i < Tab.Length; ++i)
Console.Write(Tab[i] + " ");
2. utilisation d’un énumérateur
IEnumerator ie = Tab.GetEnumerator();
while (ie.MoveNext())
Console.Write(ie.Current + " ");
Parcours d’un tableau (2)
3. Utilisation d’une boucle foreach
foreach (type Var in Tab)
Console.Write(Var + " ");
Cette méthode appelle de façon transparente la
méthode avec un énumérateur. Cependant, elle est
plus simple à utiliser.
Remarque : la variable de parcours ne peut pas être
présente dans le membre de gauche d’une
affectation.
Introspection (1)
• En C#, une variable est capable de connaitre son
type, c’est l’introspection. On peut obtenir ce type
comme suit pour une variable var :
var.GetType().Name Retourne un string
• Soit le tableau suivant
object[] TabObj = new object[3];
TabObj[0] = 1; Int32
TabObj[1] = "bla"; String
TabObj[2] = 1.2f; Single
On veut afficher le type de chaque variable du tableau :
foreach (object obj in TabObj)
Console.WriteLine(obj.GetType().Name);
Introspection (2)
• Tester le type d’un objet
1. if (obj is type)
2. if ("type" == obj.GetType().Name)
• On veut ajouter la valeur 1 à chaque objet de type
numérique
for (int i = 0; i < TabObj.Length; ++i)
{
if ( (TabObj[i] is Single) ||
(TabObj[i] is Int32))
TabObj[i] += 1;
} Erreur ֲL'opérateur '+=' ne peut pas être appliqué
aux opérandes de type 'object' et 'int‘
Introspection (3)
Pour que cela marche, on est obligé de transtyper
l’objet courant dans son type, puis de lui ajouter 1.
if (TabObj[i] is Single)
TabObj[i] = (Single) TabObj[i] + 1 ;
else if (TabObj[i] is Int32)
TabObj[i] = (Int32)TabObj[i] + 1;
Méthodes et données sur les tableaux
• Tous les tableaux dérivent de la classe array.
• Deux données membres
1. Length : nb total d’éléments
Console.Write(TabInt.Length); //10
2. Rank : nb de dimensions
Console.Write(TabInt.Rank); //1
• Méthodes de la classe array
– void Clear (Array Tab, int pos, int nb)
Remet à 0 nb éléments du tableau Tab à partir de la
position pos (ou à des chaines vides)
Array.Clear(TabInt, 0, TabInt.Length);
Méthodes sur les tableaux (2)
• Recherche d’éléments
int IndexOf (Array tab, object o)
int IndexOf (Array tab, object o, int posdeb)
int IndexOf (Array tab, object o, int posdeb,
int posfin)
Recherche de la première occurrence de l’élément o
dans le tableau tab entre les positions posdeb et
posfin. Renvoie ‐1 si l’élément est absent.
int n = Array.IndexOf(TabInt, 1, 0); //1
n = Array.IndexOf(TabInt, 1, 2); //-1
Méthodes sur les tableaux (3)
• tri
void Sort (Array tab)
trie le tableau tab
Array.Sort(TabInt);
• inversion
void Reverse (Array tab)
inverse la séquence des éléments dans le tableau
tab
Array.Reverse(TabInt); // 9, 8, 7,…
Méthodes sur les tableaux (4)
• Copie de tableau
void CopyTo (Array Tab, int pos)
copie les éléments du tableau vers le nouveau
tableau Tab (de taille supérieur ou égale) et à partir
de la position pos .
int[] TabCopy = new int[TabInt.Length];
TabInt.CopyTo(TabCopy, 0); // 0, 1, 2, …
…
int i = 4;
int j = Inc(out i);
Console.WriteLine(i + " " + j); // 2 1
Les classes (1)
class NomDeClasse {
protected type _nom;
• Utilisation des méthodes associées
P1.Initialisation("Casali","alain",30);
– Utilisation d'une variable locale non assignée
'P1'
P1.EcritConsole();
Les classes (3)
• Création d’un objet
CPersonne P1 = null;
– La variable P1 ne fait référence à aucun objet
• Utilisation des méthodes associées
P1.Initialisation("Casali","alain",30);
– on fait appel à la méthode Initialisation de l'objet référencé
par P1. Or cet objet n'existe pas encore et le compilateur ne signalera
l'erreur mais le programme plantera.
P1.EcritConsole();
Les classes (3)
• Création d’un objet
CPersonne P1 = new CPersonne();
• Utilisation des méthodes associées
P1.Initialisation("Casali","alain",30);
P1.EcritConsole();
Les classes (4)
Constructeurs
Pour créer puis instancier un objet, on est obligé de :
1. CPersonne P1 = new CPersonne();
2. P1.Initialisation("Casali","alain",30);
• On ajoute donc un constructeur à la classe CPersonne :
public CPersonne(string N, string P, int A)
{
this.Initialisation(N, P, A);
}
• Dans le Main(), on écrit :
CPersonne P1 = new CPersonne("Casali", "alain",
30);
Les classes (4)
• Ajout d’un deuxième constructeur
public CPersonne(CPersonne P)
{
_nom = P._nom;
_prenom = P._prenom;
_age = P._age;
}
• Dans le Main(), on écrit :
CPersonne P1 = new CPersonne("Casali", "alain", 30);
CPersonne P2 = new CPersonne(P1);
CPersonne P3 = P1;
• Même si les objets P1 et P2 contiennent la même information, ils ne sont
pas égaux (ce sont des objets différents et on n’a pas surchargé l’opérateur
==). Par contre les objets P3 et P1 sont bien égaux !!
Les classes (5)
Passage par référence
Dans l’exemple précédent, les objets P1 et P3 font référence à la même zone
objet / mémoire. C’est pour cela qu’on peut tester l’égalité sans surcharge
de l’opérateur ==. Par contre, toute modification d’un des deux objets
entraine la modification de l’autre !!!
CPersonne P1 = new CPersonne("Casali", "alain", 30);
CPersonne P2 = new CPersonne(P1);
CPersonne P3 = P1;
P1.EcritConsole(); //Casali, alain, 30
P2.EcritConsole(); //Casali, alain, 30
P3.EcritConsole(); //Casali, alain, 30
P1._prenom = "bob";
P1.EcritConsole(); //Casali, bob, 30
P2.EcritConsole(); //Casali, alain, 30
P3.EcritConsole(); //Casali, bob, 30
Les classes (6)
Accesseurs / Modifieurs
On veut lire / modifier des attributs privés ou protégés.
On suppose que l’attribut _prenom est de type protected.
• Accesseur
public string getPrenom() {
return _prenom;
}
• Modifieur
public void setPrenom(string P){
this._prenom = P;
}
• Dans le Main(), on écrit :
CPersonne P1 = new CPersonne("Casali", "alain", 30);
Console.WriteLine(P1.getPrenom()); // alain
P1.setPrenom("bob");
P1.EcritConsole(); // Casali, bob, 30
Les classes (6)
On peut utiliser les propriétés pour faire la même chose :
public string prenom
{
get { return _prenom;} //lecture
set { _prenom = value; } //écriture
}
• Dans le Main(), on écrit :
CPersonne P1 = new CPersonne("Casali", "alain",30);
Console.WriteLine(P1.prenom); // alain
P1.prenom = "bob";
P1.EcritConsole(); // Casali, bob, 30
Attention : une propriété ne peut pas avoir le même nom que l’attribut qu’elle
« gère ». On a donc préfixé le nom des attributs de la classe par le caractère ‘_’.
Les classes (7)
Attributs et méthodes statiques
Un champ statique d’une classe :
• existe indépendamment des objets de la classe (même si aucun objet n’a été crée);
• est partagé par tous les objets de la classe;
• peut être initialisé et manipulé dans une méthode de la classe comme n’importe
quel attribut.
• On veut compter le nombre de personnes que l’on crée
static public int _nbPersonne = 0;
• On modifie les constructeurs de la classe CPersonne pour prendre en compte le
nouvel attribut.
public CPersonne(string N, string P, int A)
{
this.Initialisation(N, P, A);
++_nbPersonne;
}
Les classes (7)
On accède à l’attribut static d’une classe par la notation préfixée
classe.attribut
Console.WriteLine(CPersonne._nbPersonne);
// 0
CPersonne P1 = new CPersonne("Casali", "alain", 30);
CPersonne P2 = new CPersonne(P1);
Console.WriteLine(CPersonne._nbPersonne);
// 2
CPersonne P3 = P2;
Console.WriteLine(CPersonne._nbPersonne);
//2 P3 n’est pas un objet, c’est juste une référence vers un objet !
Attention : contrairement au C++ un objet ne peut accéder à aucun attribut
static d’une classe (ex : P1._nbPersonne).
Les classes (7)
Les méthodes (non) statiques doivent respecter les règles suivantes :
• Une méthode statique n’a accès qu’aux attributs statique de la classe;
• Une méthode statique peut appeler une autre méthode statique mais ne peut pas
appeler une méthode non statique;
• Une méthode non statique a accès à tous les attributs de la classe (statiques ou
non).
• On va associé une propriété à la variable statique _nbPersonne (qui est privée
dans l’exemple)
static public int nbPersonne
{
get { return _nbPersonne; }
}
• Dans le Main(), on écrit :
CPersonne P1 = new CPersonne("Casali", "alain", 30);
CPersonne P2 = new CPersonne(P1);
Console.WriteLine(CPersonne.nbPersonne); //2
Les classes (8)
Passage d’objet à une fonction
Contrairement à ce que l’on pourrait penser, les objets sont passés par référence dans
des sous‐programmes et non par valeur (recopie) ! Seuls les types primitifs sont
passés par valeurs dans les fonctions et procédures.
• On modifie le prénom d’une CPersonne dans une autre classe :
static public void Modifie(CPersonne P)
{
P.prenom = "bob";
}
• Dans le Main(), on écrit :
CPersonne P1 = new CPersonne("Casali", "alain", 30);
P1.EcritConsole();
// Casali, alain, 30
Modifie(P1);
P1.EcritConsole();
// Casali, bob, 30
Les classes (9)
Tableaux d’objets
Puisqu’on peut créer des tableaux de type primitifs, on peut créer des tableaux
d’objets
En fait ce sont des tableaux de référence vers des objets
• Création d’un tableau de 3 personnes
CPersonne[] TabPers = new CPersonne[3];
Les 3 éléments du tableau sont initialisés avec valeur null : ils ne référencent
aucun objet
• Initialisation des objets du tableau
TabPers[0] = new CPersonne("Casali", "alain", 30);
TabPers[1] = new CPersonne("Casali", "bill", 30);
TabPers[2] = TabPers[1];
CPersonne.Modifie(TabPers[1]);
foreach (CPersonne P1 in TabPers)
P1.EcritConsole();
// Casali, alain, 30
// Casali, bill, 30 →bob
// Casali, bill, 30 →bob
Les classes (10)
Héritage
On va créer la classe CPersonneS qui hérite de la classe Cpersonne.
L’attribut _sexe est ajouté.
Les constructeurs de la classe aussi.
class CPersonneS : CPersonne
{
protected char _sexe;
public CPersonneS(string Nom, string Prenom,
int age, char sexe) : base(Nom, Prenom, age)
{
Appel au constructeur de la classe CPersonne
this._sexe = sexe;
}
public CPersonneS(CPersonne P, char sexe) : base(P)
{
_sexe = sexe;
}
}
Attention : contrairement au C++, l’héritage multiple n’existe pas en C#
Les classes (10)
Polymorphisme
Tous objets d’une classe fille ont accès aux fonctions de sa
classe mère (si les accès sont possibles, i.e. non privés)
• Dans le Main(), on écrit :
CPersonneS P1 = new CPersonneS ("Casali",
"alain", 30, 'M');
P1.EcritConsole(); // Casali, alain, 30
CPersonneS P2 = new CPersonneS(P1, ‘?');
P2.EcritConsole(); // Casali, alain, 30
Console.WriteLine(CPersonne.nbPersonne);
//2
Les classes (11)
Surcharge de fonctions / procédures
On veut écrire la fonction EcritConsole() pour la classe CPersonneS de
façon à pouvoir afficher le sexe d’un individu.
• Dans la classe CPersonneS, la fonction EcritConsole() doit être précédée
du mot clé new pour indiquer qu’on redéfini une nouvelle méthode pour la
classe CPersonneS.
• 2 surcharges sont alors possibles :
1. On écrit tout
public new void EcritConsole() {
Console.WriteLine (_nom + "," + _prenom + "," + _age
+ "," + _sexe);
}
2. On utilise la fonction EcritConsole() de la classe mère
public new void EcritConsole() {
base.EcritConsole();
Console.WriteLine("," + _sexe);
}
Les classes (11)
• Dans le Main(), on écrit :
CPersonneS P1 = new CPersonneS ("Casali",
"alain", 30, 'M');
P1.EcritConsole(); // Casali, alain, 30, M
CPersonneS P2 = new CPersonneS(P1, ‘?');
P2.EcritConsole(); // Casali, alain, 30, ?
Les classes (12)
Fonctions virtuelles
• Soit le programme suivant :
CPersonneS P1 = new CPersonneS ("Casali", "alain",
30, 'M');
P1.EcritConsole(); // Casali, alain, 30, M
CPersonne P2;
P2 = new CPersonneS(P1, '?');
P2.EcritConsole(); // Casali, alain, 30
• P2 a été déclaré de type CPersonne mais instancié comme une
CPersonneS. Lors de l’affichage, le programme considère que P2 est
une CPersonne et non une CPersonneS. Cette information ne
peut être connue lors de la compilation mais seulement au cours de
l’exécution du programme. Pour des raisons de performance, le
compilateur ne gère pas le code permettant de faire le transtypage
nécessaire.
• Pour résoudre ce problème, il faut utiliser des fonctions virtuelles.
Les classes (12)
Pour pouvoir tenir compte du véritable type de P2 au moment
de l’affichage, il faut :
1. Qualifier la fonction EcritConsole() de virtual
dans la classe de base (ici CPersonne);
public virtual void EcritConsole() {…}
2. Qualifier la fonction EcritConsole() de override
dans la classe dérivée (ici CPersonneS).
public override void EcritConsole() {…}
Remarque : on n’a plus besoin d’utiliser le mot clé new pour
la surcharge dans la classe fille.
En faisant ces modifications, l’affichage pour la variable P2 est
bien Casali, Alain, 30, ?
Les classes (13)
Redéfinition d’un opérateur pour une classe
• Opérateur unaire :
public static [type] operator NomOp (Classe variable1);
• Opérateur binaire :
public static [type] operator NomOp (Classe variable1,
type2 variable2);
• On va définir l’opérateur ++ qui ajoute 10 ans à une CPersonneS.
public static CPersonneS operator ++(CPersonneS P)
{
P._age += 10;
return P;
}
• Dans le Main(), on écrit :
CPersonneS P1 = new CPersonneS("Casali", "alain", 30,
'M');
++P1;
P1.EcritConsole(); // Casali, Alain, 40, M
Attention : les opérateurs == et != doivent être définis en même temps.
Les classes (14)
La classe object
Tous les objets C# dérivent de cette classe
Les méthodes de cette classe sont :
• public virtual bool Equals(object obj);
Détermine si deux instances de Object sont égales.
• public virtual int GetHashCode();
Sert de fonction de hachage pour un type particulier. GetHashCode
est approprié à une utilisation dans des algorithmes de hachage et
des structures de données telles qu'une table de hachage.
• public Type GetType();
Obtient le Type de l'instance en cours.
• public virtual string ToString();
Retourne un String qui représente l'Object en cours.
Les classes (14)
• Pour transformer un objet en un string afin de l’afficher à
l’écran, il faut écrire :
public override string ToString() {
return _nom + "," + _prenom + "," +
_age + "," + _sexe;
}
• Pour comparer deux objets, il faut :
1. Redéfinir la méthode Equals de la classe Object;
2. Redéfinir les opérateurs == et != ;
3. Redéfinir la fonction GetHashCode() de Object
Les classes (14)
On veut comparer deux objets de type CPersonneS (même nom et même prénom).
1. public override bool Equals(object obj) {
CPersonneS P = (CPersonneS)obj;
return _nom == P._nom && _prenom == P._prenom;
}
2. public static bool operator ==(CPersonneS P1,
CPersonneS P2) {
return P1.Equals(P2);
}
3. public override int GetHashCode() {
return this.ToString().GetHashCode();
}
• Dans le Main(), on écrit :
CPersonneS P1 = new CPersonneS("Casali", "alain", 30,
'M');
CPersonneS P2 = new CPersonneS("Casali", "alain", 40,
‘?');
if (P1 == P2)
Console.WriteLine("égalité");
Les Exceptions
• La plupart des fonctions C# gèrent les erreurs
aussi appelée exception.
• La classe mère qui gère les exceptions est la
classe Exception.
• Comme en C++, la gestion des exceptions se fait
sur le schéma
try { code }
catch (Exception e)
{traitement de l’exception}
[finally {bloc}]
instruction suivante
Les Exceptions
Exemple dans le cas d’un débordement de tableau
try
{
CPersonneS[] TabPers = new CPersonneS[3];
TabPers[0] = new CPersonneS("casali", "alain", 30, 'M');
TabPers[10] = TabPers[0];
ListeDePersonne.Insert(1, P);
Remarque : la valeur de index doit être inférieure au
nombre total d’éléments.
• Pour supprimer tous les éléments du vecteur, on
utilise la fonction clear().
ListeDePersonne.Clear();
Les vecteurs
• Pour la recherche à partir du début, on utilise les fonctions publiques
virtuelles :
int IndexOf (object o)
int IndexOf (object o, int posdeb)
int IndexOf (object o, int posdeb,
int combien)
Recherche de la première occurrence de l’élément o dans le
vecteur entre les positions posdeb et posfin. Renvoie ‐1
si l’élément est absent.
int i = ListeDePersonne.IndexOf(P1,1); //3
i = ListeDePersonne.IndexOf(P); // fait planter le
programme : pas de surcharge de Equals pour la
classe Cpersonne.
Les vecteurs
• On supprime un objet du vecteur avec la fonction
Remove() de profil :
public virtual void Remove ( Object
obj )
ListeDePersonne.Remove(P1); // OK
ListeDePersonne.Remove(P); //plante
• On supprime l’objet à la i° position avec la fonction
RemoveAt() de profil :
public virtual void RemoveAt ( int
index )
ListeDePersonne.RemoveAt(0);
Les vecteurs
• Pour copier un vecteur dans un tableau (pas dans un
vecteur), il faut utiliser une des fonctions CopyTo() de
profil :
public virtual void CopyTo ( Array
array )
public virtual void CopyTo ( Array
array, int arrayIndex )
TailleChaine = (int)BR.ReadInt32();
buf = BR.ReadBytes(TailleChaine);
string prenom = enc.GetString(buf);