Cours Qspa

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 31

Propriétés Description

Intitulé du Questions spéciales de Programmation avancée


cours

Formation Informatique de gestion


concernée

Présentation Développement d’un logiciel de messagerie instantanée. La


communication est basée sur les sockets entre deux applications
dotnet.

Notions Programmation à l’aide d’objets


Développement orienté réseau

Transversalité Les protocoles réseau

Pré-requis Le langage VB.Net et C#, les applications WinForms.

Outils Les applications fournies ont été réalisées avec Visual Studio 2012
et sont destinées au framework dotnet 3.5, elles peuvent
facilement être recompilées avec un autre outil et/ou cibler une
autre version du framework.

Mots-clés VB, C # , DotNet

Vol Horaire 45 heures

Auteur Ir. Théophile KITAMBALA


OBJECTIFS DU COURS
 Ceci n'est pas un cours de réseau
 C'est un cours de programmation réseau
 Comprendre les mécanismes réseau
 Communication
 Codage de l'information
 Apprendre à programmer en C et en Java
 Programmer des clients d'application déjà existantes
 Client pour serveur d'envoi de mails
 Client pour serveur web (qui fait ce que fait un navigateur)
 Développer une application réseau
À quelles questions ce cours répond
 Comment deux machines peuvent-elles communiquer ?
 Comment connaître la machine avec laquelle on souhaite communiquer ?
 Communiquer pour quoi faire ?
 Quelles sont les différentes façons de communiquer ?
 Quelles informations envoyer ?
 Comment recevoir l'information envoyée ?
CHAPITRE 1. GENERALITES SUR LES RESEAUX

Comment communiquer
Il faut envoyer de l'information
Cette information passe d'une application vers une autre
– Par exemple :
Un navigateur envoie une requête vers un serveur web. En pratique l'information qui
circule est codée en octet.
Cette information peut–être perdue ou erronée. L'ordre des messages peut être changé
Allons-nous gérer la perte des messages, le fait que des 'bits' d'information peuvent

être changés ? NON

Transmission non fiable de données


 Comment peut-on garantir une certaine fiabilité dans l'information envoyée ?
