当前位置:首页 > datasnap的初步
datasnap的初步-回调函数
服务器端 TServerMethods1 =class(TComponent) private { Private declarations } public { Public declarations } functionTest(funcCallBack: TDBXCallback):boolean; end; functionTServerMethods1.Test(funcCallBack: TDBXCallback):boolean; begin funcCallBack.Execute(TJSONNumber.Create(20)).Free; sleep(1000); Result :=True; end; 客户端,这个必须继承自TDBXCallback TFuncCallback =class(TDBXCallback) functionExecute(constArg: TJSONValue): TJSONValue;override; end; functionTFuncCallback .Execute(constArg: TJSONValue): TJSONValue; var i:Integer; begin i := TJSONNumber(Arg).AsInt;//可以的到服务器回调来的参数 Result := TJSONNull.Create; end; procedureTForm2.Button1Click(Sender: TObject); begin ClientModule1.ServerMethods1Client.Test(funcCallBack); end; initialization funcCallBack:= TFuncCallBack.Create; finalization //FreeAndNil(funcCallBack);
到此,实现了最基本的回叫。
D2010起提供了DSClientCallbackChannelManager这个控件,这是为了实现一次注册,多次回叫,使用方法很简单
1。客户端使用 DSClientCallbackChannelManager注册回叫函数 function RegisterCallback(const CallbackId: String; const Callback: TDBXCallback): boolean; overload; DSClientCallbackChannelManager控件带有一个ChannelName的属性,用于CallbackId分组用。ManagerId属性,可理解为ClientId,ClientId必须是唯一的,相同的ClientId,会被认为是相同地点来的连接。
不明白为啥 DSClientCallbackChannelManager要自己设置连接属性,而不是走TSQLConnection。
2。服务器端是TDSServer来做事,它有 两个函数 function BroadcastMessage(const ChannelName: String; const Msg: TJSONValue; const ArgType: Integer = TDBXCallback.ArgJson): boolean; overload; function BroadcastMessage(const ChannelName: String; const CallbackId: String; const Msg: TJSONValue; const ArgType: Integer = TDBXCallback.ArgJson): boolean; overload; 第一个函数,回调在ChannelName里面所有的callbackid, 第二个是回调ChannelName里面指定的CallBackId
服务器上用GetAllChannelCallbackId能返回在某个ChannelName里面所有的CallbackId。
到此,我们就能使用DSClientCallbackChannelManager来制作一个简单的聊天软件,并能实现私聊的功能。但是如何处理,聊天用户掉线的问题,就比较麻烦了。
和RO比较,这设计有些像RO里的TROEventReceiver,但远没RO灵活, TROEventReceiver直接就能订阅(RegisterEventHandlers)上一堆服务器的事件,DataSnap却要定义一堆的TDBXCallback。
datasnap的初步 TDSClientCallbackChannelManager的工作方式
理解一下TDSClientCallbackChannelManager的工作方式吧
客户端调用RegisterCallback,其实就是开始了一个线程TDSChannelThread,该线程起一个dbxconnection,连接到服务器上,执行DSAdmin.ConnectClientChannel,服务器上的这个DSAdmin.ConnectClientChannel很神奇,所有的数据传输都在这里了,这个连接不会关闭,以做到服务器往客户端push数据。TDSClientCallbackChannelManager只能做到服务器向客户端推送数据,单向的。客户端要向服务器送数据,走TDSAdminClient的NotifyCallback方法,也就是只有经过SQLConnection。
DSAdmin(在Datasnap.DSCommonServer单元)是TDBXServerComponent(在Datasnap.DSPlatform单元)的子类,ConnectClientChannel函数直接call ConsumeAllClientChannel。 代码摘抄 functionTDBXServerComponent.ConsumeAllClientChannel(constChannelName, ChannelId, CallbackId, SecurityToken:String; ChannelNames: TStringList; ChannelCallback: TDBXCallback; Timeout:Cardinal):Boolean; ... begin // wait for exit message repeat //这里开始 Data :=nil; IsBroadcast :=false; ArgType := TDBXCallback.ArgJson; QueueMessage :=nil; TMonitor.Enter(CallbackTunnel.Queue); try {Wait for a queue item to be added if the queue is empty, otherwise don't wait and just pop the next queue item} ifCallbackTunnel.Queue.QueueSize =0then IsAquired := TMonitor.Wait(CallbackTunnel.Queue, Timeout) else IsAquired :=true; ifIsAquiredand(CallbackTunnel.Queue.QueueSize >0)then begin {Get the next queued item from the tunnel} QueueMessage := CallbackTunnel.Queue.PopItem; Data := QueueMessage.Msg; IsBroadcast := QueueMessage.IsBroadcast; ArgType := QueueMessage.ArgType; end; finally TMonitor.Exit(CallbackTunnel.Queue); end; ifIsAquiredand(Data <>nil)then ifIsBroadcastthen begin try Msg := TJSONObject.Create(TJSONPair.Create('broadcast', TJSONArray.Create(Data).Add(ArgType))); if(QueueMessage.ChannelName <> EmptyStr)and (QueueMessage.ChannelName <> CallbackTunnel.ServerChannelName)then Msg.AddPair(TJSONPair.Create('channel', QueueMessage.ChannelName)); try ChannelCallback.Execute(Msg).Free; except try // Remove the callback tunnel from the list, it will be freed at the end of this method InternalRemoveCallbackTunnel(DSServer, CallbackTunnel); except end; raise; end; finally QueueMessage.InstanceOwner :=false; FreeAndNil(QueueMessage); end; end elseifAssigned(QueueMessage)then begin TMonitor.Enter(QueueMessage); try Msg := TJSONObject.Create( TJSONPair.Create('invoke', TJSONArray.Create( TJSONString.Create(QueueMessage.CallbackId), Data).Add(ArgType))); try QueueMessage.Response := ChannelCallback.Execute(Msg); except onE : Exceptiondo begin QueueMessage.IsError :=True; QueueMessage.Response := TJSONObject.Create(TJSONPair.Create('error', E.Message)); ifChannelCallback.ConnectionLostthen begin WasConnectionLost :=True; TMonitor.Pulse(QueueMessage); try // Remove the callback tunnel from the list, it will be freed at the end of this method InternalRemoveCallbackTunnel(DSServer, CallbackTunnel); except end;
共分享92篇相关文档