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);
}
• 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
• 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ù …
• 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.
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)
-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