- Create new C# Library Project
- Add Reference to System.ServiceModel
- Define your service interface
namespace PubSubLibrary
{
[ServiceContract(
CallbackContract = typeof(IPubSubCallback),
SessionMode=SessionMode.Required)]
public interface IPubSubService
{
[OperationContract(IsOneWay = true)]
void Subscribe(string clientId);
[OperationContract(IsOneWay = true)]
void Publish(string clientId, string message);
}
}
- Define your callback interface
namespace PubSubLibrary
{
public interface IPubSubCallback
{
[OperationContract(IsOneWay = true)]
void Broadcast(CallbackData callbackData);
}
[DataContract]
public class CallbackData
{
[DataMember]
public string ClientId { get; set; }
[DataMember]
public string Message { get; set; }
}
}
- Create Server C# Console Project
- Add Reference to Library project and System.ServiceModel
- Add App.config item
- Configure App.config (right click and select Edit WCF Configuration)
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MetadataBehavior" name="PubSubServer.PubSubService">
<clear />
<endpoint address="mex" binding="ws2007HttpBinding" contract="IMetadataExchange"
listenUriMode="Explicit">
</endpoint>
<endpoint address="net.tcp://localhost:8086" binding="netTcpBinding"
contract="PubSubLibrary.IPubSubService" listenUriMode="Explicit">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8085/pubsub" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
- Implement your service class
namespace PubSubServer
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class PubSubService : IPubSubService
{
Dictionary<string, Subscription> subscriptions = new Dictionary<string, Subscription>();
public void Subscribe(string clientId)
{
Subscription subscription = new Subscription();
subscription.Callback = OperationContext.Current.GetCallbackChannel<IPubSubCallback>();
subscription.ClientId = clientId;
if(subscriptions.ContainsKey(clientId))
{
subscriptions.Remove(clientId); //remove old connection
}
subscriptions.Add(clientId, subscription);
Console.WriteLine("{0}: Connected", clientId);
}
public void Publish(string clientId, string message)
{
Console.WriteLine("{0}: {1}", clientId, message);
CallbackData callbackData = new CallbackData();
callbackData.ClientId = clientId;
callbackData.Message = message;
Callback(callbackData);
}
public void Callback(CallbackData callbackData)
{
for (int i = subscriptions.Count - 1; i >= 0; i--)
{
string key = subscriptions.Keys.ToArray()[i];
Subscription subscription = subscriptions[key];
try
{
subscription.Callback.Broadcast(callbackData);
}
catch (Exception)
{
subscriptions.Remove(subscription.ClientId);
Console.WriteLine("{0}: Disconnected", subscription.ClientId);
}
}
}
}
class Subscription
{
public string ClientId { get; set; }
public IPubSubCallback Callback { get; set; }
}
}
namespace PubSubServer
{
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(PubSubService));
host.Open();
Console.WriteLine("Server running..");
Console.ReadLine();
}
}
}
- Start your server
- Create WPF client project
- Add Reference to System.ServiceModel and System.Runtime.Serialization
- Generate stubs
- Right click on References and select Add Service Reference
- Specify address http://localhost:8085/pubsub?wsdl
- Alternatively generate stubs/configuration using command line svcutil tool in visual studio command prompt
svcutil /t:code http://localhost:8085/pubsub?wsdl /out:PubSubServiceProxy.cs /config:App.config /async
- Add generated App.config and PubSubServiceProxy.cs to your client project
- Implement Callback
namespace PubSubClient
{
internal class PubSubCallback : IPubSubServiceCallback
{
#region Delegates
public delegate void CallbackEventHandler(object source, CallbackEvent e);
#endregion
#region IPubSubServiceCallback Members
public void Broadcast(CallbackData callbackData)
{
fireCallbackEvent(callbackData);
}
#endregion
public event CallbackEventHandler callback;
private void fireCallbackEvent(CallbackData callbackData)
{
if (callback != null)
{
callback(this, new CallbackEvent(callbackData));
}
}
}
public class CallbackEvent : EventArgs
{
private readonly CallbackData callbackData;
public CallbackEvent(CallbackData callbackData)
{
this.callbackData = callbackData;
}
public CallbackData CallbackData
{
get { return callbackData; }
}
}
}
- Implement your client WPF application
<Window x:Class="PubSubClient.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PubSub Client" Height="326" Width="579" ResizeMode="CanResizeWithGrip">
<DockPanel LastChildFill="True">
<StatusBar DockPanel.Dock="Bottom" Height="18"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox x:Name="clientId" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></TextBox>
<Button x:Name="subscribe" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Subscribe" Click="subscribe_Click"></Button>
<TextBox x:Name="message" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" PreviewKeyDown="message_PreviewKeyDown"></TextBox>
<Button x:Name="publish" Grid.Row="1" Grid.Column="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Send" Click="publish_Click"></Button>
<ListBox x:Name="messages" Grid.Row="2" Grid.ColumnSpan="4" />
</Grid>
</DockPanel>
</Window>
namespace PubSubClient
{
public partial class Window1 : Window
{
private PubSubServiceClient client;
private bool isConnected;
public Window1()
{
InitializeComponent();
PubSubCallback callback = new PubSubCallback();
callback.callback += new PubSubCallback.CallbackEventHandler(callback_callback);
InstanceContext context = new InstanceContext(callback);
client = new PubSubServiceClient(context);
clientId.Text = Environment.UserName;
publish.IsEnabled = false;
}
void callback_callback(object source, CallbackEvent e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
{
AddMessage(string.Format("{0}: {1}", e.CallbackData.ClientId, e.CallbackData.Message));
}));
}
private void subscribe_Click(object sender, RoutedEventArgs e)
{
Subscribe();
}
private void publish_Click(object sender, RoutedEventArgs e)
{
Publish(message.Text);
}
private void message_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
Publish(message.Text);
e.Handled = true;
}
}
private void Subscribe()
{
try
{
client.Subscribe(clientId.Text);
isConnected = true;
clientId.IsReadOnly = true;
subscribe.IsEnabled = false;
publish.IsEnabled = true;
}
catch (Exception ex)
{
MessageBox.Show("Failed to Connect.");
isConnected = false;
}
}
private void Publish(string text)
{
if (isConnected)
{
try
{
client.Publish(clientId.Text, text);
message.Clear();
}catch(Exception ex)
{
MessageBox.Show("Server is not responding");
}
}
}
private void AddMessage(string message)
{
messages.Items.Add(message);
messages.ScrollIntoView(message);
}
}
}
- Start your client app, specify clientId (your username by default) and click subscribe. To send a message type a message and click Send.
Comments
Post a Comment