Sie sind auf Seite 1von 11

DreamLive : www.dreamlive.

fr
Programmation C# - DotNet
Communication réseau

COMMUNICATION RÉSEAU -TCPCLIENT - TCPLISTENER

Les sockets sont un modèle permettant à des processus de communiquer entre eux. Leur rôle est
de faire communiquer aussi bien des processus s‛exécutant sur la même machine que des proces-
sus distants. Dans ce cas, la communication s‛établit à travers le réseau. Deux modes de commu-
nication sont possibles avec les sockets. Le mode connecté, qui utilise le protocole TCP, établit
une connexion entre la source et le destinataire. Avec ce dernier, on connaît en permanence la
destination des envois. Un système d‛accusé de réception et de contrôle d‛erreurs permet une
transmission fiable. Le mode non connecté utilise quant à lui le protocole UDP et ne crée pas de
connexion durable entre les deux processus. Lors de chaque envoi, il faut indiquer la destination.
Ce mode de communication ne propose pas d‛accusé de réception. Il est donc moins fiable que le
précédent mais reste plus simple. Les classes permettant d‛utiliser les sockets font partie de
l‛espace de noms System.Net.Sockets.

CRÉATION DE L‛APPLICATION

Ici je propose de construire une console client/serveur permettant de communiquer (Envoyer et


recevoir des messages) avec les utilisateurs du réseau.

Pour que nous puissions nous concentrer sur l‛essentiel j‛ai préparé le formulaire et ses contrôles.
Vous le trouverez dans le sous dossier sources de l‛application.

● Copier le dossier client_et_serveur que vous trouverez dans le sous dossier sources dans votre
dossier de travail,
● Double cliquer sur le fichier client_et_serveur.sln pour démarrer la solution,
Ce formulaire est constitué de trois
onglets selon les actions à réaliser.

Principe: Le client doit être en mesure


d‛envoyer un message à un utilisateur
réseau sélectionné dans la liste dérou-
lante prévue à cet effet. Le serveur
(disponible) sur cette même console
doit écouter les demandes de con-
nexion (sur un port) pour réceptionner
les messages.

Pour envoyer des messages nous avons


besoin de la classe TcpClient.

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 1
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

Et pour les réceptionner, nous devons disposer de la classe TcpListener. Ces deux classes sont
comprises dans l‛espace de noms System.Net. De même nous devons exploiter les sockets pour
surveiller les demandes de connexion. Les Sockets sont disponibles dans l‛espace de noms System.
Net.Sockets.

● Ajouter les références aux espaces de noms comme suit:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
//---RESEAU---
using System.Net;
using System.Net.Sockets;
//---THREAD pour qu’une connexion ne monopolise pas le processeur---
using System.Threading;
//---Accès aux fichiers séquentiels---
using System.Text;
Comme vous le constatez j‛ajoute de même une référence à l‛espace de noms System.Threading.
L‛écoute du réseau doit se faire dans un Thread pour ne pas risquer d‛emprunter toutes les res-
sources CPU. Enfin l‛espace de noms System.Text est inévitable puisque les informations transi-
tent par le réseau sous forme de tableaux de Bytes. Elles sont donc encodées. Et cet espace de
noms offre les méthodes permettant aussi bien d‛encoder que de décoder.

Si vous démarrez l‛application, vous constatez que le formulaire n‛apparaît pas à l‛écran mais en
sentinelle dans la barre des tâches. Je l‛ai en effet conçue pour qu‛elle s‛active à réception de
message ou par double clic sur l‛icône de la sentinelle. Cet événement est
donc déjà géré dans le code.

Nous avons ensuite besoin de variables publiques pour gérer les activités réseau. Nous allons donc
déclarer des variables concernant le client ainsi que le serveur.

Dans la classe com_reseau, à la suite des déclarations de tous les contrôles du formulaire et en
dehors de toute procédure,

● Commencer par déclarer toutes les variables concernant le client comme suit:

Seule la variable int port concerne à la fois le client et le serveur. Il s‛agit en fait du port de com-
munication pour les activités réseau.

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 2
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