Le service de base envoie des données binaires (sous forme de paquets d'octets).
Les paquets peuvent être perdus ou dégradés

0001110000100001
Un système de couches
 Comment gère-t-on les pertes d'information ?
 Mécanisme de détection de pertes
 Réémission de messages
 Comment gère-t-on la dégradation
 Redondance de l'information
 Ajout de bits permettant de savoir les parties du paquet erronées
 (cf code correcteur d'erreurs)
 Ces mécanismes existent et nous n'avons pas à les programmer
 Comment passe-t-on d'un service avec paquets non fiables à un service fiable ?
 superposition de couches logicielles chacune avec une mission spécifique
MODELE DE REFERENCE
1. Modèle ISO/OSI (Open System Interconnection) en 7 couches

 Les couches matérielles


 Couche Physique
Rôle : Transmission des données sous forme binaire ● Protocoles : Fibre, câble radio,
etc.
 Couche Liaison
Rôle : Gère la communication entre machines, adresse physique MAC ● Protocoles :
Ethernet, Wifi, ...
 Couche Réseau
Rôle : Détermine le parcours des données et l'adressage logique (adresse IP)
Protocoles : IPv4, IPv6, ...
 Les couches segments et données
 Couche Transport
Rôle : Connexion bout à bout, contrôle de flux
Protocoles : TCP, UDP
 Couche Session
Rôle : Communication points à point
Protocoles : TLS
 Couche Présentation
Rôle : Chiffrement et déchiffrement des donnés
Protocoles : SSL, WEP
 Couche Application
Rôle : Point d'accès aux services réseaux
Protocoles : SMTP, IRC, HTTP, SSH
2. MODELE INTERNET
Modèle plus simple à 4 couches
 Liaison
Protocoles : ARP, Ethernet
 Internet
Protocoles : IPv4, IPv6
 Transport
Protocoles : TCP, UDP
 Application
Protocoles : FTP, HTTP, IMAP, POP, SMTP
Ce qui va nous intéresser
 Développer des applications ou services
 Ce que nous développerons se situera à la couche Application.
Nous utiliserons les protocoles de la couche Transport.
 Quels protocoles sous-jacents allons-nous utiliser :
 TCP (modèle par flux)
 UDP (modèle par paquet)
Pour utiliser ces services, nécessité de savoir le nom ou l'adresse des machines
(adresse IPv4, IPv6).
RESUME GRAPHIQUE

Les services en réseau


o La couche transport nous fournit des services de communication
o Envoi de données
o Réception de données
o Connexion à une machine
o Pour communiquer ces machines doivent se connaître
o Mécanisme de nommage des machines
o Comment identifier la machine
o elle est identifiée par une adresse
o Comment trouver une application ou un service sur une machine
o Chaque service est identifiée par un port
SUR UNE MACHINE

SUR LE RESEAU

Informations pour communiquer


Un couple (adresse, port) est un point de communication
Pour communiquer il faut deux points de communication
1. (adresse1,port1) d'un côté
2. (adresse2, port2) de l'autre côté
Exemple :
o Quand on fait http://www.google.com dans le navigateur
o Connexion à une des machines correspondant à www.google.com
o Sur le port 80 qui correspond au service http
o Dans ce cas, notre port de sortie n'est pas important ni notre adresse
o Souvent quand on fera une architecture client-serveur, notre port sera attribué
à une valeur automatique
COMMUNICATION ENTRE DEUX MACHINES

o Du côté du client, un port et une adresse


o Du côté du serveur
o Pour certaines applications, le 'programmeur' n'a pas besoin de connaître le
port côté client
o Connaître le port côté serveur est nécessaire pour se connecter
IDENTIFICATION D'UNE MACHINE
o Par un nom internet (pas forcément nécessaire)
o Par exemple : www.google.com, www.informatique.univ-paris-diderot.fr
o Une machine peut posséder plusieurs noms
o Un nom peut correspondre à plusieurs machines
o Par une adresse internet (obligatoire pour toute machine sur le réseau)
o en fait, adresse d'un dispositif réseau sur une machine
o donc plusieurs adresses possibles pour une machine
o une adresse par dispositif
o plusieurs dispositifs par machine
o Adresse (organisation structurelle) -> mieux pour les machines
o Nom (organisation logique) -> mieux pour les humains
Question ?
C’est quoi alors l’adresse de www.google.com ?
Il y en a plusieurs, par exemple : 173.194.40.144
On verra :
o comment trouver un nom à partir d'une adresse
o comment trouver une adresse à partir d'un nom
Comment marchent les noms ?
Un nom représente une structure hiérarchique
Par exemple : www.informatique.univ-paris-diderot.fr
o la machine www
o dans le sous-domaine informatique
o qui est lui-même dans le sous-domaine univ-paris-diderot
o qui est dans le domaine fr
Deux parties dans le nom :

www.eduquepsp.cd
Machine Domaine
À PROPOS DES DOMAINES
Le nom de domaine caractérise la hiérarchie des responsabilités
Par exemple : l'ufr d'informatique de l'université Paris Diderot située dans le domaine
français.
Le domaine le plus à droite est appelé domaine de premier niveau (top level domain)
On distingue en gros deux types :
o Génériques (par exemple : .com, .edu, …)
o Nationaux (par exemple : .fr, .tz, ...)
ANALYSE D'UN NOM
Pour www.informatique.univ-paris-diderot.fr
o fr est le domaine national attribué par l'ICANN (Internet Corporation for
(Assigned Names and Numbers) à la France avec délégation à l'AFNIC
(Association Française pour le Nommage Internet en Coopération)
o univ-paris-diderot est le sous-domaine attribué par l'AFNIC à l'université Paris
Diderot avec délégation à la DSI de l'université informatique est le sous-
domaine attribué par la DSI à l'UFR d'informatique avec délégation au service
informatique de l'UFR
o www est le nom d'une des machines sous la responsabilité de l'UFR
d'informatique
À PROPOS DES ADRESSES
Exemple d'adresse pour www.informatique.univ-paris-diderot.fr : 194.254.199.98
Les adresses aussi sont structurées. La structure des adresses est un reflet de la
structure physique du réseau (tout du moins en théorie).
Dans ce cours nous ne nous intéresserons pas à la structure des adresses MAIS nous
utiliserons les adresses (et pas seulement les noms).
ATTENTION : les adresses correspondent à des machines et la plupart du temps pas
à des domaines.
LES ADRESSES IPV4
Elles sont codées sur 4 octets (donc 32 bits).
Par exemple : 173.194.66.106
Actuellement encore les plus utilisées. Certaines adresses IP sont réservées à un usage
particulier :
o 127.0.0.1 : adresses pour l'hôte local localhost (en fait adresses comprises entre
127.0.0.1 et 127.255.255.255)
o 192.168.0.0/16 : adresses privées
o 224.0.0.4 : adresses pour la multidiffusion
o 255.255.255.255 : adresse de diffusion
Les adresses privées ne sont pas routées par Internet
LES ADRESSES IPV6
Elle sont codées sur 16 octets (donc 128 bits).
Par exemple : 2a00:1450:400c:c02:0:0:0:93
On les écrit habituellement comme 8 groupes de deux octets. Chaque octet est écrit en
hexadécimal (valeur allant de 0 à F).
On supprime parfois les 0 consécutifs par :
L'exemple précédent devient : 2a00:1450:400c:c02::93
Comme pour IPv4, certaines adresses IP sont réservées à un usage particulier. Il
n'existe pas de correspondance automatique entre adresses IPv4 et IPv6.
Les réseaux IPv4 et IPv6 cohabitent.
LIENS ENTRE NOMS ET ADRESSE
Service de nom : service permettant de faire la traduction d'un nom en une adresse. Il
faut penser à un annuaire.
Le système le plus répandu aujourd'hui est le DNS (Domain Name Service).
Il s'agit d'un annuaire distribué (il y a donc plusieurs serveurs DNS). Le DNS contient
aussi d'autres informations lié à un nom de domaine.
Exemple d'informations fournies par le DNS :
o L'adresse IPV4 (A record)
o L'adresse IPv6 (AAAA record)
o Les serveurs de courrier électronique pour le domaine (MX record)
o Les serveurs DNS de ce domaine (NS record)
INTERROGATION DES SERVICES DE NOM
Différentes commandes LINUX :
 host
 nslookup (souvent considérée comme obsolète)
 dig
Commande pour connaître le nom de sa machine :
 hostname
Ces commandes peuvent être utilisées pour connaître l'adresse IP à partir d'un nom
Parfois aussi pour connaître le nom à partir d'une adresse IP (peut être plus difficile à
utiliser/comprendre).
CHAPITRE 2. PROGRAMMATION RESEAU (DISTRIBUEE)
I. ARCHITECTURE CLIENT/SERVEUR
Mode de communication qu’un hôte établit avec un autre hôte qui fournit un service
quelconque :
Serveur Client

