> 文章列表 > Delphi DataSnap 流程分析(一)

Delphi DataSnap 流程分析(一)

大家好啊,今天咱们来聊点儿关于DataSnap的那点儿事儿。DataSnap这个老伙计,在Delphi的世界里真的是个硬核角色。它的三种形态,DataSnap REST Application、DataSnap Server、还有DataSnap Webbroker Application,就像三个不同的武林高手,各有所长。

先说说DataSnap REST Application,这是最年轻的小鲜肉,专攻HTTP,支持WebModule和TDSHTTPWebDispatcher。它就像一位优雅的剑客,走哪儿都顺手,特别适合现在流行的RESTful API开发。有了它,前端的JavaScript小伙伴儿们可以轻松调用服务器方法,数据传输也贼快,用起来那是相当爽。

然后是DataSnap Server,这位可是老派的代表,支持TCP和HTTP,但没WebModule,用的是TDSHTTPService。就像一位用双刀的老将,虽然灵活,但套路有点绕,得通过Indy的HTTP来实现。说实话,这位老伙计用起来没WebModule方便,但胜在支持TCP,老粉丝们还是挺喜欢的。

最后是DataSnap Webbroker Application,这货其实和REST Application有点像,但更原始,也更灵活,只支持HTTP。就像一位不拘一格的画家,虽然自由,但需要自己处理不少细节,所以用的人不多,但有它的独特之处。

那文章里重点分析了DataSnap Server的流程,特别是HTTP通信的实现。咱们看到,TDSServer.Start会启动传输,但HTTP服务的启动得靠TDSHTTPService自己处理,涉及到一些Indy的组件,比如TIdHTTPServer。这就像武侠小说里的内力,表面上看似平静,实则暗流涌动。

整体而言,DataSnap的三种方式各有千秋,选哪个主要看你的需求。REST Application适合现代应用,Server适合传统项目,Webbroker则是灵活性和细节处理的高手。理解了它们的流程和实现,你在开发中就能游刃有余,像一位真正的武林高手了!

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:

当用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;
beginif not FStarted thentryStartTransports;FServerMethodProvider := TDSServerMethodProvider.Create;FServerMethodProvider.Server := self;FServerMethodProvider.Open;FStarted := True;finallyif not FStarted thenbeginStopTransports;if FServerMethodProvider <> nil thenbeginFServerMethodProvider.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;
varTransport: TDSServerTransport;ServerComponent: TObject;Index: Integer;
beginfor Index := 0 to FComponentList.Count - 1 dobeginServerComponent := FComponentList[Index];if ServerComponent is TDSServerTransport thenbeginTransport := 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;
varScheduler: IIPSchedulerOfThreadPool;LSocketHandle: IIPSocketHandle;
begininherited;FTcpServer := CreateTcpServer;FTcpServer.OnConnect := DoOnConnect;FTcpServer.OnDisconnect := DoOnDisconnect;FTcpServer.OnExecute := DoOnExecute;FTcpServer.UseNagle := false;FTcpServer.Bindings.Add.Port := FPort; //default IPv4if GStackPeers(IPImplementationID).SupportsIPv6 thenbeginLSocketHandle := FTcpServer.Bindings.Add;LSocketHandle.Port := FPort; //default IPv4LSocketHandle.IPVersion := TIPVersionPeer.IP_IPv6end;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;
begininherited;RequiresServer;if Assigned(FHttpServer) thenbeginif FCertFiles <> nil thenFCertFiles.SetServerProperties(FHttpServer);TDSHTTPServerIndy(FHttpServer).Active := True;end;
end;

就是这个

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


procedure TCustomDSRESTServerTransport.RequiresServer;
beginif FRestServer = nil thenbeginFRESTServer := CreateRESTServer;InitializeRESTServer;end;
end;function TCustomDSHTTPServerTransport.CreateRESTServer: TDSRESTServer;
beginFHttpServer := CreateHttpServer;Result := FHttpServer;
end;function TDSHTTPService.CreateHttpServer: TDSHTTPServer;
varLHTTPServer: TDSHTTPServerIndy;
beginif Assigned(FCertFiles) thenLHTTPServer := TDSHTTPSServerIndy.Create(Self.Server, IPImplementationID)elseLHTTPServer := 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);
beginif Value and (FServer = nil) thenbeginFServer := PeerFactory.CreatePeer(FIPImplementationID, IIPHTTPServer, nil) as IIPHTTPServer;InitializeServer;end;if FServer <> nil thenFServer.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;
beginif FServer <> nil thenbeginFServer.UseNagle := False;FServer.KeepAlive := True;FServer.ServerSoftware := FServerSoftware;FServer.DefaultPort :&