WCF Communication bi-directionnelle28 décembre 2010
J’écris cette petite note afin de regrouper au sein d’un même article tout ce qu’il faut savoir pour créer une application client/server WCF au sein de laquelle le client parle au service et le service parle au client. Cela s’appelle une communication bi-directionnelle que la technologie WCF a introduit par rapport au protocole SOAP des webservices d’antan.
Les exemples suivants ont été réalisés au sein d’une application console faite en .NET 3.5.
Téléchargement : Projet WCF Communication bi-directionnelle 1.0 (26)
Prérequis
Ajouter une référence à System.ServiceModel v3.0.0.0 à votre projet Service et Client.

Configuration App.config
Que ce soit pour la configuration du service ou du client, vous devrez ajouter une balise <system.serviceModel> juste sous le noeud <configuration> afin d’y placer les éléments de configuration service ou client.
Configuration du service
<services>
<service name="ConsoleApplication1.SampleDevilContract">
<endpoint address="net.tcp://localhost:666/devil"
contract="ConsoleApplication1.IDevilContract"
binding="netTcpBinding" />
</service>
</services>
Configuration du client
<client>
<endpoint address="net.tcp://localhost:666/devil"
binding="netTcpBinding"
contract="ConsoleApplication1.IDevilContract"
name="devilEP" />
</client>
Exemple de fichier de config complet
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<!-- client side config -->
<client>
<endpoint address="net.tcp://localhost:666/devil"
binding="netTcpBinding"
contract="ConsoleApplication1.IDevilContract"
name="devilEP" />
</client>
<!-- server side config -->
<services>
<service name="ConsoleApplication1.SampleDevilContract">
<endpoint address="net.tcp://localhost:666/devil"
contract="ConsoleApplication1.IDevilContract"
binding="netTcpBinding" />
</service>
</services>
</system.serviceModel>
</configuration>
Interfaces du service
Le service
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace ConsoleApplication1
{
/// <summary>
/// Interface à implémenter coté service et à utiliser côté client pour communiquer avec le service.
/// </summary>
[ServiceContract(CallbackContract = typeof(IDevilCallbackContract))]
public interface IDevilContract
{
/// <summary>
/// IsOneWay définit à true permet au client d'envoyer le message (ici SubscribeDevil) sans attendre un retour.
/// </summary>
[OperationContract(IsOneWay = true)]
void SubscribeDevil();
[OperationContract(IsOneWay = true)]
void UnsubscribeDevil();
}
}
Le callback
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace ConsoleApplication1
{
/// <summary>
/// Interface de callback à implémenter côté client.
/// </summary>
public interface IDevilCallbackContract
{
/// <summary>
/// Les méthodes de callback doivent être marquées avec OperationContract.
/// </summary>
/// <param name="message"></param>
[OperationContract]
void OnCallback(string message);
}
}
Implémentation du service
Cette implémentation du service est à définir dans le projet serveur.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ServiceModel;
namespace ConsoleApplication1
{
/// <summary>
/// Implémentation du service exposé aux clients.
/// InstanceContextMode.Single pour indiquer que l'instance de notre service est unique pour tous les clients.
/// ConcurrencyMode.Reentrant pour autoriser l'appel retour au client au sein d'une méthode appelée par ce client.
/// </summary>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
class SampleDevilContract : IDevilContract
{
private bool runningContract;
#region IDevilContract Members
public void SubscribeDevil()
{
Console.WriteLine("Service : SubscribeDevil");
runningContract = true;
while (runningContract)
{
// OperationContext.Current renvoie le context de communication retour afin que le service puisse
// communiquer avec le client.
if (OperationContext.Current == null)
return;
var callBack = OperationContext.Current.GetCallbackChannel<IDevilCallbackContract>();
callBack.OnCallback("You are mine.");
Thread.Sleep(2000);
}
}
public void UnsubscribeDevil()
{
Console.WriteLine("Service : UnsubscribeDevil");
runningContract = false;
}
#endregion
}
}
Implémentation du Callback
Cette implémentation est à définir dans votre projet client. La méthode OnCallback sera appelé par le service afin de notifier le client d’un évènement quelconque.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace ConsoleApplication1
{
/// <summary>
/// Implémentation du callback à définir sur l'application cliente.
/// </summary>
class SampleClientCallbackImpl : IDevilCallbackContract
{
#region IDevilCallbackContract Members
public void OnCallback(string message)
{
Console.WriteLine("Client : {0}", message);
}
#endregion
}
}
Etablissement de la connexion
Lancement du service
var singleton = new SampleDevilContract(); var host = new ServiceHost(singleton); host.Open();
Le service se lance avec le paramétrage définit dans le fichier de configuration, via l’attribut name du noeud <service>.
Lancement du client
var clientCallbackImpl = new SampleClientCallbackImpl();
// devilEP est le nom du EndPoint définit dans le fichier app.config.
var duplexChannel = new DuplexChannelFactory<IDevilContract>(clientCallbackImpl, "devilEP");
IDevilContract devilContract = duplexChannel.CreateChannel();
devilContract.SubscribeDevil();
Console.WriteLine("Press any key to end.");
Console.ReadKey();
devilContract.UnsubscribeDevil();
La connexion au service est effectuée lors de l’appel à une méthode, pas avant. Si le service n’est pas lancé au moment de la connexion du client, une exception est lancée : EndpointNotFoundException.
Aucun commentaire pour le moment. Soyez le/la premi(er/ère).