はじめに
プッシュ通知モデルは、一般的なメッセージングモデルです。 要求に応じて情報を受信することを意味するのではなく、この情報がサーバーに表示されたらすぐに送信者に送信します。
wsDualHttpBindingを使用した標準的なアプローチ
プッシュメカニズムを作成する機能もWCFによって提供されます。 このフレームワークにより、wsDualHttpBindingコントラクトを使用してプッシュサービスを作成できます。 このようなコントラクトにより、各リクエストは、イベントが発生したときに呼び出されるコールバックメソッドを決定できます。
このメカニズムをメッセージングシステムに適用すると、次のアルゴリズムが得られます。
-新しいメッセージのリクエストごとに、コールバックが作成され、新しいメッセージのサブスクライバーのリストに保存されます。
-新しいメッセージを受信すると、システムはサブスクライバーのリストを調べて、必要なメッセージ受信者(および必要なコールバック)を見つけます。
-必要なコールバックメソッドを呼び出します。
以下は、WCFサービスでwsDualHttpBindingを使用する例です。
-新しいメッセージを要求するコールバックメソッドを作成する
interface IMessageCallback { [OperationContract(IsOneWay = true)] void OnMessageAdded(int senderId, string message, DateTime timestamp); }
-サービス契約を作成します
[ServiceContract(CallbackContract = typeof(IMessageCallback))] public interface IMessageService { [OperationContract] void AddMessage(int senderId, int recipientId, string message); [OperationContract] bool Subscribe(); }
-サービス自体を作成します
Public class MessageService : IMessageService { private static List<IMessageCallback> subscribers = new List<IMessageCallback>(); public bool Subscribe(int id) { try { IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>(); callback.id = id; if (!subscribers.Contains(callback)) subscribers.Add(callback); return true; } catch { return false; } } public void AddMessage(int senderId, int recipientId, string message) { subscribers.ForEach(delegate(IMessageCallback callback) { if ((((ICommunicationObject)callback).State == CommunicationState.Opened) && (callback.id == recipientId)) { callback.OnMessageAdded(recipientId, message, DateTime.Now); } else { subscribers.Remove(callback); } }); } }
-web.configファイルでサービスを構成します
<system.serviceModel> <services> <service name="WCFPush.MessageService" behaviorConfiguration="Default"> <endpoint address ="" binding="wsDualHttpBinding" contract="WCFPush.IMessage"> </endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="Default"> <serviceMetadata httpGetEnabled="True"/> <serviceDebug includeExceptionDetailInFaults="False" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
サービスの準備ができました。
ただし、このモデルは、サーバーとサブスクライバーの両方が.NETアプリケーションである場合にのみ機能します。
RESTfulアプローチの使用
上記の方法は、たとえばサブスクライバがモバイルデバイスであり、REST形式の要求のみを使用できる場合には適していません。
この場合、非同期REST要求が役立ちます。
そのため、RESTのみに基づいて、機能が以前のものと同様のサービスを作成します。
非同期モデルの場合、リクエストはBeginRequestNameとEndRequestNameの2つの部分で構成されます。
-RESTサービスのServiceContractを定義する
[ServiceContract] public interface IMessageService { [WebGet(UriTemplate = "AddMessage?senderId={senderId}&recipientId={recipientId}&message={message}")] [OperationContract ] bool AddMessage(int senderId, int recipientId, string message); [WebGet(UriTemplate = "Subscribe?id={id}")] [OperationContract(AsyncPattern = true)] IAsyncResult BeginGetMessage(int id, AsyncCallback callback, object asyncState); ServiceMessage EndGetMessage(IAsyncResult result); }
注:EndGetMessageはOperationContact属性でマークされていません。
-IAsyncResultインターフェイスを実装する非同期結果のクラスを作成します
public class MessageAsyncResult : IAsyncResult { public AsyncCallback Callback { get; set; } private readonly object accessLock = new object(); private bool isCompleted = false; private ServiceMessage result; private int recipientId; private object asyncState; public MessageAsyncResult(object state) { asyncState = state; } public int RecipientId { get { lock (accessLock) { return recipientId; } } set { lock (accessLock) { recipientId = value; } } } public ServiceMessage Result { get { lock (accessLock) { return result; } } set { lock (accessLock) { result = value; } } } public bool IsCompleted { get { lock (accessLock) { return isCompleted; } } set { lock (accessLock) { isCompleted = value; } } } public bool CompletedSynchronously { get { return false; } } public object AsyncState { get { return asyncState; } } public WaitHandle AsyncWaitHandle { get { return null; } } }
インターフェイスの実装に加えて、このクラスは、メッセージ受信者のID(recipientId)、および送信者に配信されるメッセージ自体(結果)も格納します。
-今、サービス自体を実装します
[ServiceBehavior( InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)] public class MessageService : IMessageService { private static List<MessageAsyncResult> subscribers = new List<MessageAsyncResult>(); public bool AddMessage(int senderId, int recipientId, string message) { subscribers.ForEach(delegate(MessageAsyncResult result) { if (result.RecipientId == recipientId) { result.Result = new ServiceMessage(senderId, recipientId, message, DateTime.Now); result.IsCompleted = true; result.Callback(result); subscribers.Remove(result); } }); return true; } public IAsyncResult BeginGetMessage(int id, AsyncCallback callback, object asyncState) { MessageAsyncResult asyncResult = new MessageAsyncResult(asyncState); asyncResult.Callback = callback; asyncResult.RecipientId = id; subscribers.Add(asyncResult); return asyncResult; } public ServiceMessage EndGetMessage(IAsyncResult result) { return (result as MessageAsyncResult).Result; } }
新しいメッセージの要求が到着すると、非同期結果が作成され、サブスクライバーのリストに追加されます。 このサブスクライバーにメッセージが到着するとすぐに、このIAsyncResultのIsCompletedプロパティがtrueに設定され、EndGetMessageメソッドが呼び出されます。 EndGetMessageは、サブスクライバーに応答を送信します。
-web.configファイルでサービスを構成します
<system.serviceModel> <bindings> <webHttpBinding> <binding name="webBinding"> </binding> </webHttpBinding> </bindings> <services> <service name=" WCFPush.MessageService" behaviorConfiguration="Default"> <endpoint address="" contract="WCFPush.IMessageService" behaviorConfiguration="web" bindingConfiguration="webBinding" binding="webHttpBinding"> </endpoint> </service> </services> <behaviors> <endpointBehaviors> <behavior name="web"> <webHttp /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="Default"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel>
サービスの準備ができました。
明らかに、サービスからの応答を待つ時間が満了すると、新しいメッセージの要求を再送信する必要があります。
おわりに
したがって、REST要求に基づいたリアルタイムメッセージング用のプッシュサービスを実装することができます。 このようなサービスは、通常のブラウザーからなど、RESTful要求をサポートする任意のクライアントから使用できます。