...
private System.Windows.Forms.ListView m_r;
private System.Windows.Forms.ColumnHeader columnHeader4;
private System.Windows.Forms.ColumnHeader columnHeader5;
private System.Windows.Forms.ColumnHeader columnHeader6;
private System.Windows.Forms.NotifyIcon sentinelle;
private System.ComponentModel.IContainer components;

//===================================================================
//CONCERNE LE CLIENT...
//===================================================================

//==================================================================
//La classe TcpClient est bâtie sur Socket. Elle permet d’utiliser
//facilement ses capacités pour accéder aux services du protocole TCP.
//System.Net.Sockets
//==================================================================
TcpClient leClient = new TcpClient();

//Informations sur cette machine, le client...


IPAddress adresse_Ip;
string nom_Machine;

//Création des objets et variables pour tous les membres de la classe

//==================================================================
//Le numéro de port (sortie) pour la communication est virtuel...
//Il est fonc choisi arbitrairement. Un numéro mal choisi peut
//occasionner des pbs de sécurité...Accès refusé !!
//==================================================================
private int port = 2564;
Tout d‛abord, je crée un objet TcpClient (leClient) que j‛utiliserai pour envoyer le flux de donner
via le réseau au destinataire sélectionné. Celui-ci devra être instancié à chaque nouvel envoi pour
signifier qu‛il s‛agit potentiellement d‛un nouveau client et couper le flux de transmission entre le
client et le destinataire (le serveur dans ce contexte).

Ensuite l‛objet IPAddress (adresse_Ip), issu de l‛espace de noms System.Net.Sockets, permet-


tra de mémoriser l‛adresse Ip de l‛expéditeur. La variable suivante de type string permettra donc
de mémoriser le nom de la machine qui envoie le message.

Enfin la variable int port permet de déterminer un numéro de port (arbitraire) de communication
entre l‛expéditeur (le Client) et le destinataire (le Serveur).

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 3
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

● A la suite de ces déclarations, ajouter les déclarations publiques concernant le serveur:


...
private int port = 2564;
//===================================================================
//CONCERNE LE SERVEUR...
//===================================================================
Socket monSocket;
//Port ouvert pour la communication client/Serveur
private bool arretS = true;
//Dans un thread, écoute tous les demandes de connexion...
private TcpListener jecoute;
//Pour l’écoute dans un Thread sans prendre trop de ressources Proc.
private Thread module_Serveur;
Bien sûr un objet Socket (monSocket) de l‛espace de noms System.Net.Socket est créé, pour
surveiller et gérer les demandes de connexion. L‛objet TcpListener est créé pour réceptionner les
données issues du flux réseau. Enfin l‛objet Thread (module_Serveur) est nécessaire pour gérer
l‛écoute des activités réseaux dans un module indépendant et ne pas surcharger les ressources
CPU.

Au même moment que j‛initialise les contrôles, je décide de réceptionner les informations de la
machine et de lancer directement le Thread d‛écoute du réseau. L‛application sera ainsi tout de
suite prête à recevoir les messages.

● Modifier le code de la procédure com_reseaux comme suit:


public com_reseau()
{
InitializeComponent();
//Récupération de l’adresse Ip de la machine...
adresse_Ip = Dns.Resolve(Dns.GetHostName()).AddressList[0];
//Récupération du nom de la machine...
nom_Machine = Dns.GetHostName().ToString();
//Chaque console est client serveur donc doit commencer l’écoute au démar-
rage...
module_Serveur = new Thread(new ThreadStart(Ecoute));
module_Serveur.Start();
}

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 4
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

La méthode Resolve de l‛objet Dns me permet premièrement de récupérer et stocker l‛adresse Ip


de la machine et de la stocker dans la variable publique de type IPAddress. L‛objet Dns et sa mé-
thode statique Resolve sont issus de l‛espace de noms System.Net. De même, la méthode GetHos-
tName() de l‛objet Dns me permet de récupérer et stocker le nom de la machine dans la variable
publique de type string prévue à cet effet. Ensuite j‛instancie l‛objet Thread (module_Serveur)
de manière à démarrer la procédure (ecoute) dans un module indépendant. Ce thread sera chargé
de surveiller et gérer les demandes de connexion. Une fois instancié, la méthode Start() de l‛objet
Thread permet de démarrer le Thread. Avant de créer la procédure ecoute, je choisis d‛initialiser
les utilisateurs du réseau au chargement du formulaire. Cette initialisation est manuelle ici pour
continuer de nous concentrer sur l‛essentiel. Ainsi, en adaptant les noms à des noms correspondant
à des utilisateurs de votre réseau:

