Skip Navigation Links
Accueil
Java Standard EditionExpand Java Standard Edition
Java EE 5Expand Java EE 5
Visual Basic .Net 2005Expand Visual Basic .Net 2005
Visual C++ .Net 2005Expand Visual C++ .Net 2005
Visual C# .Net 2005Expand Visual C# .Net 2005
Cours ASP .Net 2.0Expand Cours ASP .Net 2.0
PostgresqlExpand Postgresql
LinuxExpand Linux
Visual Studio 2008Expand Visual Studio 2008
ASP 3.0 ClassiqueExpand ASP 3.0 Classique
Cours Javascript - DOM - DHTMLExpand Cours Javascript - DOM - DHTML
Cours AjaxExpand Cours Ajax
VBAExpand VBA
AssembleurExpand Assembleur
PerlExpand Perl
MembresExpand Membres
L'auteur du site
Nouveautés sur le site
Contacts
Plan du site
Accueil > Visual C# .Net 2005 > IV Le .net remoting
____________________________________________________________________________________________________
Connexion
                                                                                         

IV) LE .NET REMOTING

IV.I) L'application serveur
IV.II) L'objet serveur
IV.III) L'application cliente
IV.IV) Précisions sur les Channels
IV.V) Remarques sur .Net Remoting
IV.VI) Source d'exemple .net Remoting

Voici un tuto sur .net remoting, que je complèterai petit à petit.

Merci à MSDN pour ses précieux tutoriels, à partir desquels est inspiré largement ce cours.

Créer un serveur à distance:

Le serveur à distance se situe, bien entendu, sur l'ordinateur serveur. Le serveur à distance est divisé en deux parties:

- L'objet serveur.

  C'est l'objet avec lequel le client communique.

- L'application serveur

I) L'application serveur

  L'application serveur sert pour enregistrer l'objet serveur auprès des services d'accès à distance.
Pour faire cet enregistrement, elle utilise la méthode RemotingConfiguration.RegisterWellKnownServiceType.
Il faut remarquer que l'objet distant n'est pas créé au moment de cet enregistrement( vu sur msdn).
L'instanciation de l'objet distant se produit uniquement lorsque le client essaye d'appeler une méthode depuis le client( vu sur msdn).
La classe System.Runtime.Remoting.RemotingConfiguration est une classe qui fournit diverses méthodes statiques pour configurer l'infrastructure remoting.

I.1) Le canal( channel)

Les canaux: les canaux servent à transporter les messages des objets distants, et aussi les messages vers les objets distants( bidirectionnel). Quand un client appelle une méthode d'un objet distant, les paramètres (et d'autres choses concernant cet appel) sont transportés, à travers ce canal, vers l'objet distant. Les résultats de l'appel sont retournés vers le client de la même façon.

using
System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using  System.Runtime.Remoting.Channels.Tcp;

//on crée un objet channel, qui écoutera sur le port 8085
TcpChannel channel = new TcpChannel(8085);

//on enregistre, auprès des services de canaux, notre canal

ChannelServices.RegisterChannel(channel);

Explications:

- On crée d'abord un objet channel, de type TcpChannel car on veut utiliser le protocole tcp( car, en réalité, les communications se font par le protocole tcp ou un autre, mais cela est caché par la notion de .net remoting, pour vous simplifier la vie. Il y a donc, en réalité, une couche tcp). On précise un numéro de port, numéro sur 16 bits( car il y a 65536 ports). Il s'agit d'un port logiciel, bien entendu, rien à voir avec un port physique.
 Un port logiciel, c'est juste pour que le système d'exploitation puissent nommer un "endroit" de communication. Ainsi, les applications qui veulent communiquer, préciseront l'endroit où elles veulent communiquer, et donc avec qui.
  Ce canal( channel en anglais) est un server channel. Il écoutera le port 8085, par exemple pour pouvoir entendre une demande d'appel de méthode de l'objet distant.

- Puis il faut enregistrer cet objet channel. Car nous avons seulement un objet channel en mémoire. On utilise la méthode ChannelServices.RegisterChannel(channel);
Cette méthode va enregistrer notre canal auprès des services de canaux.

  Ce canal, maintenant qu'il est enregistré, pourra être utilisé par les clients. Et le canal commence à écouter le port dès cet enregistrement. Il écoute les demandes des clients.
D'ailleurs votre pare-feu vous indiquera que votre programme essaye d'agir en tant que serveur.

  Comment le système d'exploitation saura que c'est celui-là qu'il faudra utiliser? Eh bien, c'est parce que c'est le seul canal qui aura été enregistré par l'application serveur. Donc le système d'exploitation peut en déduire que c'est celui-là qu'il faut utiliser.

