やってみたかったWCFでP2P。P2Pの利用法としてはベタなチャットソフト作成して方法を確認しておきます。
1.通信内容を決める
WCFで通信するときはコントラクトとして通信内容をInterfaceで実装するんでした、こちら。
ITocsChat.cs
namespace WcfP2PChat { using System.ServiceModel; [ServiceContract(CallbackContract=typeof(ITocsChat))] public interface ITocsChat { [OperationContract(IsOneWay = true)] void Join(string userName); [OperationContract(IsOneWay = true)] void Chat(string userName, string message); [OperationContract(IsOneWay = true)] void Leave(string userName); } }
このInterfaceとP2Pの送信・応答チャネル定義をするIClientChannelの2つのInterfaceをもつInterfaceを定義しておきます。P2Pではこの2つのInterafaceを持つほうを使います。ただし、このInterfaceクラスを明示的に実装することはありません。
ITocsChatChannel.cs
namespace WcfP2PChat { using System.ServiceModel; public interface ITocsChatChannel : ITocsChat, IClientChannel { } }
2.P2Pのエンドポイントを構成する
コードからでも問題ないと思いますが、今回はアプリケーション構成ファイルを使います。
App.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <client> <endpoint name="TocsSimpleChatApp" address="net.p2p://TocsMesh/TocsChat" binding="netPeerTcpBinding" bindingConfiguration="TocsChatBinding" contract="WcfP2PChat.ITocsChat"/> </client> <bindings> <netPeerTcpBinding> <binding name="TocsChatBinding" port="0"> <resolver mode="Auto"/> <security mode="None"/> </binding> </netPeerTcpBinding> </bindings> </system.serviceModel> </configuration>
ChatForm.cs(抜粋)
public partial class ChatForm : Form, ITocsChat { private ITocsChatChannel m_participant; public ChatForm() { InitializeComponent(); var site = new InstanceContext(this); var binding = new NetPeerTcpBinding("TocsChatBinding"); var channelFactory = new DuplexChannelFactory<ITocsChatChannel>(site, "TocsSimpleChatApp"); m_participant = channelFactory.CreateChannel(); var statusHandler = m_participant.GetProperty<IOnlineStatus>(); statusHandler.Online += statusHandler_Online; statusHandler.Offline += statusHandler_Offline; } }
多くの方に理解して頂けるようWindows Forms にしました。FormはITocsChatを実装するようにしていますが、これは他ユーザから送られてきたメッセージをFormで処理します、ということを表しています。またm_participantとしてITocsChannelオブジェクトを取得していますが、これは他ユーザ(群)を表していると考えて良いと思います。
3.チャットソフトを作成する
あとはこまごまとした作りこみを少々。本来なら状態に応じてコントロールの有効/無効切り替えや日時表示etc..機能を盛り込むでしょうが、わかりやすさ重視で実装するとこんな感じ。
ChatForm.cs(全体)
namespace WcfP2PChat { using System; using System.Windows.Forms; using System.ServiceModel; public partial class ChatForm : Form, ITocsChat { private ITocsChatChannel m_participant; public ChatForm() { InitializeComponent(); var site = new InstanceContext(this); var binding = new NetPeerTcpBinding("TocsChatBinding"); var channelFactory = new DuplexChannelFactory<ITocsChatChannel>(site, "TocsSimpleChatApp"); m_participant = channelFactory.CreateChannel(); var statusHandler = m_participant.GetProperty<IOnlineStatus>(); statusHandler.Online += statusHandler_Online; statusHandler.Offline += statusHandler_Offline; } void statusHandler_Online(object sender, EventArgs e){ textBoxChatLog.Text += "接続しました" + Environment.NewLine; } void statusHandler_Offline(object sender, EventArgs e){ textBoxChatLog.Text += "切断されました" + Environment.NewLine; } public void Join(string userName){ textBoxChatLog.Text += userName + "さんが参加されました" + Environment.NewLine; } public void Chat(string userName, string message){ textBoxChatLog.Text += userName + ":" + message + Environment.NewLine; } public new void Leave(string userName){ textBoxChatLog.Text += userName + "さんが退出されました" + Environment.NewLine; } private void buttonJoin_Click(object sender, EventArgs e){ m_participant.Join(textBoxUserName.Text); } private void buttonLeave_Click(object sender, EventArgs e){ m_participant.Leave(textBoxUserName.Text); } private void buttonSay_Click(object sender, EventArgs e){ m_participant.Chat(textBoxUserName.Text, textBoxSay.Text); } } }
Formが実装するITocsChat Interfaceは他ユーザからの操作に対するcallbackとなるので上のような処理となります。これを動かした模様はこちら。
こんなに少ないコードながらに結構まともに見えます。
補足 – WindowsXPの場合
上記のようにWCFのP2Pでデフォルトリゾルバを使った場合、PNRP Ver2.0となりますがこれはWinXP sp3もしくはKB920342を入れる必要があるようです(こちら)。またPNRPがIPv6の上に構築されるのでIPv6もインストール必要です(こちら)。少なくともこれらが必要なのは間違いありませんが、それで正しく動くかは確認できていません。