● Ajouter les lignes de code suivante au Form_Load:


private void com_reseau_Load(object sender, System.EventArgs e)
{
//Ajout d’utilisateurs du réseau...
liste_r.Items.Add(«Stef»);
liste_r.Items.Add(«localhost»);
liste_r.Items.Add(«adminchrisian»);
liste_r.Items.Add(«portabledp»);
liste_r.SelectedIndex = 0;
etat.Panels[1].Text = adresse_Ip.ToString();
etat.Panels[2].Text = nom_Machine;
}
localhost désigne bien entendu votre machine.

Ensuite je décide de créer la procédure d‛écoute du réseau.

● Créer et saisir la procédure ecoute(), comme suit:


public void Ecoute()
{
try
{
etat.Panels[0].Text =»Initialisation de l’écoute...»;
//Initialisation du Socket d’écoute
jecoute = new TcpListener(port);
//Mise en marche du Socket
jecoute.Start();

etat.Panels[0].Text = «Serveur en attente de client...»;

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 5
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

//On accepte le client si celui-ci se présente à l’aide d’un autre Socket.


monSocket = jecoute.AcceptSocket();
etat.Panels[0].Text = «Serveur connecté au client...»;
//Attente de données provenant du client à l’aide d’une boucle infinie...
while(arretS)
{
//Tableau de bytes pour contenir les données reçues.
Byte[] transfert = new Byte[2048];
//On place les bytes récupérés dans le tableau...
monSocket.Receive(transfert);
//On traduit les bytes en caractères en default (pour les accents)
string donnees = Encoding.Default.GetString(transfert);
//Se produit dès que le client se déconnecte...
if (donnees.Substring(0,1)!=»D»)
{
jecoute.Stop();
//Il faut détruire le thread et le relancer de manière à rétablir le flux
module_Serveur.Abort();
//Relance le Thread...
module_Serveur = new Thread(new ThreadStart(Ecoute));
module_Serveur.Start();
module_Serveur.Join(500);
//Et on ne poursuit pas le reste du code
return;
}
int debut = 3;
int fin = donnees.IndexOf(«:»,0);
string de = donnees.Substring(debut, fin - 1);
string sujet = donnees.Substring(fin + 1);
construit_liste(m_r,de,sujet,System.DateTime.Now.ToString());
jecoute.Stop();
module_Serveur.Abort();
module_Serveur = new Thread(new ThreadStart(Ecoute));
module_Serveur.Start();
}
}
catch
{
jecoute.Stop();
module_Serveur.Abort();
module_Serveur = new Thread(new ThreadStart(Ecoute));
module_Serveur.Start();
}}

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 6
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

J‛instancie tout d‛abord l‛objet d‛écoute TcpListener sur le port d‛écoute choisi et défini en varia-
ble publique (jecoute = new TcpListener(port);). Je mets en marche le Socket (jecoute.Start();).
Ensuite j‛accepte le client si celui-ci se présente avec une demande de connexion (monSocket =
jecoute.AcceptSocket();). Et je réalise une boucle infinie (à l‛aide du booléen publique arretS) de
façon à scruter le réseau jusqu‛à réception du flux. Je prévois un tableau d‛octets pour le stockage
du flux (Byte[] transfert = new Byte[2048];). C‛est ainsi que les données transitent via le réseau.
La méthode Receive de l‛objet Socket permet de récupérer ces données. Je les stocke immédia-
tement dans le tableau d‛octets (transfert) précédemment déclaré. J‛exploite ensuite l‛espace de
noms System.Text pour décoder le flux (tableau de Bytes) et le mémoriser sous forme de texte
dans la variable donnees de type string.