I.1.a) Définition: un serveur

Un serveur, c'est comme un serveur de bar. Il attend que le client fasse une demande, et ensuite il le sert. C'est pareil en informatique. Ainsi le canal serveur attend qu'un client lui demande quelque chose: il écoute le port concerné, dans l'attente d'une demande. Puis il lui envoie sa réponse par l'intermédiaire du canal.

I.2) L'enregistrement de l'objet distant

  Ensuite il faut enregistrer notre objet distant auprès des services d'accès à distance.

RemotingConfiguration.RegisterWellKnownServiceType(
           System.Type.GetType("MonNameSpace.myRemoteClass, DllObjDistant"),
           //(peut être remplacé par typeof( myRemoteClass ), si la classe n'est pas dans une dll 
           "RemoteObject",
           WellKnownObjectMode.Singleton);

 On utilise la méthode statique RegisterWellKnownServiceType. Son premier paramètre est le type de votre objet distant, donc sa classe. Il faut donc fournir un objet de type "System.type". Deux méthodes sont possibles:

* soit la classe de votre futur objet distant est dans votre projet, dans ce cas c'est facile, faites seulement typeof( votreClasse), qui retourne un objet de type "System.Type" correspondant.

*soit la classe de votre futur objet distant se trouve dans une dll. Dans ce cas, utilisez la méthode System.Type.GetType qui prend une string en paramètre. Cette string contient, en premier lieu, le nom de la classe de votre objet( y compris les espaces de nom), puis une virgule, puis le nom de votre dll contenant votre classe( sans l'extension ".dll", bien sûr).

*Et d'ailleurs, comme vous allez forcément ajouter la dll à votre projet, vous pourrez toujours vous en sortir avec typeof!

  Le deuxième paramètre de RegisterWellKnownServiceType est le nom du "point final"( end point), c'est-à-dire une string qui sera le nom par lequel vos clients accèderont à l'objet. Pourquoi "point final"? C'est parce que votre client indiquera, dans une sorte d'URL( qui s'appelle URI): le protocole, le nom de l'ordinateur serveur, son port, et enfin( à la fin, d'où le nom point final ou point de terminaison) ce nom de votre objet. Exemple d'URI que le client utilisera: "tcp://localhost:8085/RemoteObject".

  Le troisième paramètre de RegisterWellKnownServiceType est le mode( SingleCall ou Singleton). Vous trouverez plus loin des explications sur ce mode.

I.3) Laisser actif le processus de l'application serveur

- Enfin vous pouvez utiliser la méthode Readline, si votre application serveur est une application console, afin que l'application serveur ne s'arrête pas( afin que le processus continue d'exister).

  Il est à remarquer que les canaux et les objets enregistrés sont disponibles uniquement tant que le processus qui les a enregistré est actif. Dès que ce processus prend fin, l'ensemble des canaux et objets enregistrés par ce processus sont automatiquement supprimés des services distants où ils ont été enregistrés.
C'est pour cette raison que je vous propose le Readline.

System.Console.Writeline( "Touche 'Entrée' pour arrêter le serveur");
System.Console.Readline( );

II) L'objet serveur

  L'objet serveur est instancié par l'application cliente. L'application cliente communique avec cet objet serveur, qui est situé sur l'ordinateur serveur. L'application cliente passe par un objet proxy créé sur l'ordinateur client.

II.1) Comment construire l'objet serveur


  La classe de l'objet serveur( donc la classe de l'objet distant) doit hériter de la classe MarshalByRefObject, qui est la classe des objets distants. Il suffit donc de faire dériver votre classe de la classe MarshalByRefObject.

Par exemple:

public class myRemoteClass : system.MarshalByRefObject
{
  (...) //contenu de votre classe, avec vos méthodes, etc...

  public override object InitializeLifetimeService( )
  {
  //pour indiquer que la durée de vie de l'objet est illimitée
    return null;
  }
}

  Cette classe, comme étant utilisée par l'application serveur et par l'application suivante, peut être placée dans une dll, pour simplifier. Donc vous devez créer un projet "bibliothèque de classes"( "class library"), écrire votre classe d'objet distant, puis compiler votre projet. Vous obtiendrez une dll, que vous pourrez ajouter aux références de vos applications serveurs et clientes.

  On ajoute une méthode qui surcharge la méthode InitializeLifetimeService de la classe MarshalByRefObject, afin que notre objet ait une durée de vie illimitée. En effet, pour les objets distants en singleton( ce qui est notre cas), on peut préciser la durée de vie de l'objet, dans diverses circonstances. Par exemple, il est possible de définir la durée minimale de vie de l'objet entre son activation et le premier appel de méthode. Cette durée est le InitialiseLeaseTime et est par défaut de cinq minutes. D'autres durées de vie sont définissables, comme le RenewOnCallTime qui est la durée de vie minimale entre deux appels de méthode.
  Si on souhaite que l'objet distant ne soit jamais détruit pendant toute la durée de l'application serveur, il suffit de surcharger la méthode InitializeLifetimeService, et de retourner null.

