Bi-directional WCF Client-Server Communication
- by Bill
I have been working for weeks on creating a client/server to control a music-server application located on the server-side that is controlled by several client apps located across the LAN. I've been successful in getting the client-side to communicate with the Server, sending commands to operate the music-server, and through the use of callbacks, reply to the clients so that all of the client UI's can be appropriately updated. My problem is however, that I unable to figure-out how to broadcast other messages that need to be sent from the server app to the clients. I was hoping to utilize the callback method; however I have not been able to access it from the server side. Do I need to modify or create another contract that provides for communication from the server to the clients? Does the binding require modification? As I mentioned earlier, I have truly been working on this for weeks (which is beginning to feel like 'years'), and hope to get this last piece of the application working. Would someone please steer me in the right direction?
Client Side SERVICE REFERENCE:
<?xml version="1.0" encoding="utf-8"?>
<ServiceReference>
<ProxyGenerationParameters
ServiceReferenceUri="http://localhost:8001/APService/mex"
Name="APGateway"
NotifyPropertyChange="True"
UseObservableCollection="False">
</ProxyGenerationParameters>
<EndPoints>
<EndPoint
Address="net.tcp://localhost:8000/APService/service"
BindingConfiguration="TcpBinding"
Contract="APClient.APGateway.APUserService"
>
</EndPoint>
<EndPoint
Address="http://localhost:8001/APService/service"
BindingConfiguration="HttpBinding"
Contract="APClient.APGateway.APUserService"
>
</EndPoint>
</EndPoints>
</ServiceReference>
Client Side AP CONFIG
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="APClient.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<system.serviceModel>
<client>
<endpoint
address="net.tcp://localhost:8000/APService/service"
binding="netTcpBinding"
contract="APClient.APGateway.APUserService"
name="TcpBinding" />
<endpoint
address="http://localhost:8001/APService/service"
binding="wsDualHttpBinding"
contract="APClient.APGateway.APUserService"
name="HttpBinding" />
</client>
</system.serviceModel>
<applicationSettings>
<APClient.Properties.Settings>
<setting name="pathToDatabase" serializeAs="String">
<value>C:\Users\Bill\Documents\APData\</value>
</setting>
</APClient.Properties.Settings>
</applicationSettings>
Server Side AP.CONFIG
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8001/APService/mex" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MetadataBehavior" name="APService.APService">
<endpoint address="service" binding="netTcpBinding" name="TcpBinding"
contract="APService.IAPServiceInventory" />
<endpoint address="service" binding="wsDualHttpBinding" name="HttpBinding"
contract="APService.IAPServiceInventory" />
<endpoint address="mex" binding="mexHttpBinding" name="MexBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8000/APService/" />
<add baseAddress="http://localhost:8001/APService/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
Server Side APSERVICE.CS
namespace APService
{
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single,InstanceContextMode=InstanceContextMode.PerCall)]
public class APService : IAPServiceInventory
{
private static List<IClientCallback> _callbackList = new List<IClientCallback>();
private static int _beerInventory = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["InitialBeerInventory"]);
public APService() {}
public int SubscribeToServer(string guestName)
{
IClientCallback guest = OperationContext.Current.GetCallbackChannel<IClientCallback>();
if(!_callbackList.Contains(guest)) { _callbackList.Add(guest); }
else { Console.WriteLine(guest + " is already logged onto the Server."); }
_callbackList.ForEach(delegate(IClientCallback callback) { callback.NotifyGuestJoinedParty(guestName); });
}
public void UpdateClients(string guestName,string UpdateInfo)
{
_callbackList.ForEach(delegate(IClientCallback callback) { callback.NotifyUpdateClients(guestName,UpdateInfo); });
}
public void SendRequestToServer(string guestName, string request)
{
_callbackList.ForEach(delegate(IClientCallback callback) { callback.NotifyRequestMadeToServer(guestName,request); });
if(request == "Play") { APControl.Play(); }
else if(request == "Stop") { APControl.Stop(); }
else if(request == "Pause") { APControl.PlayPause(); }
else if(request == "Next Track") { APControl.NextTrack(); }
else if(request == "Previous Track") { APControl.PreviousTrack(); }
else if(request == "Mute") { APControl.Mute(); }
else if(request == "Volume Up") { APControl.VolumeUp(5); }
else if(request == "Volume Down") { APControl.VolumeDown(5); }
}
public void CancelServerSubscription(string guestName)
{
IClientCallback guest = OperationContext.Current.GetCallbackChannel<IClientCallback>();
if(_callbackList.Contains(guest)) { _callbackList.Remove(guest); }
_callbackList.ForEach(delegate(IClientCallback callback) { callback.NotifyGuestLeftParty(guestName); });
}
}
Server Side IAPSERVICE.CS
namespace APService
{
[ServiceContract(Name="APUserService",Namespace="http://AP.com/WCFClientServer/",SessionMode=SessionMode.Required, CallbackContract=typeof(IClientCallback))]
public interface IAPServiceInventory
{
[OperationContract()]
int SubscribeToServer(string guestName);
[OperationContract(IsOneWay=true)]
void SendRequestToServer(string guestName,string request);
[OperationContract(IsOneWay=true)]
void UpdateClients(string guestName,string UpdateInfo);
[OperationContract(IsOneWay=true)]
void CancelServerSubscription(string guestName);
}
}
Server side - IAPServiceCallback.cs
namespace APService
{
public interface IClientCallback
{
[OperationContract(IsOneWay=true)]
void NotifyGuestJoinedParty(string guestName);
[OperationContract(IsOneWay=true)]
void NotifyUpdateClients(string guestName,string UpdateInfo);
[OperationContract(IsOneWay=true)]
void NotifyRequestMadeToServer(string guestName,string request);
[OperationContract(IsOneWay=true)]
void NotifyGuestLeftParty(string guestName);
}