Le test de l‛instruction if qui suit permet de s‛assurer que l‛écoute du flux ne se fait pas en boucle
dans le vide. Cette mésaventure se produit lorsque le client dont la connexion fut acceptée par
le serveur, se déconnecte. Si le message recomposé ne commence pas par la lettre ‘D‛ (je ferai
en sorte que tout message issu du client commence ainsi...), je coupe le flux en fermant l‛objet
TcpListener (ecoute) et en détruisant le thread d‛écoute pour le relancer aussitôt et rester dis-
ponible pour les autres connexions à venir.
if (donnees.Substring(0,1)!=»D»)
{
jecoute.Stop();
//Il faut détruire le thread et le relancer de manière à rétablir le
flux
//de transmission...
module_Serveur.Abort();
//Relance le Thread...
module_Serveur = new Thread(new ThreadStart(Ecoute));
module_Serveur.Start();
module_Serveur.Join(500);
//Et on ne poursuit pas le reste du code
return;
}
Dans le cas contraire, j‛ajoute les données recomposées (soit le message) dans le TreeView prévu
à cet effet. Pour ce faire, je détecte les différentes parties du message à l‛aide de la méthode in-
dexOf de la class string permettant de renvoyer la position de certains caractères particuliers.
int debut = 3;
int fin = donnees.IndexOf(«:»,0);
string de = donnees.Substring(debut, fin - 1);
string sujet = donnees.Substring(fin + 1);
construit_liste(m_r,de,sujet,System.DateTime.Now.ToString());

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 7
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

Lorsque l‛expéditeur et le sujet sont dissociés, j‛appelle la fonction construit_liste (à créer) qui se
charge de remplir les éléments et sous éléments du ListView. Cette fonction requiert en paramè-
tre, le nom de l‛objet ListView concerné, le string de l‛expéditeur, le string du sujet et le string
de la date.

● Créer la fonction construit_liste() comme suit:


private void construit_liste(ListView Liste, string el1, string el2, string
el3)
{
ListViewItem elmt;
ListViewItem.ListViewSubItem ssElmt;
//Crée l’élément principal
elmt = new ListViewItem();
elmt.Text = el1;
//Crée les 2 Ss éléments
ssElmt = new ListViewItem.ListViewSubItem();
ssElmt.Text = el2;
elmt.SubItems.Add(ssElmt); // Ajoute à la collection
ssElmt = new ListViewItem.ListViewSubItem();
ssElmt.Text = el3;
elmt.SubItems.Add(ssElmt); // Ajoute à la collection
Liste.Items.Add(elmt);
}
Pour mieux manipuler ces ListView et leur implémentation, je vous suggère de vous reporter à
MSDN. Ici la procédure se contente de créer l‛élément de la liste et de lui ajouter les sous-élé-
ments (colonne). L‛objet ListView est passé en paramètre pour que cette fonction puisse être
réutilisée pour l‛autre liste, celle des messages envoyés.

Comme je fais en sorte que le client se déconnecte juste après avoir envoyé son message, pour
laisser la place à d‛autres messages, d‛autres clients, je coupe le flux de la même façon que précé-
demment pour le relancer aussitôt.
jecoute.Stop();
module_Serveur.Abort();
module_Serveur = new Thread(new ThreadStart(Ecoute));
module_Serveur.Start();
Ensuite, la console, tel un client, doit être en mesure d‛envoyer un message au destinataire choisi
dans la liste.

● Double cliquer sur le bouton Envoyer depuis le formulaire pour éditer son code,
● Et saisir sa ligne de code comme suit:

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 8
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