Réponse

Opération Application
Envoie d’une requête

Fonctionnement
- identification de la machine qui abrite le serveur par le client
- identification du serveur sur la machine
- canal de communication entre le serveur et le client
- construction de la trame réseau et échange du protocole d’application
Une application client/serveur a besoin de :
- trouver l’adresse du serveur
- demander un service spécifique
- faire la requête
- obtenir une réponse
Mots clés de l’architecture
- client : entité qui fait l’appel
- sockets : moyen de communication entre ordinateurs
- adresses IP : adresse d’un ordinateur
- serveur : entité qui prend en charge la requête
- serveur de noms (DNS ; LDAP) : correspondances entre noms logiques et adresses
IP.
- Port : canal dédié à un service
- Protocole : langage utilisé par 2 ordinateurs pour communiquer entre eux
- Adresse internet : est attribuée à chaque nœud du réseau, est une série d’octets dont
la valeur dépend du type de réseau et est associée à un nom logique (Domain Name
Server).
Chaque hôte possède environ 65535 ports.
Ports réservés
- 1 à 1024 services fondamentaux (administrateurs). Comme par exemple : 80 pour
le http, 25 pour le service SMTP
- TCP : implique une file d’attente par connexion
Serveur FTP : 21
Serveur Telnet : 23
Serveur SMTP : 25
- UDP : implique une file d’attente unique pour le port
Agent SNMP : 161
Logger SNMP : 162
- 1025 à 5000 disponibles pour les utilisateurs.

II. PROGRAMMATION SOCKET


