Delphi DataSnap 流程分析(一)

这篇具有很好参考价值的文章主要介绍了Delphi DataSnap 流程分析(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

DataSnap 有三种方式:

1、DataSnap REST Application: Create a DataSnap Server with support for REST Communication and with pages that invoke server methods using Java Script and JSON.

2、DataSnap Server: The DataSnap Server Wizard provides an easy way to implenent a server application using DataSnap technology.

3、DataSnap Webbroker Application: The DataSnap WebBroker Application Wizard provides an easy way to implenent a server application using both The WebBroker and DataSnap technology.

1方式是最新的也是主要的运用方式。只支持HTTP。有WebModule,有TDSHTTPWebDispatcher。

2方式传统的运用方式,支持TCP和HTTP。没有WebModule,TDSHTTPService代替了TDSHTTPWebDispatcher。

3方式和1方式类似,比较"原始",也比较灵活。只支持HTTP。

早期的DataSnap只有方式2和3,方式2只支持TCP传输的。方式3的运用要自己处理许多细节,所以方式3用的不多。

现在的方式2添加了HTTP支持,但是其实现方式不是直接通过WebModule来实现,而是转了个弯,通过桥接Indy的Http来实现。

因为方式2没有了TWebModule,所以和其它两种方式的区别比较大。

我们先来简要分析下方式2的流程,然后主要分析方式1的流程,方式3的流程类似方式1,就不做分析了。

DataSnap Server 流程:

向导生成时,选择支持TCP和HTTP服务。服务器是自动启动的,ServerContainerUnit1.ServerContainer1.DSServer1.AutoStart := True;

只要运行服务端程序,就可以开始提供服务。如果要手动启动,则设置:

ServerContainerUnit1.ServerContainer1.DSServer1.AutoStart := False;

启动:

ServerContainerUnit1.ServerContainer1.DSServer1.Start;

停止:

ServerContainerUnit1.ServerContainer1.DSServer1.Stop;

TCP通信流程不管,看看HTTP通信流程。

向导生成的ServerContainer单元,包含了TDSServer(服务控制组件),TDSServerClass(用于导出方法到客户端),TDSTCPServerTransport(用于TCP通信),TDSHTTPService(HTTP服务),以及其它的辅助组件,用的是TDataMudule:

Delphi DataSnap 流程分析(一)

当用TDataMudule时,如果要提供HTTP服务,肯定要提供一个WebDisptcher。

(见:Delphi Web Server 流程分析_看那山瞧那水的博客-CSDN博客) 

TDSTCPServerTransport和TDSHTTPService都有一个Server属性,指向TDSServer。

当DSServer1.Start时,是如何启动HTTP服务的?


procedure TDSServer.Start;
begin
  inherited;
  // Add a DBX "driver" for the server component
  TDBXDriverRegistry.RegisterDriverClass(Name, TDSServerDriver);

end;
这里没有什么,只是添加了DBX驱动,DATASNAP的TCP通信是通过DBX框架实现的。

类继承关系:

TDSServer->TDSCustomServer->TComponent:

procedure TDSCustomServer.Start;
begin
  if not FStarted then
    try
      StartTransports;
      FServerMethodProvider := TDSServerMethodProvider.Create;
      FServerMethodProvider.Server := self;
      FServerMethodProvider.Open;
      FStarted := True;
    finally
      if not FStarted then
      begin
        StopTransports;
        if FServerMethodProvider <> nil then
        begin
          FServerMethodProvider.Close;
          FreeAndNil(FServerMethodProvider);
        end;
      end;
    end;
end;

这里只看到关于TCP的组件,HTTP在哪里呢?

没找到,先看看TDSHTTPService的继承关系:

TDSHTTPService->TCustomDSHTTPServerTransport->TCustomDSRESTServerTransport->TDSServerTransport->TDSServerComponent->TComponent

好像和HTTP都没什么关系,和TDSServerTransport有关系,TCP的有个组件TDSTCPServerTransport,也看看它的继承关系

TDSTCPServerTransport->TDSServerTransport,看到了,TDSHTTPService和TDSTCPServerTransport都是TDSServerTransport的子类。

前面看到了,TDSServer.Start,要启动了TDSTCPServerTransport:

StartTransports:


procedure TDSCustomServer.StartTransports;
var
  Transport: TDSServerTransport;
  ServerComponent: TObject;
  Index: Integer;
begin
  for Index := 0 to FComponentList.Count - 1 do
  begin
    ServerComponent := FComponentList[Index];
    if ServerComponent is TDSServerTransport then
    begin
      Transport := TDSServerTransport(ServerComponent);
      Transport.DbxContext := FDbxContext;
      Transport.Start;
    end;
  end;
end;

调用了TDSServerTransport.Start:

但是TDSServerTransport本身没有这个方法,其父类的Start:


procedure TDSServerComponent.Start;
begin
//
end;
哎,是空的,看来是子类实现。代码里的Transport是TDSServerTransport,是TDSTCPServerTransport的父类,这个方法肯定是在TDSTCPServerTransport:


procedure TDSTCPServerTransport.Start;
var
  Scheduler: IIPSchedulerOfThreadPool;
  LSocketHandle: IIPSocketHandle;
begin
  inherited;
  FTcpServer := CreateTcpServer;
  FTcpServer.OnConnect := DoOnConnect;
  FTcpServer.OnDisconnect := DoOnDisconnect;
  FTcpServer.OnExecute := DoOnExecute;
  FTcpServer.UseNagle := false;
  FTcpServer.Bindings.Add.Port := FPort; //default IPv4
  if GStackPeers(IPImplementationID).SupportsIPv6 then
  begin
    LSocketHandle := FTcpServer.Bindings.Add;
    LSocketHandle.Port := FPort; //default IPv4
    LSocketHandle.IPVersion := TIPVersionPeer.IP_IPv6
  end;
  Scheduler := PeerFactory.CreatePeer(IPImplementationID, IIPSchedulerOfThreadPool, FTCPServer.GetObject as TComponent) as IIPSchedulerOfThreadPool;
  Scheduler.MaxThreads := MaxThreads;
  Scheduler.PoolSize := PoolSize;
  FTCPServer.Scheduler := Scheduler;
  FTcpServer.Active := True;
end;

这里还是没有涉及到HTTP,回头看看TDSHTTPService这边,

TDSHTTPService->TCustomDSHTTPServerTransport->TCustomDSRESTServerTransport->TDSServerTransport 这中间某个肯定实现了和HTTP的挂钩。

TDSHTTPService.Start:


procedure TDSHTTPService.Start;
begin
  inherited;
  RequiresServer;
  if Assigned(FHttpServer) then
  begin
    if FCertFiles <> nil then
      FCertFiles.SetServerProperties(FHttpServer);
    TDSHTTPServerIndy(FHttpServer).Active := True;
  end;
end;

就是这个

RequiresServer()方法在父类TCustomDSRESTServerTransport,CreateRESTServer()在TCustomDSHTTPServerTransport,CreateHttpServer()在TDSHTTPService:


procedure TCustomDSRESTServerTransport.RequiresServer;
begin
  if FRestServer = nil then
  begin
    FRESTServer := CreateRESTServer;
    InitializeRESTServer;
  end;
end;

function TCustomDSHTTPServerTransport.CreateRESTServer: TDSRESTServer;
begin
  FHttpServer := CreateHttpServer;
  Result := FHttpServer;
end;


function TDSHTTPService.CreateHttpServer: TDSHTTPServer;
var
  LHTTPServer: TDSHTTPServerIndy;
begin
  if Assigned(FCertFiles) then
    LHTTPServer := TDSHTTPSServerIndy.Create(Self.Server, IPImplementationID)
  else
    LHTTPServer := TDSHTTPServerIndy.Create(Self.Server, IPImplementationID);
  Result := LHTTPServer;
  LHTTPServer.HTTPOtherContext := HTTPOtherContext;
end;

CreateHttpServer()方法里出现了TDSHTTPServerIndy,看看它是什么,前面的Start()里有这一行代码:

TDSHTTPServerIndy(FHttpServer).Active := True;

TDSHTTPServerIndy = class(TDSHTTPServer), 是TDSHTTPServer的子类,启动代码:


procedure TDSHTTPServerIndy.SetActive(const Value: Boolean);
begin
  if Value and (FServer = nil) then
  begin
    FServer := PeerFactory.CreatePeer(FIPImplementationID, IIPHTTPServer, nil) as IIPHTTPServer;
    InitializeServer;
  end;
  if FServer <> nil then
    FServer.Active := Value;
end;

有个名称叫 PeerIP(本意是对等IP),INDY里一些组件采用多端口技术时,有2组参数:

 IP 、Port:代表本地IP地址和端口;

PeerIP、PeerPort:代表远端IP地址和端口;

服务端可以向PeerIP和PeerPort回应数据,这里是HTTP服务端。

(PeerIP的技术原理还没搞明白)

支持IIPHTTPServer接口的实现在IPPeerServer.pas(路径:D:\Program Files (x86)\Embarcadero\Studio\22.0\source\indy\implementation\IPPeerServer.pas)

部分代码:

  TIdHTTPServerIP = class(TIdHTTPServer)
  private
    FSetDestroyedProc: procedure of object;
  public
    destructor Destroy; override;
  end;

  TIdHTTPServerPeer = class(TIdClassIP, IIPHTTPServer, IIPObject)
  private
    FHTTPServer: TIdHTTPServerIP;
    FContexts: TDictionary<TIdContext, IIPContext>;

.....................................

FHTTPServer => FHTTPServer =>TIdHTTPServer

本质上也是一个HTTPSERVER,只是通过PeerIP技术来实现了。


procedure TDSHTTPServerIndy.InitializeServer;
begin
  if FServer <> nil then
  begin
    FServer.UseNagle := False;
    FServer.KeepAlive := True;
    FServer.ServerSoftware := FServerSoftware;
    FServer.DefaultPort := FDefaultPort;

    FServer.OnCommandGet := Self.DoIndyCommand;
    FServer.OnCommandOther := Self.DoIndyCommand;
  end;
end;

                                   
procedure TDSHTTPServerIndy.DoIndyCommand(AContext: IIPContext; ARequestInfo: IIPHTTPRequestInfo;
                                AResponseInfo: IIPHTTPResponseInfo);
var
  LContext: TDSHTTPContextIndy;
begin
  LContext := TDSHTTPContextIndy.Create(AContext, ARequestInfo, AResponseInfo);
  try
    DoCommand(LContext, LContext.FRequest, LContext.FResponse);
  finally
    LContext.Free;
  end;
end;

DoCommand()代码:


procedure TDSRESTServer.DoCommand(AContext: TDSHTTPContext; ARequestInfo: TDSHTTPRequest;
                                  AResponseInfo: TDSHTTPResponse);
var
  Request: string;
  NextRequest: string;
  NextContext: string;
  RestCtxt: string;
  StartDispatch: Boolean;
begin

  // HTTPDispatch object if necessary
  StartDispatch := not TDSHTTPApplication.Instance.Dispatching;
  if StartDispatch then
    TDSHTTPApplication.Instance.StartDispatch(AContext, ARequestInfo, AResponseInfo);
  try
{$IFNDEF POSIX}
  if CoInitFlags = -1 then
    CoInitializeEx(nil, COINIT_MULTITHREADED)
  else
    CoInitializeEx(nil, CoInitFlags);
{$ENDIF}
  try
    // check for context, if not found send the appropriate error message
    Request := ARequestInfo.URI;
    if Consume(FDSContext, Request, NextRequest) then
    begin
      Request := NextRequest;
      if Consume(FRESTContext, Request, NextRequest) then
      begin
        // datasnap/rest
        DoDSRESTCommand(ARequestInfo, AResponseInfo, NextRequest);
      end
      else if ConsumeOtherContext(Request, NextContext, NextRequest) then
      begin
        DoDSOtherCommand(AContext, ARequestInfo, AResponseInfo, NextContext, NextRequest, FDSServerName <> EmptyStr);
      end
      else
      begin
        RestCtxt := Trim(FRESTContext);
        if RestCtxt = EmptyStr then
          RestCtxt := SProtocolRestEmpty;

        AResponseInfo.ResponseNo := 501; {rest or other service not found in URI}
        AResponseInfo.ContentText := Format(SProtocolNotSupported, [Request, RestCtxt]);
        AResponseInfo.CloseConnection := true;
      end;
    end
    else
    begin
      // This may dispatch .js files for example
      DoCommandOtherContext(AContext, ARequestInfo, AResponseInfo, Request);
    end;
    if Assigned(Self.FTrace ) then
    begin
      FTrace(Self, AContext, ARequestInfo, AResponseInfo);
    end;
  finally
                                                     
    ClearInvocationMetadata();
{$IFNDEF POSIX}
    CoUnInitialize;
{$ENDIF}
  end;
  finally
    if StartDispatch then
      TDSHTTPApplication.Instance.EndDispatch;
  end;
end;

开始引入了Dispatch,到这里基本就明白了,后面的处理方式和一般的HTTP类似,只是简化了(DataSnap专用)。

可以看出,和一般的使用WebModule也就是WebReq方式还是有大的区别的。文章来源地址https://www.toymoban.com/news/detail-423669.html

到了这里,关于Delphi DataSnap 流程分析(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Verilog的三种描述方式(结构化描述、数据流描述、行为级描述对电路功能的描述有三种方式:结构化描述、数据流描述、行为级描述

    Verilog的三种描述方式(结构化描述、数据流描述、行为级描述对电路功能的描述有三种方式:结构化描述、数据流描述、行为级描述。三种描述方式抽象级别不同,各有优缺点,相辅相成,需要配合使用。 目录 一、结构化描述 1、概念 2、特点 3、示例 真值表: 电路抽象:

    2024年02月04日
    浏览(55)
  • 固态硬盘、机械硬盘、手机的“内存”有三种

    就是处理器处理文件的地方,相当于的办公桌桌面。可以把所有文件摊开,便于快速处理(速度快,面积有限)。闪存,是真正用来存储文件的地方,相当于办公桌的抽屉,文件可以堆叠(存储量大),装订保存。但每次要查看时,需要从抽屉取出,拿到桌面摊开(速度比内

    2024年02月11日
    浏览(34)
  • Solidity中函数有三种装饰器

    Solidity中函数有三种装饰器,分别是pure、view和payable。使用装饰器可以轻松改变函数的行为。 pure 装饰器 表示在函数中没有修改任何函数以外的变量,包括状态变量,只是单纯地进行了一个数值计算。函数的执行并不会消耗任何Gas,因为函数执行使用的是本地节点的CPU,所以

    2024年02月13日
    浏览(30)
  • 记录--啊?Vue是有三种路由模式的?

    众所周知,vue路由模式常见的有 history 和 hash 模式,但其实还有一种方式- abstract 模式(了解一哈~) 别急,本文我们将重点逐步了解: 路由 + 几种路由模式 + 使用场景 + 思考 + freestyle 路由的本质就是一种对应关系 ,根据不同的URL请求,返回对应不同的资源。那么url地址和真

    2024年02月05日
    浏览(27)
  • 云计算,主要有三种服务模式:IaaS、PaaS、SaaS的区别

    云计算,主要有三种服务模式,它们分别是IaaS、PaaS、SaaS,那么他们之间到底有什么区别和联系呢?今天数合宙就带大家一探究竟。 01 云计算服务类型介绍 云计算服务:指可以拿来作为服务、提供使⽤的云计算产品。包括云主机,云空间,云开发,云测试和综合类产品等。

    2024年02月01日
    浏览(42)
  • 标记垃圾,有三种色彩:四千长文带你深入了解三色标记算法

    🔭 嗨,您好 👋 我是 vnjohn,在互联网企业担任 Java 开发,CSDN 优质创作者 📖 推荐专栏:Spring、MySQL、Nacos、Java,后续其他专栏会持续优化更新迭代 🌲文章所在专栏:JVM 🤔 我当前正在学习微服务领域、云原生领域、消息中间件等架构、原理知识 💬 向我询问任何您想要的

    2024年02月13日
    浏览(30)
  • 荣耀magicbook重装系统后,指纹不能录入的情况,有三种解决办法,完美解决问题。

    有些小伙伴用了很多年的荣耀的笔记本还没坏,但是用着很卡,就想着重装windows系统,系统是重装完了,但是发现指纹录入不上了,就像下面这样,手指放在感应器上也不管用。 本人电脑是2018年买的,已经是过了保修期了,咨询了客服,检测是不需要自己掏钱的,但是如果

    2024年02月02日
    浏览(28)
  • 量化分析革新金融服务软件的三种方式

    金融服务软件行业爱死量化分析了。 为什么呢?因为在这个本质上不可预测的行业中,量化分析提供了一种确定性,或者至少是类似于确定性的东西。 市场总是在变动,利润也起伏不定。交易达成了,然后落空,又再次达成,从交易大厅到董事会,纳秒级的差异可能成就巨

    2024年02月08日
    浏览(41)
  • Delphi控件安装之BAT(批处理)方式-FastReport

    更多关于Delphi控件安装说明,请看这里。 针对FastReport安装的特别说明 其关联了TeeChart组件,所以须先安装TeeChart,并在FastReprt正确设置(tee.inc) 汉化乱码问题,是官方Resfrcc.exe文件导致(在转换Res目录下xml语言包文件为Delphi的pas文件,换行时把一个汉字从中间分开了)。分

    2024年01月20日
    浏览(33)
  • Delphi 开发手持机(android)打印机通用开发流程(举一反三)

       目录 一、场景说明 二、厂家应提供的SDK文件 三、操作步骤: 1. 导出Delphi需要且能使用的接口文件: 2. 创建FMX Delphi项目,将上一步生成的接口文件(V510.Interfaces.pas)引入: 3. 将jarsdk.jar 包加入到 libs中:  4. Delphi中调用: 四、完整源代码下载 五、总结: 一、场景说明

    2024年02月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包