envoie_Serveur(m.Text);
Je fais appel à la fonction envoie_Serveur (non encore créée) qui doit se charger d‛établir la com-
munication et d‛envoyer le message encodé.
private void envoie_Serveur(string message)
{
try
{
string choix = liste_r.Text;
leClient = new TcpClient();
leClient.Connect(choix, port);
etat.Text = «Connexion en cours...»;
//=================================================================
//La transmission de données se fait via un objet NetworkStream qui
//s’obtient en utilisant la méthode GetStream...System.Net.Sockets
//=================================================================
NetworkStream transmission = leClient.GetStream();
message = ‘‘De ’’ + nom_Machine + ‘‘ (’’ + adresse_Ip.ToString() + ‘‘): ’’ + message;
Byte[] envoie = System.Text.Encoding.Default.GetBytes(message.ToCharArray());

transmission.Write(envoie,0,envoie.Length);
string de = nom_Machine + « (« + adresse_Ip.ToString() + «): «;
string sujet = m.Text;
construit_liste(m_e,de, sujet, System.DateTime.Now.ToString());
transmission.Close();
leClient.Close();
}
catch (Exception Ex)
{
gestion_Erreur(Ex);
}
}
Après avoir stocké le destinataire choisi par le biais de la liste déroulante, j‛instancie un nouvel
objet TcpClient. Le fait de recréer un objet TcpClient à chaque envoi permet de ne pas maintenir
le flux et de couper la connexion entre l‛expéditeur et le destinataire de façon à libérer le port
pour les potentielles autres connexions de clients. Ensuite j‛utilise la méthode Connect de l‛objet
TcpClient pour définir l‛expéditeur auquel je me connecte et le port de communication sollicité,
déclaré en variable publique. On crée l‛objet de flux (NetworkStream) en local et non en variable
publique de façon à pouvoir le couper instantanément après l‛envoi pour que l‛expéditeur (serveur)
reste disponible pour d‛autres connexions (d‛autres clients). Ensuite je stocke le message à en-
voyer sous forme de string dans la variable message. Puis je l‛encode sous forme de tableau de
Byte à l‛aide de l‛espace de noms System.Text.

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 9
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

Remarque: j‛utilise le système d‛encodage Default plutôt que ASCII de façon à retranscrire les
caractères latins (accentuations...). Ensuite j‛utilise la méthode Write de l‛objet NetworkStream
de façon à envoyer ces informations ainsi encodées à travers le réseau.

Ensuite, comme précédemment j‛appelle la fonction construit_liste(), pour remplir le ListView des
messages envoyés (Expéditeur, sujet et date...).

Ensuite je ferme le flux ainsi que le client pour libérer le port de communication une fois le mes-
sage envoyé (transmission.Close(); leClient.Close();).

En cas de souci (Catch), j‛appelle la procédure chargée de gérer les erreurs (gestion_
Erreur(Ex);).

● Je vous laisse ajouter cette procédure comme suit:


private void gestion_Erreur(Exception probleme)
{
MessageBox.Show (probleme.Message + «\n\n» + probleme.Source);
}
Enfin, à la fermeture du formulaire pour favoriser la destruction des objets de connexion et du
flux, je choisis d‛agir sur le processus même si la méthode n‛est pas bien propre.

● Ajouter donc le code comme suit:


private void com_reseau_Closing(object sender, System.ComponentModel.Cance-
lEventArgs e)
{
System.Diagnostics.Process leProc = System.Diagnostics.Process.GetCur-
rentProcess();
leProc.CloseMainWindow();
sentinelle.Visible = false;
if(!leProc.HasExited)
leProc.Kill();
leClient = null;
monSocket = null;
}
Bien sûr la tâche est encore grande, il faudrait gérer l‛envoi de pièce jointe, permettre un accusé
de réception, visionner l‛intégralité des messages reçus par le biais d‛une fenêtre annexe permet-
tant toutes les techniques de texte enrichi, créer la liste des utilisateurs réseau automatique-
ment, activer l‛onglet des messages reçus automatiquement à réception de message...

Ici, je m‛arrête là pour simplifier le problème et surtout dissocier les fonctionnalités fondamenta-
les et je projette ensuite de faire évoluer l‛application en lui greffant ces fonctionnalités.

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 10
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.
DreamLive : www.dreamlive.fr
Programmation C# - DotNet
Communication réseau

Ces documents DreamLive sont libres de droits et ouverts à tous alors, profitez, partagez et
appréciez ! Pour nous retrouver sur le Web : http://www.dreamlive.fr.

Ces documents DreamLive sont libres de droits et ouverts à tous alors, Page 11
profitez, partagez et appréciez ! Pour nous retrouver sur le Web :
http://www.dreamlive.fr.

Das könnte Ihnen auch gefallen