II.2) Une autre façon de construire l'objet serveur

  Une variante consiste à déclarer une interface dans une dll, par exemple une interface IRemoteClass .

public interface IRemoteClass
{
  int maMethodeUn( int param1, string param2 );
  void maMethodeDeux( int monParam );
}

  Puis on déclare notre classe myRemoteClass dans le même projet que celui de l'application serveur, en n'oubliant pas d'ajouter au projet la référence à notre dll contenant l'interface.

 public class myRemoteClass : MarshalByRefObject, IRemoteClass
{
  int maMethodeUn( int param1, string param2 )
  {
//on écrit le corps de la méthode
  }

  void maMethodeDeux( int monParam )
  {
//on écrit le corps de la méthode
  }

  public override object InitializeLifetimeService( )
  {
  //pour indiquer que la durée de vie de l'objet est illimitée
    return null;
  }
}

  Dans l'application serveur, on aura, bien entendu:

RemotingConfiguration.RegisterWellKnownServiceType(
           typeof( myRemoteClass ),
           "RemoteObject",
           WellKnownObjectMode.Singleton);

  Pour ce qui concerne l'application cliente, la déclaration de l'objet proxy se fera de cette façon:

IRemoteClass objet_proxy;

et

objet_proxy = (IRemoteClass) Activator.Getobject(  typeof( IRemoteClass ), "tcp://localhost:8085/RemoteObject");

  Un "avantage" est que l'objet proxy n'est pas un MarshallByRefObject, ce qui est plus en accord avec la logique, car pas besoin qu'il dérive aussi de MarshallByRefObject car le proxy est un objet local.  Mais la première méthode est tout aussi valable et efficace, et c'est même celle décrite dans msdn. Dans tout ce tuto, je parle uniquement, sauf quand c'est précisé expressément, de la première méthode(sans les interfaces).

II.3) Remarque sur les accesseurs

  On peut remarquer qu'on peut accéder aux propriétés d'un objet distant uniquement en appelant des méthodes de cet objet. Les méthodes en question sont get et set. Je ne veux pas dire qu'on ne peut pas obtenir directement les attributs d'un objet distant. Je voulais juste faire cette remarque, je voulais juste remarquer qu'on peut tout effectuer sur un objet, uniquement en utilisant ses méthodes, y compris obtenir et fixer la valeur de ses attributs.

private string nom;

public
string Nom
{
    get
    {
        return nom;
    }
    set
    {
        nom = value;
    }
}


III) L'application cliente

  L'application cliente passe par un objet proxy créé sur l'ordinateur client. Le proxy est l'objet créé et retourné par la méthode statique Activator.GetObject. En même temps( vu sur msdn), cette méthode active l'objet distant qui est sur l'ordinateur serveur. Nous verrons que activation ne signifie pas instanciation de l'objet distant.

III.1) Le channel

TcpChannel channel = new TcpChannel( );
ChannelServices.RegisterChannel(channel);

Créez un objet TcpChannel et enregistrez votre canal auprès des services de canaux.
C'est ce canal qui sera utilisé pour la communication avec le serveur. Comment le système d'exploitation saura que c'est celui-là qu'il faudra utiliser? Eh bien, c'est parce que c'est le seul canal qui aura été enregistré par l'application cliente. Donc le système d'exploitation peut en déduire que c'est celui-là qu'il faut utiliser.

III.2) La création de l'objet proxy et l'activation de l'objet distant

RemoteClass objet_proxy; //myRemoteClass est la classe de l'objet distant

objet_proxy = (myRemoteClass) Activator.Getobject( System.Type.GetType("MonNameSpace.myRemoteClass, DllObjDistant"), "tcp://localhost:8085/RemoteObject");

ou, avec la méthode avec les interfaces décrite ci-dessus:

objet_proxy = (IRemoteClass) Activator.Getobject(  typeof( IRemoteClass ), "tcp://localhost:8085/RemoteObject");

Rappel: L'avantage de cette deuxième méthode est que l'objet proxy n'est pas un MarshallByRefObject, ce qui est plus en accord avec la logique, car pas besoin qu'il dérive aussi de MarshallByRefObject car le proxy est un objet local.  Mais la première méthode est tout aussi valable est efficace, et c'est même celle décrite dans msdn.


