Multicast Events - RemObjects Wiki

来源:百度文库 编辑:神马文学网 时间:2024/04/29 02:43:43
Multicast Events
From RemObjects Wiki
Jump to:navigation,search
This is a RemObjects SDKArticles topic
Feel free to add your notes to this topic below.
Contents
[hide]
1 The problem
2 The solution
3 How to use RO Multicast Events3.1 Event definition - server side
3.2 Code changes at server side
3.3 Managing subscriptions
4 RO.SDK related changes needed to send server events4.1 Using .NET
4.2 Using Delphi
4.3 Raising server events
5 Defining MulticastEvents on Client side5.1 RO.SDK related changes needed to receive server events
6 Subscribing to server events6.1 Implementing server event handlers
6.2 Triggering server events
7 RO MulticastEvents in action
8 Summary
The problem
In some cases, when developing distributed applications, some way is needed to allow the server application to send messages to the client applications. For example, when developing a chat application you'll need some mechanism to allow the server to tell its clients that they should reconnect or than a new user has entered the chat. Usually this can be done by implementing some kind of message queue and client application that periodically calls the server to check if new messages are available. But proper implementation of this can be quite a pain, because you'll need to develop not only a reliable message queue that has to remember the queue position for every client (because some clients might already receive some messages while others not), but some kind of periodical call mechanism for client application. This system would be a rather complex and hard to test properly.
Fortunately, RemObjects SDK not only perfectly solves the problem of distributed communication, even between Win32 and .NET applications (including ones running on Mono in Linux or MacOS), it also greatly decreases development time and adds flexibility in solving the problems described above.
The solution
To solve the problem of communication between the server and its clients we introduced a new feature called Multicast Server Events.
Using this feature, you can implement server callbacks to the client application with a minimum of effort, while both server and client can be implemented using Delphi or one of the .NET languages. Note that depending on the projected server workload and performance needed you can use two different approaches - message polling by client application or a server that actively calls its clients. In the first case you can manage how often clients poll the server (i.e. depending on the client's priority), while in the second case your clients will receive messages with minimum delay. So these two approaches allow you to select what is more important to you - to minimize server workload while serving multiple clients or to have a robust communication from server to clients. Also note, that active server events can be implemented only by using our newly introduced RO.SDK Super Channels (described in another article), while message polling could utilize any channel type provided by RO.SDK
Steps needed to implement both these approaches will be described below.
If you want to see a Multicast Server Event in action, you should take a look at these RemObject SDK samples :
In C#: Message polling approach -Chat sample
Active server events -SuperTcpChannel Chat sample
In Delphi (from 7 to 2009): Message polling approach -HTTP Chat sample
Active server events -Super TCP Channel Chat sample
How to use RO Multicast Events
In this topic we will describe steps needed to implement RO MulticastEvents. In cases where the two different approaches mentioned above need different implementation it will be mentioned.
Event definition - server side
Open RODL and add an Event Sink named ServerMessageEventSink using the context menu or toolbar. Add a new event named ServerMessageEvent to ServerMessageEventSink with one Widestring parameter named Message. Note that for obvious reasons events cannot return anything and its parameters can only be in type. Also, you need to add a method named Subscribeto the service declaration, with a Boolean parameter named aSubscribe. Later we will use this method to manage clients' subscriptions to server events.

RODL with server event definition opened in RemObjects Service Builder
Code changes at server side
After you close Service Builder, code files will be automatically generated.
In .NET, not only the usual _Intf, _Impl and _Invk files will be generated, but also an _Events file.
the _Events file contains a declaration of an ancestor of RemObjects.SDK.Server.EventSinkProxy, used as proxy to represent remote clients in the server application:
public class ServerMessageEventSink_EventSinkProxy : RemObjects.SDK.Server.EventSinkProxy, IServerMessageEventSink
The _Intf file contains not only the ordinary service interface, proxy and asynchronous proxy declaration, but also the declaration of an interface that should be implemented by events receivers and declaration of an invoker class for those events.
[RemObjects.SDK.EventSink(Name = "ServerMessageEventSink", InvokerClass = typeof(ServerMessageEventSink_EventSinkInvoker))]public interface IServerMessageEventSink : RemObjects.SDK.IROEventSink{void ServerMessageEvent(string Message);}public class ServerMessageEventSink_EventSinkInvoker : RemObjects.SDK.EventSinkInvoker{public static void Invoke_ServerMessageEvent(RemObjects.SDK.IROEventSinkHandlers @__Handlers, RemObjects.SDK.IMessage @__Message){string Message = @__Message.ReadWideString("Message");for (int @__i = 0; (@__i < @__Handlers.Count); @__i = (@__i + 1)){((IServerMessageEventSink)(@__Handlers[@__i])).ServerMessageEvent(Message);}}}
_Impl and _Invk files generated will remain unchanged.
In Delphi, only usual sets of files will be generated. Two additional interfaces will be added to the _Intf file:
{ IServerMessageEventSink }IServerMessageEventSink = interface['{41026DC0-4D65-4FCC-85D4-128962E1D8F6}']procedure ServerMessageEvent(const Message: Widestring);end;{ IServerMessageEventSink_Writer }IServerMessageEventSink_Writer = interface(IROEventWriter)['{41026DC0-4D65-4FCC-85D4-128962E1D8F6}']procedure ServerMessageEvent(const __Sender : TGUID; const Message: Widestring);end;
Managing subscriptions
We will implement a simple and obvious way to manage clients subscriptions in the Subscribe method:
C#
public void Subscribe(Boolean aSubscribe){if (aSubscribe)this.SubscribeClientEventSink(typeof(IServerMessageEventSink));elsethis.UnsubscribeClientEventSink(typeof(IServerMessageEventSink));}
Delphi
procedure TServerMessageService.Subscribe(const aSubscribe: Boolean);beginif aSubscribe thenself.EventRepository.AddSession(Session.SessionID)elseself.EventRepository.RemoveSession(Session.SessionID);end;
Now, the client can call Subscribe(true) to subsribe to server events and Subscribe(false) to unsubsribe.
RO.SDK related changes needed to send server events
Using .NET
You should add these components to your server's main form: MemoryMessageQueueManager and EventSinkManager. Also, you need to set the Message property of the EventSinkManager to an already existing message component.
The MemoryMessageQueueManager component will be used to handle message queuing. This implementation of Message Queue Manager stores the message queue in memory.
The EventSinkManager component will handle the process of sending events to the clients.
Using Delphi
You should add these components to your server's main form: ROInMemoryEventRepository and ROEventSessionManager. Also, you need to set the Message property of the ROInMemoryEventRepository to an already existing message component. PropertySessionManager should be set to the newly added ROEventSessionManager component.
You should also add a main form unit to the uses section of your server's _Impl file. After this you should set the service's properties EventRepository andSessionManager to corresponding main form components.
Raising server events
We will add raising of a server event to the implementation of the Sum method:
C#
public virtual int Sum(int A, int B){((IServerMessageEventSink)GetEventSink(typeof(IServerMessageEventSink))).ServerMessageEvent("Sum was called");return A+B;}
Delphi
function TServerMessageService.Sum(const A: Integer; const B: Integer): Integer;varev : IServerMessageEventSink_Writer;beginev := (self.EventRepository as IServerMessageEventSink_Writer);ev.ExcludeSender := False;ev.ServerMessageEvent(Session.SessionID, 'Sum was called');Result := A + B;end;
As you can see, this is pretty simple to do.
Defining MulticastEvents on Client side
RO.SDK related changes needed to receive server events
Using .NET
If you are planning to actively receive server events and are using one of the Super Channels, you should add a EventReceiver component to the client's main form and set itsChannel andMessage properties to already existing main form components.
If you are using one of the non-Super channels, you should add another channel component of the same type to the main form and point the EventReceiver'sChannel property to the newly added channel component. Also you can setup polling-related properties like MaximumMessagesPerPoll, MaximumPollInterval and MimimumPollInterval. This is the only difference between the implementation of active server events and the message pooling approach.
Using Delphi
The mechanism that is used for server events delivery is selected automatically based on the channel type.
You should add the ROEventReceiver component to the client main form and point its propertiesChannel andMessage to corresponding, already existing components. You also need to properly set the ServiceName property. It should be named after the service that will send service events.
Also, you can setup the message polling interval using ROEventReceiver's Interval property.
Note that only the asynchronous SuperChannel allows the server to actively communicate with the client, so active server events can only be implemented when SuperHttp or SuperTcp channels are used.
Using synchronous channels, you can only implement the message polling mechanism, because there is no way the server could send a message to the client without prior established connection from client to the server.
Subscribing to server events
To manage event subscriptions, you can use the already defined Subscribe method. In this sample we will subscribe to events in the OnLoad event handler and unsubscribe in the FormClosing event handler:
C#
private void MainForm_Load(object sender, EventArgs e){fService.Subscribe(true);this.eventReceiver1.RegisterEventHandler(this, typeof(IServerMessageEventSink));}private void MainForm_FormClosing(object sender, FormClosingEventArgs e){this.eventReceiver1.UnregisterEventHandler(this);fService.Subscribe(false);}
Delphi
procedure TClientForm.FormActivate(Sender: TObject);beginself.fService := self.RORemoteService as IServerMessageService;self.ROEventReceiver1.RegisterEventHandlers([EID_ServerMessageEventSink], [ self ]);self.ROEventReceiver1.Active := true;self.fService.Subscribe(true);end;procedure TClientForm.FormClose(Sender: TObject; var Action: TCloseAction);beginself.fService.Subscribe(false);self.ROEventReceiver1.Active := false;self.ROEventReceiver1.UnregisterEventHandlers([EID_ServerMessageEventSink]);end;
Note that fService is declared earlier as
C#
private IServerMessageService fService;
Delphi
var fService: IServerMessageService;
and initialised in form constructor using
C#
service = CoServerMessageService.Create(this.message, this.clientChannel);
Delphi
self.fService := self.RORemoteService as IServerMessageService;
eventReceiver1 is the name of the EventReceiver component added at step 2.2.1.
Implementing server event handlers
To receive server events we should implement IServerMessageEventSink declared in the service's _Intf file. We will use a very simple indication of the received service event:
C#
void IServerMessageEventSink.ServerMessageEvent(String Message){MessageBox.Show(Message);}
Delphi
procedure TClientForm.ServerMessageEvent(const Message: Widestring);beginMessageDlg(Message, mtError, [mbOK], 0);end;
Triggering server events
As you can remember, the server event we declared is fired when the service's Sum operation is called. So we will add a call of this operation in our client application. To do this, we will add a button to the client's main form and implement its OnClick event handler:
C#
private void button1_Click(object sender, EventArgs e){fService.Sum(0, 0);}
Delphi
procedure TClientForm.Button1Click(Sender: TObject);beginself.fService.Sum(0, 0);end;
RO MulticastEvents in action
Even this simple sample demonstrates MulticastEvents in action.
When you start the server and multiple clients, each of them will show a message box when you press the inlaying button. You can also see, that event subscriptions are lost when the server is restarted, while you still can call service functions. Depending on the approach used (message polling or active events), you will see a message box immediately after pressing the button calling the service's method or after some polling interval.
Note that much more sophisticated and advanced samples can be ouind in the RemObjects SDK Samples folder, as mentioned in part 1.2 of this article.
Summary
RO MulticastEvents described in this article allow you to implement server applications with minimum overhead that can actively send messages to its clients. Two different approaches described allow you to select between the message polling approach that allows you to minimize server workload, and the active server event approach that allows you to minimize event retrieval delay. Using the RemObjects SDK framework, you can easily implement this feature, even if your communications need to cross platform boundaries (from .NET to Win32 and from Windows to Linux, MacOS an even iPhone).