Une porte à travers laquelle l’application peut à la fois envoyer et recevoir des
messages d’une autre application. Outil de communication pour échanger des
données entre un client et un serveur et ce sont des canaux de communication
(descripteur d’entrée / sortie dans lesquels on écrit et sur lesquels on lit).
Deux types de transports doivent être utilisés pour construire des applications
Client/serveur via socket :
- Datagramme (non reliable)
- Orienté flux d’octets (reliable)
Un socket : une entrée sortie dédiée au réseau (gestion similaire des entrées sorties
standard (écran, clavier) et des fichiers.
L’exécution d’une application utilisant les sockets à partir d’un disque réseau peut
poser des problèmes de sécurité. Si vous obtenez une exception concernant la sécurité,
configurez les paramètres de sécurité du Framework. Vous pouvez consulter la page
http://msdn.microsoft.com/frfr/library/2bc0cxhc.aspx pour obtenir des informations
concernant l’outil .NET Framework Configuration (Mscorcfg.msc).
A. PRESENTATION
i. Les sockets
Les sockets fournissent un mécanisme générique de communication entre les
processus. Elles sont apparues pour la première fois en 1986 dans la version UNIX de
l’université de Berkeley.
Un processus peut être sommairement défini comme une application (ou un service)
en cours d’exécution.
Un socket permet à un processus (le client) d’envoyer un message à un autre processus
(le serveur). Le serveur qui reçoit ce message peut alors accomplir toutes sortes de
tâche et éventuellement retourner un résultat au processus client.
Lors de la création d’un socket, il faut préciser le type d’adressage, le type de message
et le protocole transport utilisés. Nous utiliserons : IPV4, les datagrammes simples et
le protocole UDP.
ii. Point de terminaison
Un point de terminaison (EndPoint) est défini par une adresse IP et un numéro de port.
Une communication s’établit entre deux points de terminaison.
Un processus serveur reçoit les messages destinés à un numéro de port et à un
protocole transport (UDP, TCP, ...) déterminés. Un client désirant envoyer un message
à un serveur doit donc créer un point de terminaison représentant le récepteur du
message en fournissant l’adresse IP du serveur et le numéro de port écouté.
Pour envoyer un message, un processus doit également créer un point de terminaison
représentant l’émetteur du message en fournissant sa propre adresse IP. Il ne fournit
pas de numéro de port, c’est le système qui attribuera un numéro de port libre pour
la communication.
iii. Principe de communication
Lors de l’envoi d’un message, il faut :
- Créer un socket,
- Créer le point de terminaison émetteur,
- Lier le socket au point de terminaison émetteur (ce qui précisera le protocole
transport utilisé pour l’émission).
- Créer le point de terminaison récepteur,
- Envoyer le message à l’aide du socket vers le point de terminaison récepteur.
Pour recevoir un message, le processus serveur doit :
- Créer un socket,
- Créer le point de terminaison récepteur (lui-même donc),
- Lier le socket au point de terminaison récepteur (ce qui précisera le protocole
transport utilisé pour la réception).
- Créer le point de terminaison émetteur (sans préciser l’adresse IP ni le numéro
de port puisqu’ils ne sont pas connus à ce stade).
- Mettre le socket en état de réception en lui fournissant un buffer pour les
données à recevoir, et une référence au point de terminaison émetteur.
- Lorsque le message est effectivement reçu, le point de terminaison émetteur est
renseigné.
Premier exemple (exemple01)
Le client :

public Fm_client()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
adrIpLocale = getAdrIpLocaleV4();
} private IPAddress adrIpLocale;
private IPAddress getAdrIpLocaleV4()
{
string hote = Dns.GetHostName();
IPHostEntry ipLocales = Dns.GetHostEntry(hote);
foreach (IPAddress ip in ipLocales.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip;
}
}
return null; // aucune adresse IP V4
}
private void bt_envoyer_Click(object sender, EventArgs e)
{
byte[] message;
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint epEmetteur = new IPEndPoint(adrIpLocale, 0);
sock.Bind(epEmetteur);
IPEndPoint epRecepteur = new IPEndPoint(
IPAddress.Parse(tb_ipDestinataire.Text),33000);
message = Encoding.Unicode.GetBytes(tb_message.Text);
sock.SendTo(message, epRecepteur); sock.Close();
}
Remarque : la chaîne à envoyer est transformée en un tableau de bytes.
Le serveur :

