Vous êtes sur la page 1sur 49

Partie 

3 : Programmation réseau 
« avancée » en C#
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
La socket vue comme un flux
• On souhaite manipuler des flux binaires (BinaryWriter
ou BinaryReader) afin de pouvoir envoyer autre chose 
que des tableaux de bytes.
• Les deux constructeurs prennent en paramètre des objets 
de la classe Stream. Les FileStream qui 
permettent de manipuler les fichier binaires sont dérivées 
de la classe Stream. Les deux constructeurs de ces 
classe sont :
public BinaryWriter ( Stream output )
public BinaryReader ( Stream input )
• On va utiliser la classe NetworkStream pour transformer 
la socket en flux (stream). Cette classe dérive de la classe 
Stream.
La socket vue comme un flux
• Les constructeurs usuels de la classe NetworkStream sont :
– public NetworkStream ( Socket socket )
– public NetworkStream ( Socket socket,
FileAccess access )
• Comme pour les fichiers, FileAccess peut avoir les valeurs
suivantes :
– Read : Accès en lecture à la socket. Les données peuvent être lues à 
partir de cette socket. 
– Write : Accès en écriture à la socket. Les données peuvent être écrites 
dans cette socket.
– ReadWrite : Accès en lecture et en écriture à la socket. Les données 
peuvent être écrites dans la socket et lues à partir de celle‐ci.
• Comme pour les fichiers, on peut utiliser la classe 
NetworkStream pour lire et écrire sur la socket, cependant 
nous n’allons pas le faire !
La socket vue comme un flux
• On veut envoyer depuis le serveur un objet de type 
CPersonne sur une socket et le reconstituer dans le 
client.
• La sérialisation ne marche pas telle qu’elle a été 
proposée précédemment. Il faudrait utiliser le 
protocole SOAP et avoir des notions en XML pour 
obtenir un résultat similaire.
• On va être obligé :
1. Dans le serveur, de décomposer chaque donnée membre 
de l’objet CPersonne et de l’envoyer sur la socket
2. Dans le client, de recevoir chaque donnée membre 
séparément, puis de reconstruire l’objet CPersonne.
La socket vue comme un flux
• Code du serveur
//on a accès à la socket du client (SocClient)
NetworkStream NS = new NetworkStream(SocClient
/*,FileAccess.Write */);
BinaryWriter BW = new BinaryWriter(NS);
CPersonne P = new CPersonne("Casali", "alain", 30);
BW.Write((string)P._nom);
BW.Write((string)P.prenom);
BW.Write((int)P._age);
BW.Close();
NS.Close();
SocClient.Close();
• Si le serveur a besoin de lire des données sur la socket, on peut créer un 
objet de type BinaryReader.
La socket vue comme un flux
• Code du client
//le client s’est déjà connecté au serveur sur la socket
soc
NetworkStream NS = new NetworkStream(Soc
/*,FileAccess.Read */);
BinaryReader BR = new BinaryReader(NS);
string nom = BR.ReadString();
string prenom = BR.ReadString();
int age = BR.ReadInt32();
CPersonne P = new CPersonne(nom, prenom, age);
Console.WriteLine(P); // Casali, alain, 30
BR.Close();
NS.Close();
Soc.Close();
• Si le client a besoin d’écrire des données sur la socket, on peut créer un objet de 
type BinaryWriter.
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
Les threads
• En bon français : processus léger aussi appelé fil 
d'exécution
• Du point de vue de l'utilisateur, ces exécutions semblent se 
dérouler en parallèle. 
• Chaque processus possède sa propre mémoire virtuelle 
• Les processus légers appartenant au même processus père 
se partagent sa mémoire virtuelle. 
• Tous les processus légers possèdent leur propre pile 
système. 
• Principales utilisations : 
1. lancer un calcul tout en ayant la main dans le programme
2. Interface graphique
Les threads
• Pour utiliser la classe Thread, il faut inclure 
l’espace de nom System.Threading.
• Constructeur (C#1.1) :
public Thread ( ThreadStart Nom
)
Nom : Délégué ThreadStart (pointeur de 
fonction vers une fonction static void
Nom()) qui représente la méthode à appeler 
au début de l'exécution de ce thread. 
Les threads
Propriétés de la classe Thread :
• public static Thread CurrentThread {
get; }
Obtient le thread en cours d'exécution. 
• public bool IsAlive { get; }
Obtient une valeur indiquant l'état de l'exécution du thread 
en cours. 
• public bool IsBackground { get; set; }
Obtient ou définit une valeur indiquant si le thread est ou 
non un thread d'arrière‐plan. 
• public string Name { get; set; }
Obtient ou définit le nom du thread. 
Les threads
Méthodes de la classe thread
• public void Start ()
lance l'exécution d'un thread
• public void Abort ()
arrête définitivement l'exécution d'un thread
• public static void Sleep ( int
millisecondsTimeout )
Suspend le thread en cours pendant une durée 
spécifiée. 
• public void Join ()
Bloque le thread d'appel jusqu'à ce qu'un thread 
s'arrête
Les threads
public static void affiche() {
Console.Out.WriteLine("Début d'exécution de la méthode affiche dans
le Thread " + Thread.CurrentThread.Name);
Thread.Sleep(1000);
Console.Out.WriteLine("Fin d'exécution de la méthode affiche dans le
Thread " + Thread.CurrentThread.Name);
}

public static void Main() {


Thread T1 = new Thread(new ThreadStart (affiche));
Thread T2 = new Thread(new ThreadStart(affiche));
T1.Start();
T2.Name = "2eme";
T2.Start();
Console.Out.WriteLine("On attend la fin des 2 thread");
T1.Join();
T2.Join();
Console.Out.WriteLine("Pgm fini");
}
Les threads
• De quoi est on sûr par rapport au programme 
précédent ?
1. On n’a pas fixé de nom au 1° thread, il est donc 
vide.
2. Pour chaque thread, la 1° instruction sera 
affichée avant la 2°.
3. Le dernier affichage écran est "Pgm fini"
• Par contre, on ne peut pas deviner l’ordre d’ 
exécution des threads (qui aura la main sur le 
processeur, quand et pendant combien de 
temps).
Les threads
• Comment le thread peut il accéder à des variables de la fonction Main()? 
En créant une donnée membre de la classe courante.
public static int i ;
public static void Main() {
i = 1;
Thread T1 = new Thread(new ThreadStart (affiche));
Thread T2 = new Thread(new ThreadStart(affiche));
T1.Start();
++i;
T2.Start();
Console.Out.WriteLine("On attend la fin des 2
thread");
Console.Out.WriteLine("Pgm fini");
}
Les threads
public static void affiche() {
Console.Out.WriteLine("Début du " + i + "eme thread");
Thread.Sleep(1000);
Console.Out.WriteLine("Fin du " + i + "eme thread");
}

• Attention au résultat obtenu : le 1° thread peut 
prendre la main au niveau du processeur alors que la 
variable i vaut déjà 2 /
• Cependant, cette solution est valable si on ne modifie 
pas la valeur de la donnée membre ni dans le thread, ni 
dans le programme lançant le thread (ici Main()).
Les threads
Autre solution : utiliser les threads paramétrés (C# 2.0).
Constructeur :
public Thread ( ParameterizedThreadStart
Nom )
Nom : Délégué ParameterizedThreadStart
(pointeur de fonction vers une fonction static void
Nom(Object o)) qui représente la méthode à appeler 
au début de l'exécution de ce thread. Cette fois ci, le 
délégué prend comme unique paramètre un objet (et rien 
d’autre).
La méthode Start() change de profil en conséquence :
public void Start ( Object parameter )
Les threads
L’exemple précédant revisité :
public static void affiche(object o) {
Console.Out.WriteLine("Début du " + o + "eme thread");
Thread.Sleep(1000);
Console.Out.WriteLine("Fin du " + o + "eme thread");
}
public static void Main() {
int i = 1;
Thread T1 = new Thread(new ParameterizedThreadStart
(affiche));
T1.Start(i);
Thread T2 = new Thread(new
ParameterizedThreadStart(affiche));
T2.Start(++i);
Console.Out.WriteLine("Pgm fini");
}
Les threads
• Pour les sections critiques, il faut utiliser un 
objet de la classe Mutex
– Constructeur : public Mutex ()
– Espace de nom : System.Threading
– public void ReleaseMutex () : Libère 
une fois Mutex. (similaire à V) 
– public virtual bool WaitOne () : 
bloque le thread en cours  (similaire à P)
NB : cette fonction est déjà surchargée pour la 
classe Mutex.
Les threads
Avec l’exemple précédent, on veut que l’affichage se fasse « d’un seul bloc »
public static Mutex Mu ;
public static void Main() {
Mu = new Mutex();

}
public static void affiche(object o) {
Mu.WaitOne();
Console.Out.WriteLine("Début du " + o + "eme
thread");
Console.Out.WriteLine("Fin du " + o + "eme
thread");
Mu.ReleaseMutex();
}
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
Serveur Multi‐threadé
• Dans le cas d’un serveur itératif, le serveur ne 
peut délivré qu’un unique service à la fois (sauf si 
on utilise la fonction Select()).
• On va confié la partie dialogue avec le client à un 
thread afin de pouvoir servir plusieurs clients en 
même temps. 
• Le code du processus client ne change pas !
• On suppose qu’on dispose d’une classe CClient
qui contient une méthode statique appelée 
PgmClient qui contient le protocole de 
communication entre le serveur et le client.
Serveur Multi‐threadé
Code du serveur
Socket Soc = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint IPMonServeur = …;
Soc.Bind(IPMonServeur);
Soc.Listen(10);
while (true)
{
Socket SocClient = Soc.Accept();
Thread ThreadClient = new Thread(new
ParameterizedThreadStart(CClient.PgmClient));
ThreadClient.Start(SocClient);
}
Soc.Close();
Serveur Multi‐threadé
La classe CClient
public static void PgmClient(Object SocClient) {
if (SocClient.GetType().Name == "Socket") {
Socket Soc = (Socket)SocClient;
NetworkStream NS = new NetworkStream(Soc);
BinaryWriter BW = new BinaryWriter(NS);
CPersonne P = new CPersonne("Casali",
"alain", 30);
BW.Write((string)P._nom);
BW.Write((string)P.prenom);
BW.Write((int)P._age);
BW.Close();
NS.Close();
Soc.Close();
}
}
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
Serveur « pré‐threadé »

• Serveur concurrent : 1 thread (esclave) par connexion demandée. 
– Serveur facile à mettre en place
– Lent : duplication de la table des pages
• Idée : créer un pool d’esclaves pouvant satisfaire plusieurs demandes 
avant que celles‐ci ne soient formulées.
• Les threads ne meurent pas après avoir rendu leur service : ils attendent 
qu’une autre demande leur parviennent

Gain de temps pour satisfaire la demande du client


Serveur « pré‐threadé »

• Répartition équitable de l’activité entre les esclaves
serveur doit être informé de l'activité de ses fils (dispo/occupé : IsAlive)
serveur surveille les ressources consommées par ses fils
serveur doit répartir l'activité (nécessité de communiquer entre les processus)
• Gestion des accidents : quand un fils meurt, le serveur doit le relancé au 
plus vite
• Gestion dynamique du pool d’esclaves  :
– Pic de charge : tous les esclaves occupés; le serveur doit en augmenter le 
nombre (dans la limite du paramètre de Accept())
– Calme : faible taux d’utilisation des esclaves : le serveur doit en diminuer le 
nombre.
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
La classe TcpClient
• Classe permettant de représenter un client TCP / 
IP.
• Espace de nom : System.Net.Sockets
• Trois constructeurs :
1. public TcpClient ( IPEndPoint
localEP )
2. public TcpClient ( IPAddress
localaddr, int port )
3. public TcpClient ()
Attention : les deux premiers constructeurs font un 
appel sous‐jacent à la fonction Bind() !!
La classe TcpClient
Méthodes de la classe TcpClient
• public void Connect ( IPEndPoint remoteEP )
public void Connect ( IPAddress address, int port
)
Connecte le client à un hôte TCP distant en utilisant le point de 
terminaison réseau distant (l'adresse IP et le numéro de port) spécifié(s).
• public NetworkStream GetStream ()
Retourne le NetworkStream utilisé pour l'envoi et la réception de 
données. 
• public void Close ()
Supprime cette instance de TcpClient sans fermer la connexion sous‐
jacente (la connexion TCP). L'appel à cette méthode ne libère pas le 
NetworkStream utilisé pour envoyer et recevoir des données. Vous devez 
appeler la méthode Close du NetworkStream pour fermer le flux et la 
connexion TCP.
La classe TcpClient
Code générique d’un TcpClient
IPEndPoint IPServeur = …;
TcpClient Client = new TcpClient();
Client.Connect(IPServeur);
NetworkStream NS = Client.GetStream();
BinaryReader BR = new
BinaryReader(NS);

BR.Close();
NS.Close();
Client.Close();
La classe TcpListener
• Classe permettant de représenter un serveur 
TCP / IP.
• Espace de nom : System.Net.Sockets
• Deux constructeurs :
1. public TcpListener ( IPEndPoint localEP )
2. public TcpListener ( IPAddress localaddr, int port )
La classe TcpListener
Méthodes de la classe TcpListener
• public void Start ()
public void Start ( int backlog )
Lance l'écoute des demandes de connexion 
entrantes avec un nombre maximal de connexions 
en attente. 
• public TcpClient AcceptTcpClient()
Accepte une demande de connexion en attente 
• public void Stop ()
Ferme l'écouteur. 
La classe TcpListener
Code générique d’un TcpListener
IPEndPoint IPServeur = …;
TcpListener serveur = new TcpListener(IPServeur);
serveur.Start();
while (true) {
TcpClient Client = serveur.AcceptTcpClient();
NetworkStream NS = Client.GetStream();
BinaryWriter BW = new BinaryWriter(NS);

BW.Close();
NS.Close();
Client.Close();
}
Serveur.Close();
La classe TcpListener
Code générique d’un TcpListener Multi‐threadé
1. Création d’une fonction contenant le protocole de communication.
static void pgmClient(object MonClient) {
if (MonClient.GetType().Name == "TcpClient")
{
TcpClient Client = (TcpClient)MonClient;

}
}
2. On modifie la boucle while() dans le code du serveur
while (true) {
TcpClient Client = serveur.AcceptTcpClient();
Thread ThClient = new Thread( new
ParameterizedThreadStart (pgmClient));
ThClient.Start(Client);
}
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
Démon xinetd

• Chaque service réseau est programmé comme un daemon avec 1 ou 
plusieurs sockets, surveillant son ou ses ports de communication. C’est ce 
qu’on appelle  un fonctionnement « stand alone »
• C’était le cas avant BSD 4.3
• On doit maintenir dans la mémoire primaire (RAM) ou secondaire (zone 
de « swap »)  beaucoup de processus inutiles à instant donné, juste au cas 
où … 

Consommation importante des ressources systèmes d’autant que


le nombre de services augmente
Tous les serveurs effectuent les mêmes séquences d’instructions
au démarrage, seuls changent les protocoles liés à l’application
(ce que chaque serveur doit faire).
Démon xinetd (2)

• Apparu dans BSD 4.3
• xinetd est un serveur de serveurs 
• Il est utilisable par tout serveur TCP / UDP
• Il fournit essentiellement 2 services : 
1. Un unique processus attend les multiples demandes de connexion. Le 
nombre de processus en attente est donc réduit.
2. Il simplifie l’écriture des serveurs car il prend en charge la connexion. Les 
serveurs lisent les demandent sur leur entrée standard et répondent sur 
leur sortie standard.

Serveur concurrent mutli-services, multi-protocoles

Fichier de configuration : /etc/xinetd.conf


Fonctionnement simplifié (1)
socket()

bind() Pour chaque service


trouvé dans
listen () /etc/xinetd.conf
(socket TCP)

select ()

accept ()
(socket TCP)

fork ()
père
fils
close ()
(socket TCP)
Fonctionnement simplifié (2)
close ()
Descr. autre que la socket

dup()
Socket vers 0,1,2
close(socket)

setgid()
setuid()
(si non root)

exec()
(du serveur)
Démon xinetd (3)

]$ ps alx | grep xinetd


0 2470 1 25 0 2564 768 select Ss

Pid ppid select() sleeping

-stayalive
Tourne même si aucun service n’est spécifié
]$ more /etc/xinetd.d/sshd-xinetd
# default: off
# description: sshd server, xinetd version. \
# Don't run the standalone version if you run \
# this.
service ssh Type de service à employer /etc/services
{
disable = yes
socket_type = stream
wait = no Serveur itératif
user = root
server = /usr/sbin/sshd
server_args = -i
}
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
Serveur stateless (1)

• Stateless : se rapporte à un système ou à un protocole qui ne garde pas 
un état persistant entre les transactions. 
• Avantages : 
1. Serveur simple à mettre à place
2. Pas de pertes de ressources pour mémoriser l’avancement
3. Serveur plus robuste :
• perte de connexion ne laisse pas des données dans un état invalide (fichier par
ex.)
• redémarrage du serveur ne perd pas l'information d'état
• redémarrage du client ne trouble pas le serveur
• Inconvénients :
1. requêtes comportent plus d'information
2. serveur doit interpréter information supplémentaire à chaque requête   ⇒
plus long
Serveur stateless (2)

• Exemple de serveur stateless : web
– Requête complète dans l’URL
– Aucune mémorisation des précédentes requêtes
• Inconvénients :
– Les pages précédentes/suivantes doivent être codée « en dur »
– E‐commerce :
• Base de données mémorisant les informations
• Cookies
• Encapsulation de clés dans l’url
ce n’est pas le serveur http qui le fait !!!
Serveur statefull

• Il y a mémorisation des précédentes transactions
• Exemple de serveur statefull : serveur ftp
– Requête de transferts de fichier :
• Client identifié
• Répertoire courrant mémorisé
• Mode de transfert (ASCII / binaire) identifié
• Crash serveur statefull : nécessite un dialogue avec le client pour restaurer 
l’état courrant
Plan du cours
• La socket vue comme un flux
• Les threads
• Serveur Multi‐threadé
• Serveur « pré‐threadé »
• Classes facilitant la programmation réseau TCP 
/IP en C#
• Démon xinetd
• Serveur stateless
• Le projet réseau
Projet Réseau
Objectif : réaliser une application de sauvegarde / 
restauration (ou de synchronisation) de document(s) 
(fichier(s))

Votre client (au moins) devra permettre : 
• l'enregistrement sur le serveur;
• la modification du mot de passe;
• la connexion au serveur;
• l'envoi / la réception de fichiers;
• la spécification des fichiers (répertoires / sous‐
répertoires) à sauvegarder.
Projet Réseau
Votre serveur devra (au moins) :
permettre l'autentification d'un client via le 
SGBD MySQL;
• autoriser la connexion si l'identifiant et le mot 
de passe du client sont correctes;
• permettre la modification d'un mot de passe;
• permettre l'envoi / la réception de fichier
• stocker les fichiers reçus dans une 
arborescence appropriée.
Projet Réseau
Dates importantes :
• 19 décembre 2008 : constitution des groupes 
de projets. 
• 19 janvier 2009 : avant projet à rendre
• 9 mars 2009 : projet à rendre
• 16 mars 2009 ‐ 28 mars 2009 : présentation 
du projet

Vous aimerez peut-être aussi