Lundi 20 Mars 2006
.NET Remoting (Visualiser avec IE -Internet Explorer-)
Par dotnet, Lundi 20 Mars 2006 à 11:55 GMT+2 dans .NET
Dans cet article je vais expliquer en quelques étapes comment faire une application distribué avec la technologie .NET Remoting.
L’environnement technique :
IDE : Visual studio 2003 avec le .NET Framework 1.1
Langage : C#
Présentation
.NET Remoting est une technologie offerte avec le Framework .NET et permet de faire de la programmation distribuée. La programmation distribuée permet par principe d’exécuter un processus sur plusieurs machines. Le but étant d’exécuter des parties du processus sur des machines qui sont le plus disposées à l’effectuer (gros calculs, traitement métier, …).
Scenarii
Voici notre scenario :
Nous disposons d’une application de gestion de compte bancaire qui est composée de trois applications reparties dans toute la France.
- La première application a pour but de récupérer dans une DB le montant du découvert d’un client identifié
- La seconde, permettra à partir du montant de découvert d’effectuer des calculs permettant de savoir le montant des agios
- La troisième, collectera les informations transmises par les deux premières applications et prendra la décision d’envoyer un courrier au client ou pas en étudiant le rapport découvert/agios.

Pour visualiser le schéma vous devez avoir un web browser IE
Mise au point
Il est utile de comprendre l’architecture du .NET remoting.
En gros : vous avez d’un côté un serveur puis de l’autre un ou plusieurs client comme ci-dessous.

Pour visualiser le schéma vous devez avoir un web browser IE
L’échange de données peut se faire via HTTP ou TCP (dans notre cas nous utiliserons le protocole TCP). HTTP est plus adapté pour des communications distantes nécessitants le réseau web et TCP pour des communications en réseaux locaux (format binaire, plus performant). Le format d’échange peut être du Xml ou du binaire.
Important :
Dans notre cas les applications 1 et 2 sont des serveurs et l’application 3 le
client
Mise en pratique
- Créez une solution nommée _NETRemoting sous visual studio .NET,
- Ajoutez une application console (projet) nommée Svr_App1 à la solution,
- Ajoutez un projet ClassLibrary nommé Lib_App1 à la solution,
- Ajoutez une application console (projet) nommée Svr_App2 à la solution,
- Ajoutez un projet ClassLibrary nommé Lib_App2 à la solution,
- Ajoutez
une application WinForm (projet) nommée clt_App3
à la solution.
Il est intéressant de savoir que le client ne manipule jamais les objets hébergé par le serveur mais passe par se qu’on appelle des proxies.
En effet, les proxies sont
les représentants locaux des objets distants. Pour communiquer avec un objet
distant, on instancie un proxy qui sera la représentation locale de cet objet.
On manipule le proxy comme si c’était notre objet distant.
Dans notre cas nous allons avoir deux proxies
(un pour l’application 1 et l’autre pour l’application 2). Un proxy étant un
objet, il est donc représentable par une classe ou une interface.
A ce stade nous avons toutes les entités nécessaires, reste plus qu’à les meubler.
Projet Lib_App2
Son rôle est de calculer un agio à partir d’un montant représentant le découvert bancaire
Renommer class1 en LibApp2 ainsi que le fichier (LibApp2.cs)
La classe LibApp2 doit
forcément dériver de MarshalByRefObject
Ajouter au projet une méthode nommée float getAgio(float fDec)
Le projet Lib_App1
Son rôle est de récupérer le montant du découvert de la base de données puis d’interroger l’application 2 pour avoir le montant de l’agio.
Renommer class1 en LibApp1 ainsi que le fichier (LibApp1.cs)
La classe LibApp1 doit
forcément dériver de MarshalByRefObject
Ajouter une méthode nommée void getAgioAndDec(long lIDClient, ref float fDec, ref float fAgio)
Ajouter une interface nommée IApp2 (proxy de Lib_App2) –fichier IApp2.cs-
Le projet Svr_App2
Renommer Class2 en App2 ainsi que le fichier (App2.cs au lieu de Class1.cs)
Ajouter une référence du projet Lib_App2
Le projet Svr_App1
Renommer Class1 en App1 ainsi que le fichier (App1.cs au lieu de Class1.cs)
Ajouter une référence du projet Lib_App1
Le projet clt_App3
Ajouter un bouton, une textbox, et un label
Dans le textbox vous saisirez l’ID du client et l’appui sur le bouton invoquera les méthodes distantes puis le résultat sera affiché dans le label.
Ajouter une interface nommée IApp1 (proxy de Lib_App1) –fichier IApp1.cs-
Au niveau des deux
serveurs
Ajouter
les références suivantes (fichier App1.cs et App2.cs) :
using
System;
using System.Collections;
using System.Runtime.Remoting;
using MyTcpChannel = System.Runtime.Remoting.Channels.Tcp.TcpChannel;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
Attention: Vous devez ajouter la référence
de l’assembly System.Runtime.Remoting
à vos projets (Click droit sur le projet puis ajouter référence)
Pour “servir un objet” on doit:
- Ouvrir un canal selon un protocole et sur un port (1234 pour Svr_App1 et 5678 pour Svr_App2)
- Enregistrer le canal ouvert précédemment
- Publier son type avec l’URI et le mode pour y accéder
Et tout ça dans la méthode main
Au niveau du client
(clt_App3)
using
System;
using System.Collections;
using System.Runtime.Remoting;
using MyTcpChannel = System.Runtime.Remoting.Channels.Tcp.TcpChannel;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
Attention: Vous devez ajouter la référence
de l’assembly System.Runtime.Remoting
à vos projets (Click droit sur le projet puis ajouter référence)