public Fm_serveur()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
adrIpLocale = getAdrIpLocaleV4();
} private IPAddress adrIpLocale; private IPAddress
getAdrIpLocaleV4()
{
string hote = Dns.GetHostName();
IPHostEntry ipLocales = Dns.GetHostEntry(hote);
foreach (IPAddress ip in ipLocales.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip;
}
}
return null; // aucune adresse IP V4
}
private void bt_recevoir_Click(object sender, EventArgs e)
{
byte[] message = new byte[40];
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint epRecepteur = new IPEndPoint(adrIpLocale, 33000);
sock.Bind(epRecepteur);
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
sock.ReceiveFrom(message, ref epTemp);
IPEndPoint epEmetteur = (IPEndPoint)epTemp;
string strMessage = Encoding.Unicode.GetString(message);
MessageBox.Show(epEmetteur.Address.ToString()+"->"+
strMessage);
}
Remarques :
- La fonction getAdrIpLocaleV4 retourne l’adresse IP V4 de l’hôte en utilisant le
service DNS. Le nom de l’hôte local est récupéré en utilisant le DNS.
- Elle est appelée dans le constructeur du formulaire.
- La méthode ReceiveFrom de la classe Socket attend un paramètre de type
EndPoint. Il faut donc opérer une conversion de type pour récupérer le point
de terminaison émetteur.
- Le message est un tableau de bytes. Il faut le transformer en chaîne de
caractères.
- L’instruction « CheckForIllegalCrossThreadCalls = false; » dans le
constructeur des deux formulaires permet de s’affranchir simplement des
problèmes liés à la mise à jour de l’interface lors de la réception d’un message.
Mise en œuvre :
- Créer l’application client et l’application serveur.
- Lancer les deux applications (sur le même poste ou sur deux postes différents).
- Cliquer sur le bouton recevoir du serveur.
- Renseigner l’adresse IP du serveur et le message à envoyer dans l’application
client.
- Cliquer sur le bouton envoyer du client.
- Le serveur affiche l’adresse IP et le message du client.
B. RECEPTION ASYNCHRONE
i. Principe
Lorsque l’on clique sur le bouton recevoir du serveur, celui-ci se trouve bloqué en
attente du message et ne peut accomplir aucune autre tâche. Pour y remédier, il est
possible d’utiliser le mécanisme de réception asynchrone fourni par Dotnet. Ce
mécanisme est basé sur les threads. Un thread peut être vu comme un « mini
processus » interne à une application. Une application peut avoir plusieurs threads et
donc exécuter plusieurs tâches simultanément (cette simultanéité n’est que fictive, en
tous cas sur une machine mono-processeur).
Le principe est le suivant :
 Mettre le socket en état de réception en utilisant la méthode ci-dessous :
sock.BeginReceiveFrom(message,0,40, SocketFlags.None,
ref epTemp, new AsyncCallback(recevoir),null);
- Le socket est mis en état de réception dans un thread de réception différent du
thread principal. Le thread principal poursuit immédiatement son exécution, le
programme n’est pas bloqué par cet appel.
- L’avant dernier paramètre indique que la méthode recevoir doit être appelée dès
la réception d’un message par le thread de réception.
 Gérer le message reçu :
Quand le thread de réception reçoit le message, il le prend en charge (l’affiche par
exemple), et se termine. L’idéal est de remettre le socket en état de réception
immédiatement, de manière à accepter plusieurs messages les uns après les autres.
ii. Modification du serveur (exemple 02)

