A few days ago I was reading up on some WCF technology called Peer Channel. It looked pretty cool so I decided to try it out. I was pretty shocked to find out how easy it was to build a chat messaging service using this this stuff. I was able to run a chat client on my computer, a chat client on my friends computer, and the WCF Chat service on a server. We were able to send messages back and forth to each other (all on the same network of course). It worked pretty fast without using a lot of resouces on either computer or over the network. I threw a WPF interface on it to look nice, which was really easy to do as well. I just wanted to post up some of the code here on my blog in case anyone wanted to see how it works.
Client 1 and Client 2:


Service that runs on the server:

Only needed a few files to make it work:

Here's the code to the important files:
PeerChannel – Client – App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<!-- chat instance participating in the mesh -->
<endpoint name="ChatEndpoint" address="net.p2p://chatMesh/MyChatServer/Chat" binding="netPeerTcpBinding" bindingConfiguration="BindingCustomResolver" contract="MyChat.IChat">
</endpoint>
</client>
<bindings>
<netPeerTcpBinding>
<!-- Refer to Peer channel security samples on how to configure netPeerTcpBinding for security -->
<binding name="BindingCustomResolver" port="0">
<security mode="None"/>
<resolver mode="Custom">
<custom address="net.tcp://SERVERNAMEGOESHERE/MyChatServer/peerResolverService" binding="netTcpBinding" bindingConfiguration="Binding3"/>
</resolver>
</binding>
</netPeerTcpBinding>
<netTcpBinding>
<!-- You can change security mode to enable security -->
<binding name="Binding3">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
|
PeerChannel – Client – ChatClient.xaml.cs
using System; using System.DirectoryServices; using System.Security.Principal; using System.Windows; using System.Windows.Input; namespace MyChat { public partial class ChatClient : Window { PeerChat chat; public ChatClient() { InitializeComponent(); } private void btnJoin_Click(object sender, RoutedEventArgs e) { if (!String.IsNullOrEmpty(txtName.Text)) { chat = new PeerChat(txtName.Text); chat.OnChatOnline += new PeerChat.ChatOnline(chat_OnChatOnline); chat.OnChatOffline += new PeerChat.ChatOffline(chat_OnChatOffline); chat.OnJoin += new PeerChat.JoinChat(chat_OnJoin); chat.OnLeave += new PeerChat.LeaveChat(chat_OnLeave); chat.OnChat += new PeerChat.ChatSendReceive(chat_OnChat); string result = chat.Start(); txtChat.Text += result; } } void chat_OnChat(string member, string msg) { txtChat.Text += String.Format("[{0}] {1}" + Environment.NewLine, member, msg); txtChat.ScrollToEnd(); } void chat_OnLeave(string member) { txtChat.Text += String.Format("[{0} left]" + Environment.NewLine, member); } void chat_OnJoin(string member) { txtChat.Text += String.Format("[{0} joined]" + Environment.NewLine, member); btnSend.IsEnabled = true; } void chat_OnChatOffline(object sender, EventArgs e) { txtChat.Text += String.Format("** Offline" + Environment.NewLine); } void chat_OnChatOnline(object sender, EventArgs e) { txtChat.Text += String.Format("** Online" + Environment.NewLine); } private void btnSend_Click(object sender, RoutedEventArgs e) { chat.SendMessage(txtMessage.Text); txtMessage.Text = String.Empty; } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (chat != null) chat.End(); } private void txtMessage_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { chat.SendMessage(txtMessage.Text); txtMessage.Text = String.Empty; } } } }
|
PeerChannel – Client – PeerChat.cs
using System; using System.ServiceModel; namespace MyChat { [ServiceContract(Namespace = "MyChat", CallbackContract = typeof(IChat))] public interface IChat { [OperationContract(IsOneWay = true)] void Join(string member); [OperationContract(IsOneWay = true)] void Chat(string member, string msg); [OperationContract(IsOneWay = true)] void Leave(string member); } public interface IChatChannel : IChat, IClientChannel { } public class PeerChat : IChat { private bool IsConnected { get; set; } string member; IChatChannel participant; DuplexChannelFactory<IChatChannel> factory; public delegate void JoinChat(string member); public delegate void ChatSendReceive(string member, string msg); public delegate void LeaveChat(string member); public delegate void ChatOnline(object sender, EventArgs e); public delegate void ChatOffline(object sender, EventArgs e); public event JoinChat OnJoin; public event ChatSendReceive OnChat; public event LeaveChat OnLeave; public event ChatOnline OnChatOnline; public event ChatOffline OnChatOffline; public PeerChat(string member) { this.member = member; } public string Start() { string result = String.Empty; InstanceContext instanceContext = new InstanceContext(this); factory = new DuplexChannelFactory<IChatChannel>(instanceContext, "ChatEndpoint"); participant = factory.CreateChannel(); IOnlineStatus ostat = participant.GetProperty<IOnlineStatus>(); ostat.Online += new EventHandler(OnOnline); ostat.Offline += new EventHandler(OnOffline); try { participant.Open(); IsConnected = true; } catch (CommunicationException) { result += "Could not find resolver." + Environment.NewLine + "Please make sure the Chat Server is running before using this chat client." + Environment.NewLine + "Refer to the readme for more details." + Environment.NewLine; IsConnected = false; return result; } participant.Join(member); return result; } public void SendMessage(string message) { participant.Chat(member, message); } public void End() { try { if (IsConnected) { if (participant != null) { participant.Leave(member); participant.Close(); participant.Dispose(); } if (factory != null) { factory.Close(); } } IsConnected = false; } catch (Exception) { } } public void Join(string member) { if (OnJoin != null) OnJoin(member); } public void Chat(string member, string msg) { if (OnChat != null) OnChat(member, msg); } public void Leave(string member) { if (OnLeave != null) OnLeave(member); } public void OnOnline(object sender, EventArgs e) { if (OnChatOnline != null) OnChatOnline(member, e); } public void OnOffline(object sender, EventArgs e) { if (OnChatOffline != null) OnChatOffline(member, e); } } }
|
PeerChannel – WCF Service Console – App.config
<?xml version="1.0"?> <configuration> <system.serviceModel> <services> <service name="System.ServiceModel.PeerResolvers.CustomPeerResolverService"> <host> <baseAddresses> <add baseAddress="net.tcp://SERVERNAMEGOESHERE/MyChatServer/peerResolverService"/> </baseAddresses> </host> <!-- use base address provided by the host --> <endpoint address="net.tcp://SERVERNAMEGOESHERE/MyChatServer/peerResolverService" binding="netTcpBinding" bindingConfiguration="Binding1" contract="System.ServiceModel.PeerResolvers.IPeerResolverContract"/> </service> </services> <bindings> <netTcpBinding> <!-- You can change security mode to enable security --> <binding name="Binding1"> <security mode="None"/> </binding> </netTcpBinding> </bindings> </system.serviceModel> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
|
PeerChannel – WCF Service Console – DefaultCustomResolver.cs
using System; using System.ServiceModel; using System.ServiceModel.PeerResolvers; namespace MyChatServer { class DefaultCustomResolver { public static void Main() { // Create a new resolver service CustomPeerResolverService crs = new CustomPeerResolverService(); crs.ControlShape = false; // Create a new service host ServiceHost customResolver = new ServiceHost(crs); // Open the resolver service crs.Open(); customResolver.Open(); Console.WriteLine("Custom resolver service is started"); Console.WriteLine("Press <ENTER> to terminate service"); Console.ReadLine(); } } }
|