c sharp c# dotnet communication réseau tcpclient

11

Click here to load reader

Upload: sofia-martel

Post on 10-Mar-2016

241 views

Category:

Documents


2 download

DESCRIPTION

● 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, Ici je propose de construire une console client/serveur permettant de communiquer (Envoyer et recevoir des messages) avec les utilisateurs du réseau.

TRANSCRIPT

Page 1: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 11Ces 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, profitez, partagez et appréciez ! Pour nous retrouver sur le Web : http://www.dreamlive.fr.

Page 2: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 1Ces 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.

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.

Page 3: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 2Ces 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.

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.

Page 4: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 3Ces 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.

... 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).

Page 5: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 4Ces 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.

● 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();}

Page 6: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 5Ces 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.

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...»;

Page 7: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 6Ces 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.

//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(); }}

Page 8: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 7Ces 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.

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());

Page 9: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 8Ces 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.

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:

Page 10: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 9Ces 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.

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.

Page 11: C sharp c# dotnet communication réseau tcpclient

DreamLive : www.dreamlive.frProgrammation C# - DotNet

Communication réseau

Page 10Ces 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.

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.