移动端的消息推送大家都体验过,智能手机上一大堆广告等各种消息会不时从消息栏中弹出来骚扰你。
PC程序中我们有时也会用到消息推送,比如通知之类。通常我们使用的方法可能更多地使用Socket之类来处理,有时效率更低的方法是做数据库的轮询。如果终端多、消息多,数据库的轮询方式是肯定不能被接受的。
现在比较流行的消息服务器有很多,像Apache的ActiveMQ,升级产品是Apollo,RabbitMQ,还有号称速度最快的ZeroMQ,等等很多。这些消息服务器大多支持各种平台,如Java,PHP,Python等,也大多对Delphi支持。
Delphi下比较著名的客户端是Habari的客户端,官方主页:
https://www.habarisoft.com/
上面有针对ActiveMQ等的专用客户端。
还有这个开源作品:
http://www.danieleteti.it/stomp-client/
代码托管在:
http://code.google.com/p/delphistompclient/
ActiveMQ等对消息的持久化支持比较简单,配置一下就可以,这个百度一下就一大把,关键是通过连接消息服务器,可以使客户端简单地处理消息,做到高效、实时。
下面摘录一点简单的Delphi使用stomp协议客户端的应用吧,就是上面提到的那个开源作品:
Stomp Client
Stomp protocol provides an interoperable wire format so that any of the available Stomp Clients can communicate with any Stomp Message Broker to provide easy and widespread messaging interop among languages, platforms and brokers.
The Delphi Stomp Client is an open source implementation of the STOMP protocol for Delphi 2010 (should work with Delphi 2007 and 2009 too) and FreePascal 2.4.x.
This Delphi Stomp Client isn’t an attempt to copy JMS client architecture to Delphi. So aren’t included some JMS specific features like message transformations.
This stomp client is actually tested on ActiveMQ 5.2 and ActiveMQ 5.3, but should work with every STOMP compliant server.
In Delphi you can use the built-in INDY component suite or the OpenSource Synapse acording with a compiler directive. In StompClient.pas there is following definitions:
unit StompClient; // For FreePascal users: // Automatically selected synapse tcp library {$IFDEF FPC} {$MODE DELPHI} {$DEFINE USESYNAPSE} //FREEPASCAL ALWAYS USE SYNAPSE {$ENDIF} // For Delphi users: // Decomment following line to use synapse also in Delphi { .$DEFINE USESYNAPSE } //DELPHI USERS CAN USE INDY OR SYNAPSE
Some examples of basic functionalities (not real world example, use included examples instead):
program SimpleMessaging; {$APPTYPE CONSOLE} uses SysUtils, StompClient, StompTypes; procedure Example_Pub_Subscriber; var StompPub, StompSubscriber: IStompClient; StompFrame: IStompFrame; begin WriteLn('==> Example_Pub_Subscriber'); StompSubscriber := StompUtils.NewStomp('127.0.0.1'); // default port StompSubscriber.Subscribe('/topic/dummy'); StompPub := StompUtils.NewStomp('127.0.0.1'); // default port StompPub.Send('/topic/dummy', 'Some test message'); repeat StompFrame := StompSubscriber.Receive; until Assigned(StompFrame); WriteLn(StompFrame.GetBody); // Print "Some test message" WriteLn; end; procedure Example_OnePub_TwoSubscriber; var StompPub, StompSub1, StompSub2: IStompClient; StompFrame: IStompFrame; begin WriteLn('==> Example_OnePub_TwoSubscriber'); StompSub1 := StompUtils.NewStomp('127.0.0.1'); // default port StompSub2 := StompUtils.NewStomp('127.0.0.1'); // default port StompSub1.Subscribe('/topic/dummy'); StompSub2.Subscribe('/topic/dummy'); StompPub := StompUtils.NewStomp('127.0.0.1'); // default port StompPub.Send('/topic/dummy', 'First test message on a topic'); StompPub.Send('/topic/dummy', 'Second test message on a topic'); StompFrame := StompSub1.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.GetBody); StompFrame := StompSub1.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.GetBody); StompFrame := StompSub2.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.GetBody); StompFrame := StompSub2.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.GetBody); WriteLn; end; procedure Example_PointToPoint; var StompPub, StompSub1, StompSub2: IStompClient; StompFrame: IStompFrame; begin WriteLn('==> Example_PointToPoint'); StompSub1 := StompUtils.NewStomp('127.0.0.1'); // default port StompSub2 := StompUtils.NewStomp('127.0.0.1'); // default port StompSub1.Subscribe('/queue/dummy'); StompSub2.Subscribe('/queue/dummy'); StompPub := StompUtils.NewStomp('127.0.0.1'); // default port StompPub.Send('/queue/dummy', 'First test message on a queue'); StompPub.Send('/queue/dummy', 'Second test message on a queue'); StompFrame := StompSub1.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.Output); StompFrame := StompSub1.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.Output); StompFrame := StompSub2.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.Output); StompFrame := StompSub2.Receive(2000); if Assigned(StompFrame) then WriteLn(StompFrame.Output); WriteLn; end; begin try Example_Pub_Subscriber; Example_OnePub_TwoSubscriber; Example_PointToPoint; except on E: Exception do WriteLn(E.ClassName, ': ', E.message); end; end.
DelphiStompClient have also a simple listener for asynchronous use. To use the listener you should implement follwing interface:
IStompClientListener = interface ['{C4C0D932-8994-43FB-9D32-A03FE86AEFE4}'] procedure OnMessage(StompFrame: IStompFrame); end;
Also a normal form can be used:
TForm1 = class(TForm, IStompClientListener) … public procedure OnMessage(StompFrame: IStompFrame); //Called by the stomp receiver end;
And then you can manager your message in a simple form’s method.
There are many features in this STOMP client. I’ve tried to respect all the STOMP specs also in the transactions side (http://stomp.codehaus.org/Protocol).
You can find all the source code and the examples at following Google Code Project:
http://code.google.com/p/delphistompclient/
The FreePascal version is actually mantained by Daniel Gaspary, thank Daniel.