public Fm_serveur()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
init();
}
private int lgMessage = 40;
private IPAddress adrIpLocale;
private Socket sock;
private IPEndPoint epRecepteur;
byte[] message;
private IPAddress getAdrIpLocaleV4()
{
// Idem version pécédente
}
private void init()
{
message = new byte[lgMessage];
adrIpLocale = getAdrIpLocaleV4();
sock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
epRecepteur = new IPEndPoint(adrIpLocale, 33000);
sock.Bind(epRecepteur);
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any,
0);
sock.BeginReceiveFrom(message,0,lgMessage,SocketFlags.None,
ref epTemp, new AsyncCallback(recevoir), null);
}
private void recevoir(IAsyncResult AR)
{
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
sock.EndReceiveFrom(AR, ref epTemp);
IPEndPoint epEmetteur = (IPEndPoint)epTemp;
string strMessage;
strMessage=Encoding.Unicode.GetString(message,0,message.Length
);
Array.Clear(message, 0, message.Length);
sock.BeginReceiveFrom(message,0,lgMessage,SocketFlags.None,
ref epTemp, new AsyncCallback(recevoir),null);
MessageBox.Show(epEmetteur.Address.ToString() + " -> " +
strMessage);
}
Remarques :
- Le serveur se met en réception dès le lancement de l’application, il n’y a donc
plus besoin d’un bouton recevoir.
- Dès la réception d’un message, le serveur relance un nouveau thread de
réception.
- La réception ne s’arrête qu’à la fermeture du programme.
- Vous pouvez tester l’envoi de messages à partir de deux clients différents.
C. UN EXEMPLE PLUS COMPLET
i. Améliorer l’interface
Modifiez votre application serveur de la manière suivante :
- Ajouter un contrôle de type ListBox (lb_messages) sur le formulaire.
- Au lieu d’afficher le message reçu, la méthode recevoir doit maintenant ajouter
ce message à la liste lb_messages.
- Ajouter une propriété nbMessages à la classe Fm_serveur. Cette propriété sera
destinée à compter le nombre de messages reçus.
- Ce nombre de messages sera affiché dans un label lbl_nbMessages.
ii. Gérer un message plus complexe
Le client va maintenant envoyer un message constitué de son adresse IP et du texte
du message luimême. Une solution simple consiste à envoyer une chaîne contenant
les deux éléments séparés par un caractère particulier.
Exemple : « 192.168.200.3#Le texte du message# ».
A la réception de cette chaîne, le serveur doit retrouver les deux éléments du message.
Il peut le faire de la manière suivante :
- Récupérer le message reçu sous la forme d’une chaîne de caractères :
strMessage = Encoding.Unicode.GetString(message, 0,
message.Length);
- Isoler les différents elements dans un tableau de chaînes :
string[] tabElements;
char[] separateur = new
char[1]; separateur[0] =
'#';
tabElements = strMessage.Split(separateur);
- tabElements[0] contient l’adresse IP.
- tabElements[1] contient le texte du message.
Remarques :
- La méthode Split découpe la chaîne strMessage en plusieurs éléments et place
ces éléments dans le tableau tabElements.
- Elle prend en paramètre un tableau contenant les caractères jouant le rôle de
séparateur pour découper la chaîne strMessage.
- Il faut donc que ce tableau contienne le caractère ‘#’.
iii. Modification du serveur (exemple03)

private int nbMessages;