Pour visualiser le schéma vous devez avoir un web browser IE
Codes Sources
Si vous voulez les codes complets envoyez moi un mail à l’@ suivante :bossiel@yahoo.fr
Svr_App1 :App1.cs
using System;
using System.Collections;
using System.Runtime.Remoting;
using MyTcpChannel
= System.Runtime.Remoting.Channels.Tcp.TcpChannel;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
namespace
Svr_App1
{
/// <summary>
/// Description résumée de Class1.
/// </summary>
class
App1
{
/// <summary>
/// Point d'entrée
principal de l'application.
/// </summary>
[STAThread]
static void
{
BinaryServerFormatterSinkProvider
serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel
= System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider
clientProv = new BinaryClientFormatterSinkProvider();
IDictionary
props = new Hashtable();
props["port"] = 1234;//port
//(1): Ouvrir un canal selon le protocole tcp et sur un port 1234
// Enregistrer le canal ouvert précédemment
MyTcpChannel _tcpChannel = new MyTcpChannel(props,clientProv,serverProv);
ChannelServices.RegisterChannel(_tcpChannel);
//(2): Publier son type avec l’URI et le mode pour y
accéder
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Lib_App1.LibApp1),"Svr_App1",WellKnownObjectMode.SingleCall);
Console.Write("**********************************************\n");
Console.Write("
MON SERVEUR Svr_App1 \n");
Console.Write("***********************************************\n");
Console.Write("Moi
je suis un serveur d'application conçu pour Lib_App1 distant\nqui sera utilisé par un client autonome (App3) via tcp/http au format binaire ou soap\n");
Console.Write("\n\n");
Console.Write("-Je
viens de me lancer sur tcp:1234\n\n");
Console.Write("-Je
viens de demarrer l'objet distant ok\n\n");
Console.Write("[Entree] pour quitter");
Console.ReadLine();
ChannelServices.UnregisterChannel(_tcpChannel);
}
}
}
Lib_App1 :LibApp1.cs
using System;
using System.Collections;
using System.Runtime.Remoting;
using MyTcpChannel
= System.Runtime.Remoting.Channels.Tcp.TcpChannel;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
namespace
Lib_App1
{
/// <summary>
/// Description résumée de Class1.
/// </summary>
public
class LibApp1 :MarshalByRefObject
{
public LibApp1()
{ }
public void getAgioAndDec(long lIDClient, ref float fDec, ref float fAgio)
{
//les calcul sont fictifs: ajouter du code pour acceder à une DB
if(lIDClient
> 0)
{
fDec = -152;//toujours
fAgio = getAgio2(fDec);
}
}
/// <summary>
/// Cette methode
appel l'objet distant Lib_App2 pour avoir le montant de l'agio
/// sachant
à combien s'éleve le découvert.
/// Pour que cette méthode
marche il faut que le serveur Svr_App2 soit démarré
/// </summary>
/// <returns></returns>
private float
getAgio2(float fDec)
{
//(1): Ouverture du canal selon le protocole Tcp
MyTcpChannel _tcpChannel = new MyTcpChannel(1);
//(2): Enregistrement du canal ouvert précédemment
ChannelServices.RegisterChannel(_tcpChannel);
// (3): Récupération d'une référence de l'objet distant
Lib_App2 hebergé par Svr_App2
Lib_App2.LibApp2 pxy_App2 = (Lib_App2.LibApp2)RemotingServices.Connect(typeof(Lib_App2.LibApp2),
"tcp://localhost:5678/Svr_App2");
try
{
return pxy_App2.getAgio(fDec);
}
catch(Exception ex)
{
return -1;
}
finally
{
// (4):
Fermeture et deréférencement du canal
ChannelServices.UnregisterChannel(_tcpChannel);
}
}
}
}
ctl_App3 :IApp1.cs
/*C'est le proxy de l'objet
Lib_App1*/
using System;
namespace
Lib_App1
{
public
interface LibApp1
{
void getAgioAndDec(long lIDClient, ref float fDec, ref float fAgio);
int test();
}
}
clt_App3 :
Form1.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.Remoting;
using MyTcpChannel
= System.Runtime.Remoting.Channels.Tcp.TcpChannel;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
namespace
clt_App3
{
/// <summary>
/// Description résumée de Form1.
/// </summary>
public
class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox
textBox1;
private System.Windows.Forms.Button
button1;
private System.Windows.Forms.Label
label1;
/// <summary>
/// Variable nécessaire au
concepteur.
/// </summary>
private System.ComponentModel.Container
components = null;
public Form1()
{
//
// Requis pour la prise en charge du Concepteur Windows Forms
//
InitializeComponent();
//
// TODO : ajoutez le code du constructeur après l'appel à InitializeComponent
//
}
/// <summary>
/// Nettoyage des
ressources utilisées.
/// </summary>
protected override
void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Code généré par le Concepteur Windows Form
/// <summary>
/// Méthode requise pour la
prise en charge du concepteur - ne modifiez pas
/// le
contenu de cette méthode avec l'éditeur de code.
/// </summary>
private void InitializeComponent()
{
this.textBox1
= new System.Windows.Forms.TextBox();
this.button1
= new System.Windows.Forms.Button();
this.label1
= new System.Windows.Forms.Label();
this.SuspendLayout();
//
//
textBox1
//
this.textBox1.Location
= new System.Drawing.Point(16, 16);
this.textBox1.Name
= "textBox1";
this.textBox1.TabIndex
= 0;