DllObjDistant est, comme tout-à-l'heure, le nom de l'assembly( la dll) qui contient votre classe d'objet distant, par exemple "maLib"( sans l'extension .dll). Bien entendu, dans le cas où votre classe est dans le même projet que l'application cliente, un typeof( myRemoteClass) peut remplacer le GetType. Et d'ailleurs, comme vous allez forcément ajouter la dll à votre projet, vous pourrez toujours vous en sortir avec typeof!

Bien sûr, "localhost" signifie que le serveur se situe sur l'ordinateur local( le même que le client). Si le serveur est distant, remplacez "localhost" par le nom de l'ordinateur sur lequel se situe le serveur. Exemple: "tcp://monpc:8085/RemoteObject". Remarque: "localhost" peut être remplacé par l'adresse IP 127.0.0.1 .

  Dans ce code, j'ai appelé le proxy de l'objet distant "objet_proxy". C'est pour que vous compreniez bien. Mais on aurait pu l'appeler "objet_distant", en "oubliant" qu'il y a un proxy, et en imaginant cette variable comme étant l'objet distant.

  A quoi sert l'objet proxy? Il nous permet de passer par un objet local, pour atteindre l'objet distant. C'est donc une copie locale de l'objet distant. Cela présente des intérêts techniques, et aussi pratiques( on peut par exemple exprimer ce qu'on veut faire, ce qu'on veut appeler comme méthode, etc). L'objet proxy n'est donc pas une fin en soi. C'est un intermédiaire, comme tout proxy. Quand on parle de serveur internet, on ne pense d'ailleurs presque jamais aux serveurs internet proxy intermédiaires. On fait comme si l'internaute était directement relié au serveur internet final!

  La méthode GetObject ( de la classe Activator) est une méthode d'activation de l'objet distant ( vu sur msdn).
Mais, en réalité, l'objet distant n'est pas instancié à ce moment là( vu sur msdn). Aucune connexion réseau n'a lieu. Une connexion réseau a lieu seulement lors de l'appel à une méthode de l'objet. Dans ce cas, l'objet distant est instancié si cela est nécessaire, selon que l'objet distant a été déclaré en singlecall ou pas. Si l'objet est en singlecall, l'objet distant est créé à chaque appel de méthode de cet objet. Et l'objet distant est détruit après chaque appel de méthode effectué( msdn). Les objets singleton sont créés une seule fois. Et dans le cas du singleton, tous les clients utilisent donc le même objet distant( msdn).

IV.IV) Précisions sur les channels

         Je vais préciser certains points sur les channels. Comme le précise MSDN, « la classe TcpChannel est une classe pratique combinant les fonctionnalités des classes TcpClientChannel et TcpServerChannel ». Le constructeur que vous utilisez détermine quel(s) type(s) de canal vous aurez :

 

-          Le constructeur sans paramètre TcpChannel( ) : crée seulement un canal client, et pas un canal serveur( voir MSDN). J’ajoute que c’est pour cette raison que vous ne précisez pas de numéro de port : le numéro de port n’a de sens que pour un canal serveur !(c’est le lieu de communication des données).

 

 

-          Le constructeur TcpChannel( Int32) : crée un canal serveur qui écoute sur le port spécifié( voir MSDN). Ce constructeur est utilisé typiquement dans le cadre de l’application serveur. Mais il peut l’être aussi dans une application cliente, dans le cas( paragraphe ci-dessous) où on veut passer un objet du client en paramètre à une méthode distante.

J’ajoute que ce constructeur crée aussi un canal client, ce qui permet d’appeler les méthodes distantes de l’objet serveur, dans le cas d’un client qui s’est créé son channel ainsi pour pouvoir passer en paramètre un objet à une méthode distante(voir cas ci-dessous).

 

·         Il est indispensable d’utiliser ce constructeur dans le cas d’un client qui va passer à une méthode distante  un objet par référence. En effet, le client se comporte alors en serveur, et l’objet passé à la méthode devient un objet distant pour le serveur ! Un exemple vous est donné ci-dessous :

 

//ici on est obligé de préciser le numéro de port,

                //pour pouvoir passer en paramètre un objet par référence, à une méthode distante: dans ce cas, on a besoin

                //d'un canal serveur également

                TcpChannel channel = new TcpChannel(1);

               

                //Marche pas TcpChannel channel = new TcpChannel(); //ne marche pas ici,

                //essayé!!!: server internal error ds ce client à l'exécution!!!

                //mais marche dès lors que je ne passe pas d’objet en