// idem … private void init()
{ nbMessages = 0;
// idem …
}
private void recevoir(IAsyncResult AR)
{
EndPoint epTemp = (EndPoint) new IPEndPoint(IPAddress.Any,
0);
sock.EndReceiveFrom(AR, ref epTemp);
string strMessage;
strMessage = Encoding.Unicode.GetString( message, 0,
message.Length);
string[] tabElements;
char[] separateur = new char[1];
separateur[0] = '#';
tabElements = strMessage.Split(separateur);
nbMessages++;
string affichage = tabElements[0] + " -> " +
tabElements[1];
lb_messages.Items.Add(affichage);
lbl_nbMessages.Text = "Nombre de messages reçus : " +
nbMessages.ToString();
Array.Clear(message, 0, message.Length);
sock.BeginReceiveFrom(message,0,lgMessage,SocketFlags.None,
ref epTemp, new AsyncCallback(recevoir),null);
}
D. UNE VRAIE CONVERSATION
1. Transmettre un message à tout le monde
Pour transmettre un message à plusieurs hôtes, il y a au moins deux solutions :
- Transmettre le même message successivement aux différents destinataires, ce
qui nécessite une simple boucle.
- Envoyer le message en broadcast.
Pour envoyer un message en broadcast, il suffit de modifier ainsi la procédure
d’envoi :
private void bt_envoyer_Click(object sender, EventArgs e)
{
byte[] messageBytes;
Socket sock = new Socket( AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
sock.SetSocketOption( SocketOptionLevel.Socket,
SocketOptionName.Broadcast, true);
IPEndPoint epEmetteur = new IPEndPoint(adrIpLocale, 0);
sock.Bind(epEmetteur);
IPEndPoint epRecepteur = new
IPEndPoint(IPAddress.Broadcast, 33000);
string leMessage = "Contenu du message"; messageBytes =
leMessage.GetInfos();
sock.SendTo(messageBytes, epRecepteur); sock.Close();
}
Le message sera reçu par tous les hôtes en état de réception sur le port UDP 33000.
2. Définir un protocole
Il s’agit de fixer les règles de la communication entre les différentes applications.
Imaginons l’exemple d’un pauvre chat :
- Nous avons un serveur et plusieurs clients.
- Lorsqu’un client se connecte, il en informe le serveur.
- Le serveur conserve la liste des clients connectés.
- Lorsqu’un client envoie un message au serveur, celui-ci le transmet à
l’ensemble des clients.
- Lorsqu’un client se déconnecte, il en informe le serveur.
- Les clients sont repérés par leur adresse IP, l’unicité des pseudos n’est pas
assurée.
Nous avons besoin de définir plusieurs types de messages :
- Connexion (type C)
Emetteur : un client qui se connecte
Récepteur : le serveur
Contenu : L’adresse IP et le pseudo du client
Réaction du serveur : mémorisation du pseudo
- Envoi (type E)
Emetteur : un client qui envoie un texte sur le chat
Récepteur : le serveur
Contenu : l’adresse IP du client et le texte envoyé
Réaction du serveur : réémission du texte à tous les clients
- Réémission (type R) Emetteur : le serveur
Récepteur : les clients
Contenu : l’adresse IP du serveur, le pseudo de l’auteur du texte et le texte lui-
même
Réaction des clients : affichage du pseudo et du texte
- Déconnexion (type D)
Emetteur : un client qui se déconnecte, c'est-à-dire qui quitte l’application
Récepteur : le serveur
Contenu : l’adresse IP du client et son pseudo
Réaction du serveur : mise à jour de la liste des clients connectés
E. LE PAUVRE CHAT (CHAT)
1. Le client

Généralités :
- L’adresse IP du serveur est supposée connue.
- Il faut également déterminer les ports utilisés par le serveur et par les clients.
public partial class Fm_client : Form
{public m_client()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
adrIpLocale = getAdrIpLocaleV4();
separateur = new char[1];
separateur[0] = '#';
}
private IPAddress adrIpLocale;
private char[] separateur;
private IPAddress ipServeur = IPAddress.Parse("192.168.3.3");
private int portServeur = 33000;
private int portClient = 33001;
private int lgMessage = 1000;
private Socket sockReception;
private IPEndPoint epRecepteur;
byte[] messageBytes;
private IPAddress getAdrIpLocaleV4()
{
// idem…
}
L’envoi d’un message ne pose pas de problème particulier.
Chaque message est constitué des éléments suivants :
- Le type de message (C, E, R, D). Le client peut envoyer des messages C, E ou D.
- Le pseudo à l’origine du message.
- Le texte du message.
private void envoyer(string typeMessage,string texte)
{
byte[] messageBytes;
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint epEmetteur = new IPEndPoint(adrIpLocale, 0);
sock.Bind(epEmetteur);
IPEndPoint epRecepteur = new IPEndPoint(ipServeur,
portServeur);
string infos = typeMessage + "#" + tb_pseudo.Text + "#" +
texte + "#";
messageBytes = Encoding.Unicode.GetBytes(infos);
sock.SendTo(messageBytes, epRecepteur);
sock.Close();
tb_message.Clear();
tb_message.Focus();
}
Quand l’utilisateur clique sur « se connecter », le message de connexion est envoyé au
serveur (il ne contient pas de texte) et le client se met en état de réception. Il devient
ensuite possible de poster un texte sur le chat.
private void bt_connecter_Click(object sender, EventArgs e)
{ if (tb_pseudo.Text != "")
{
bt_connecter.Enabled = false;
tb_pseudo.Enabled = false;
envoyer("C", "");
bt_envoyer.Enabled = true;
tb_message.Enabled = true;
initReception();
tb_message.Focus();
}
}
private void initReception()
{
messageBytes = new byte[lgMessage];
sockReception = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
epRecepteur = new IPEndPoint(adrIpLocale, portClient);
sockReception.Bind(epRecepteur);
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any,
0);
sockReception.BeginReceiveFrom(messageBytes, 0, lgMessage,
SocketFlags.None, ref epTemp, new AsyncCallback(recevoir),
null);
}
A la réception d’un message, le client met à jour son interface.
private void recevoir(IAsyncResult AR)
{
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any,
0);
sockReception.EndReceiveFrom(AR, ref epTemp);
string strMessage = Encoding.Unicode.GetString(messageBytes,
0,messageBytes.Length);
string[] tabElements;
tabElements = strMessage.Split(separateur);
switch (tabElements[0])
{
case "R":
lb_messages.Items.Add(tabElements[1]+"->" + tabElements[2]);
break;
}
Array.Clear(messageBytes, 0, messageBytes.Length);
sockReception.BeginReceiveFrom( messageBytes, 0,
lgMessage, SocketFlags.None, ref epTemp, new
syncCallback(recevoir), null);
}
Quand l’utilisateur clique sur le bouton « envoyer », le texte saisi est envoyé au
serveur.
private void bt_envoyer_Click(object sender, EventArgs e)
{
envoyer("E", tb_message.Text);
}
La déconnexion est gérée par un message envoyé à la fermeture du formulaire.
private void Fm_client_FormClosing( object sender,
FormClosingEventArgs e)
{
envoyer("D", "");
}
2. Le serveur

Dès son lancement, le serveur se met en état de réception sur son port.
public partial class Fm_serveur : Form
{
public Fm_serveur()
{
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
initReception();
}
private int lgMessage = 1000;
private int portServeur = 33000;
private int portClient = 33001;
private IPAddress adrIpLocale;
private char[] separateur;
private Socket sockReception;
private IPEndPoint epRecepteur;
byte[] messageBytes;
private IPAddress getAdrIpLocaleV4()
{
// idem… }
private void initReception()
{ messageBytes = new byte[lgMessage];
adrIpLocale = getAdrIpLocaleV4();
separateur = new char[1];
separateur[0] = '#';
sockReception = new Socket( AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
epRecepteur = new IPEndPoint(adrIpLocale, portServeur);
sockReception.Bind(epRecepteur);
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
sockReception.BeginReceiveFrom( messageBytes, 0, lgMessage,
SocketFlags.None, ref
epTemp, new AsyncCallback(recevoir),
null);
}
Une méthode envoyerBroadcast permet l’envoi d’un message sur l’ensemble du réseau.
private void envoyerBroadcast(string pseudo,string texte)
{
byte[] messageBroadcast;
Socket sockEmission = new Socket(
AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Ud
p);
sockEmission.SetSocketOption( SocketOptionLevel.Socket,
SocketOptionName.Broadcast, true);
IPEndPoint epEmetteur = new IPEndPoint(adrIpLocale, 0);
sockEmission.Bind(epEmetteur);
IPEndPoint epRecepteur = new IPEndPoint(
IPAddress.Broadcast, portClient);
string strMessage = "R" + "#" + pseudo + "#" + texte + "#";
messageBroadcast = Encoding.Unicode.GetBytes(strMessage);
sockEmission.SendTo(messageBroadcast, epRecepteur);
sockEmission.Close();
}
Les messages reçus sont traités en fonction de leur type.
private void recevoir(IAsyncResult AR)
{
EndPoint epTemp = (EndPoint)new IPEndPoint(IPAddress.Any,
0);
sockReception.EndReceiveFrom(AR, ref epTemp);
string strMessage = Encoding.Unicode.GetString(
messageBytes, 0, messageBytes.Length);
string[] tabElements;
tabElements = strMessage.Split(separateur);
switch (tabElements[0])
{
case "C":
lb_clients.Items.Add(tabElements[1]);
break;
case "E":
envoyerBroadcast(tabElements[1], tabElements[2]);
break;
case "D":
lb_clients.Items.Remove(tabElements[1]);
break;
}
Array.Clear(messageBytes,0,messageBytes.Length);
sockReception.BeginReceiveFrom( messageBytes, 0, lgMessage,
SocketFlags.None, ref epTemp, new AsyncCallback(recevoir),
null);
}

Vous aimerez peut-être aussi