使用http.sys,让delphi 的多层服务飞起来
一直以来,delphi 的网络通讯层都是以indy 为主,虽然indy 的功能非常多,涉及到网络服务的
各个方面,但是对于大多数多层服务来说,就是需要一个快速、稳定、高效的传输层。Delphi 的 datasnap
主要通过三种实现数据通讯的,一种是大家恨得牙痒痒的indy,另外一种是通过iis 的isapi,最后一种是通过
apache 的动态模块(DSO) 来实现。
indy 的问题多多,大家基本上都是趋向使用后两种方式,后面两种方式的麻烦是必须安装IIS 或者是
Apache。用起来还要配置很多东西,也不是太方便。
还好,微软在Windows Vista (server 2008) 以后使用http.sys 作为web 服务的核心,IIS 也是通过这个核心
实现其web 服务的。使用http.sys 都有哪些优势呢?
1.不用做额外的编码,直接支持https(妈妈再也不用担心ios 10 要 https 了)
2.内核级的缓冲和内核级的请求队列(大大降低应用服务器自身的压力)
3.多个应用程序可以使用同一个端口(防火墙表示很欣慰)
4.内核级的SSL 支持
5.内核级的静态文件输出支持
6.理论上,这是windows 下最快的http 服务,没有之一。
这么多好处,那么我们是否可以在delphi 里面直接使用http.sys ,让delphi 的多层服务在windows 下飞起来?
答案是肯定的,delphi 完全可以非常顺利的使用http.sys 服务,不光是webbroke, datasanp, 包括我们常用的kbmmw.
目前delphi 的第三方控件里面支持http.sys 的主要有两个,一个是著名的控件商TMS, 其专门有一个控件叫TMS Sparkle,
主要就是封装http.sys 服务,这个公司的其他的一些多层控件都是架构在这个控件上的,唯一不好的是,它是商业软件,需要
付费购买。另外一个就是著名的开源框架mormot。此作者的功力已经是恐龙级,可以进delphi 界牛人前十名。他在mormot
里面也封装了 http.sys. 由于是开源的,所以是需要自己把对应封装的代码拿出来,实现与delphi 现有的多层应用适配。
下面以mormot 封装的 THttpApiServer 为例,说明一下在多层应用中如何使用适配使用http.sys.
我们首先解决webbroker 中如何使用THttpApiServer?
其实如果大家对webbroker 比较了解的话,就知道webbroker 的工作原理就是把客户端来的请求分发到webbroker 的处理过程,
然后再把返回结果响应给客户端。那么我们需要做一个winapiWebBrokerBridge,功能就是完成以上要求。
首先下载mormot 源码,添加相关目录。
然后加入我们的单元,需要使用的相关对象声明如下:
unit winapiWebBrokerBridge;{ by xalion 2016.12.25} interface uses Classes,
HTTPApp,
SysUtils,
system.NetEncoding,
SynCommons,
SynZip,
SynCrtSock , WebBroker, WebReq;type EWBBException = class(EWebBrokerException);
EWBBInvalidIdxGetDateVariable = class(EWBBException);
EWBBInvalidIdxSetDateVariable = class(EWBBException );
EWBBInvalidIdxGetIntVariable = class(EWBBException );
EWBBInvalidIdxSetIntVariable = class(EWBBException );
EWBBInvalidIdxGetStrVariable = class(EWBBException);
EWBBInvalidIdxSetStringVar = class(EWBBException);
EWBBInvalidStringVar = class(EWBBException); Twinapirequestinfo=class(Tobject)
protected FHttpServerRequest:THttpServerRequest;
Finrawheaders:Tstringlist;
FContentStream : TStream;
FFreeContentStream : Boolean;
Fhost:string;
Fport:string;
Fcontent:string;
FURL:string;
Fremoteip:string;
Fcontentlength:integer;
fInContentType:string; Fcommand:string;
public constructor Create(C: THttpServerRequest);
destructor Destroy; override;
end; Twinapiresponseinfo=class(Tobject)
protected FHttpServerRequest:THttpServerRequest;
Foutrawheaders:Tstringlist;
FContentStream : TStream;
FFreeContentStream : Boolean;
Fhost:string;
Fport:string;
Fcontent:string;
Fcontenttype:string;
Fcontentlength:integer;
Fstatuscode:integer;
FCookies: TCookieCollection;
public constructor Create(C: THttpServerRequest);
destructor Destroy; override;
procedure AddCookiestohead;
end; TwinapiAppRequest = class(TWebRequest)
protected FRequestInfo : TwinapiRequestInfo;
FResponseInfo : TwinapiResponseInfo;
FFreeContentStream : Boolean;
FStatusCode:integer;
// function GetDateVariable(Index: Integer): TDateTime; override;
function GetIntegerVariable(Index: Integer): Integer; override;
function GetStringVariable(Index: Integer): string; override;
function GetRemoteIP: string; override;
function GetRawPathInfo:string; override;
function GetRawContent: TBytes; override; public constructor Create(arequestinfo:Twinapirequestinfo; aresponseinfo:Twinapiresponseinfo);
destructor Destroy; override;
function GetFieldByName(const Name: string): string; override; function ReadClient(var Buffer; Count: Integer): Integer; override;
function ReadString(Count: Integer):string; override;
function TranslateURI(const URI: string): string; override; function WriteHeaders(StatusCode: Integer; const ReasonString, Headers: string): Boolean; override; end; TwinapiAppResponse = class(TWebResponse)
protected FRequestInfo : TwinapiRequestInfo;
FResponseInfo : TwinapiResponseInfo;
function GetContent: string; override;
function GetStatusCode: Integer; override;
procedure SetContent(const AValue: string); override;
procedure SetContentStream(AValue: TStream); override;
procedure SetStatusCode(AValue: Integer); override;
procedure SetStringVariable(Index: Integer; const Value:string); override;
procedure SetDateVariable(Index: Integer; const Value: TDateTime); override;
procedure SetIntegerVariable(Index: Integer; Value: Integer); override; public constructor Create(AHTTPRequest: TWebRequest;arequestinfo:Twinapirequestinfo; aresponseinfo:Twinapiresponseinfo);
destructor Destroy; override;
procedure SendRedirect(const URI: string); override;
procedure SendResponse; override;
procedure SendStream(AStream: TStream); override;
function Sent: Boolean; override;
end; TwinapiWebBrokerBridge = class(THttpApiServer)
private // procedure RunWebModuleClass(C : THttpServerRequest); protected FWebModuleClass: TComponentClass;
function Request(C : THttpServerRequest): cardinal;override; public procedure RegisterWebModuleClass(AClass: TComponentClass); end;
然后我们就可以使用这个,实现我们的webbroker 应用了。
我们使用delphi 自带的向导,开始建一个webserver.
点ok,继续
点完成。
生成对应的工程文件,然后我们替换主窗体的代码。
主程序对应的代码很简单。
unit mainp; interface uses
Winapi.Messages, System.SysUtils, System.Variants, SynCrtSock,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.AppEvnts, Vcl.StdCtrls, winapiWebBrokerBridge, Web.HTTPApp; type
TForm1 = class(TForm)
ButtonStart: TButton;
ButtonStop: TButton;
EditPort: TEdit;
Label1: TLabel;
ApplicationEvents1: TApplicationEvents;
ButtonOpenBrowser: TButton;
procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
procedure ButtonStartClick(Sender: TObject);
procedure ButtonStopClick(Sender: TObject);
procedure ButtonOpenBrowserClick(Sender: TObject);
private
FServer: TwinapiWebBrokerBridge;
procedure StartServer;
{ Private declarations }
public
{ Public declarations }
end; var
Form1: TForm1; implementation {$R *.dfm} uses
WinApi.Windows, Winapi.ShellApi;
procedure TForm1.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);begin if fserver=nil then begin ButtonStart.Enabled :=True;
ButtonStop.Enabled :=false;
EditPort.Enabled := True;
end else begin ButtonStart.Enabled := not FServer.Started;
ButtonStop.Enabled := FServer.Started ;
EditPort.Enabled := not FServer.Started;
end;end;procedure TForm1.ButtonOpenBrowserClick(Sender: TObject);var LURL: string;begin LURL := Format('http://localhost:%s', [EditPort.Text]);
ShellExecute(0,
nil,
PChar(LURL), nil, nil, SW_SHOWNOACTIVATE);end;procedure TForm1.ButtonStartClick(Sender: TObject);begin StartServer;end;procedure TForm1.ButtonStopClick(Sender: TObject);begin freeandnil( FServer);end;procedure TForm1.StartServer;begin FServer := TwinapiWebBrokerBridge.Create(True); Fserver.Clone(10);// 开始10个进程 Fserver.AddUrl('/','8080',false,'+',true);
fserver.Start;end;
webmodel 里面就很简单了
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);begin response.Content:='你好!' end;
然后我们开始运行这个程序。
打开浏览器,就会发现,我们的webbroker 程序运行正常。
webbroker 服务器成功了,那么常用的webservice 也就不在话下了。
根据自带的向导,替换对应的主主窗体的文件,运行,棒棒哒。
有同学质疑,这个真的是http.sys提供的服务吗?
那么有图有真相:
datasnap 的·例子就不再演示了,方法与上面差不多。
最后,对于不使用datasnap,使用kbmmw 的同学,不用担心,在kbmmw 里面照样可以使用http.sys ,
只不过是要写对应的transport.下面给出服务端和客户端的对象声明。
unit kbmMWHTTPAPIServerTransport;{$define httpsyslog} interface uses Classes, Sysutils,
kbmMWCustomTransport,kbmMWServer,kbmMWGlobal, variants, kbmMWHTTPUtils,
{$ifdef httpsyslog} kbmMWLog,
{$endif} SynCommons,
SynZip,
SynCrtSock;type TProtServer = class(TkbmMWServer);
TxalionTransport=class(TkbmMWCustomServerTransport); Txalioninfo=class(TkbmMWServerTransportInfo); Txalionserver = class private FServer:Tkbmmwserver;
FTransport: TkbmMWCustomServerTransport; fPath: TFileName;
fapiServer: THttpApiServer;
function Process(C : THttpServerRequest): cardinal;
public
destructor Destroy; override; end; TkbmMWCustomhttpapiServerTransport = class(TkbmMWCustomServerTransport)
private { Private declarations } FhttpsysServer: TxalionServer; Fhost:string;
Fport:string;
FServerUrl:string;
Fssl:boolean;
Fversion:string;
FHTTPQueueLength: integer; FServerThreadPoolCount :integer; public // @exclude constructor Create(AOwner:TComponent); override;
// @exclude destructor Destroy; override; public class function IsSerializedTransport:boolean; override;
class function IsConnectionlessTransport:boolean; override; procedure Listen; override;
procedure Close; override;
function IsListening:boolean; override; published { 设置url 例如/kbmmw} property ServerURL:string read Fserverurl write Fserverurl; { 服务器 ip 例如 127.0.0.1} property Host:string read Fhost write Fhost; property Port:string read Fport write Fport; property SSL:boolean read fssl write fssl; Property Version:string read Fversion;
property HTTPQueueLength: integer read FHTTPQueueLength write FHTTPQueueLength;
property ServerThreadPoolCount: integer read FServerThreadPoolCount write FServerThreadPoolCount; end; TkbmMWhttpapiServerTransport= class(TkbmMWCustomhttpapiServerTransport)
published { Published declarations } property Crypt;
property Compression;
property StreamFormat;
property VerifyTransfer;
property TransportStateOptions;
property FormatSettings;
property Plugin;
property Params;
property StringConversion;
property NodeID;
property ClusterID;
end;
{$I httpsysversion.inc}
unit kbmMWNativeHTTPClientTransport;// by xalion interface {$I kbmMW.inc} {.$define indyhttp} {.$define httpsyslog} uses Classes, Sysutils, kbmMWCustomTransport,kbmMWClient, {$ifdef indyhttp} idhttp,
{$else} System.Net.HttpClientComponent,System.Net.HttpClient,
{$endif} {$ifdef httpsyslog} kbmMWLog,
{$endif} kbmMWGlobal;type {$IFDEF LEVEL16} [ComponentPlatformsAttribute({$IFDEF LEVEL23}pidiOSDevice64 or {$ENDIF}{$IFDEF LEVEL18}pidiOSSimulator or pidiOSDevice or {$ENDIF}{$IFDEF LEVEL19}pidAndroid or {$ENDIF}pidWin32 or pidWin64{$IFDEF LEVEL17} or pidOSX32{$ENDIF})]{$ENDIF} TkbmMWNativeHTTPClientTransport = class(TkbmMWCustomClientTransport)
private {$ifdef indyhttp} FHttpClient:Tidhttp;
{$else} FHttpClient:TNetHTTPClient;
{$endif} FTimeout:integer;
MyRequestContent:TMemoryStream;
fhost:string;
fserverurl:string;
fssl:boolean;
Fversion:string;
FClientType:string; public constructor Create(AOwner:TComponent); override;
destructor Destroy; override; class function IsSerializedTransport:boolean; override;
class function IsConnectionlessTransport:boolean; override; procedure Connect; override;
procedure Disconnect; override;
procedure Assign(ATransport:TPersistent); override; function ReceiveStream(AInfo:IkbmMWCustomTransportInfo; const AStream:IkbmMWCustomTransportStream; ALimit:integer):boolean; override;
procedure TransmitStream(AInfo:IkbmMWCUstomTransportInfo; const AStream:IkbmMWCustomTransportStream); override;
published property Host:string read fhost write fhost;
property ServerURL:string read fserverurl write fserverurl;
property SSL:boolean read fssl write fssl;
Property ClientType:string read FClientType;
Property Version:string read Fversion; property Crypt ;
property Compression ;
property StreamFormat;
property StringConversion;
property Timeout:integer read FTimeout write FTimeout default 3000;
property OnException; property OnConnectionLost;
property OnReconnect;
property MaxRetries;
property MaxRetriesAlternative;
property ConnectionString;
property FallbackServers;
property AutoFallback;
property VerifyTransfer; end;
{$I httpsysversion.inc}
使用http.sys 的应用服务器比使用indy 的速度及稳定性都大大提高。
经过多个实际项目的使用,效果非常好。
总而言之,在windows 上,使用http.sys,就这么自信!
使用http.sys,让delphi 的多层服务飞起来的更多相关文章
- 使用delphi 开发多层应用(二十一)使用XE5 RESTClient 直接访问kbmmw 数据库
delphi XE5 出来了,增加了android 的开发支持,另外增加了一个RESTClient 来支持访问REST 服务器. 这个功能非常强大,可以直接使用非常多的REST 服务器.同时也可以支持 ...
- 在Delphi开发的服务中调用指定应用程序
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://fxh7622.blog.51cto.com/63841/529033 在很多时候 ...
- 使用delphi 开发多层应用(十六)使用XMLRPC 实现basic4android 远程调用RTC服务(讲述了RTC的特点,其底层通讯协议是自己封装SOCK 库,与kbmmw 的适合场合不完全一样)
RealThinClient (以下简称RTC) 也是一款delphi 多层开发的框架,由于其底层通讯协议是自己封装SOCK 库,抛弃了 大家诟病的indy,因此表现的非常稳定,效率也非常高, ...
- Delphi的TService 服务路径获取 Dll中获取文件路径
研究delphi服务的路径,试了好几个方法 ,都没取出来,最后发现,要采用取DLL路径的方法 //一.获取Dll自身路径 //1)方法一: Function GetDllPath(sDllName:s ...
- 使用delphi 开发多层应用(二十二)使用kbmMW 的认证管理器
从kbmmw 4.4 开始,增加了认证管理器,这个比原来的简单认证提供了更多的功能.细化了很多权限操作. 今天对这一块做个介绍. 要做一个认证管理,大概分为以下5步: 1. 定义你要保护的资源,一般 ...
- 使用delphi 开发多层应用(十九) ios通过soap 访问kbmmw服务器
随着delphi XE4 的推出,开始真正意义上支持ios 的开发,由于目前kbmmw 还不完全支持ios 的开发,因此 无法直接使用kbmmw 的客户端访问kbmmw 的服务器(虽然kbmmw 也提 ...
- Win7/Vista/Server2008下VS 环境 调试调用 HTTP.SYS 无法启动监听服务及启动后其他机器无法访问端口
一. VS调试在Win7(vista系列)操作系统下 HttpListener无法绑定多个 指定IP.端口问题 来自:http://www.cnblogs.com/ryhan/p/4195693.ht ...
- 转(Delphi 新窑洞):使用delphi 开发多层应用(十七)使用RTC web 服务器返回JSON
RTC作为delphi 的最专业的web 应用服务器,如果客户端要使用JSON 的话,那么使用RTC 应该也是一种 非常好的选择.下面我们做一个使用RTC web 服务器返回数据库JSON 的例子. ...
- delphi xe5 android 服务端和手机端的源码下载
xe5 android的服务端和手机客户端的源代码下载地址 http://files.cnblogs.com/nywh2008/AndroidTest.rar
随机推荐
- [No000074]C#创建桌面快捷方式
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单
一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...
- Web 开发中很实用的10个效果
在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...
- javaScript中的页面传值
function getURIParam(name) { var search = window.location.search; search = search.substring(1); if ( ...
- Java 8新特性终极指南
目录结构 介绍 Java语言的新特性 2.1 Lambdas表达式与Functional接口 2.2 接口的默认与静态方法 2.3 方法引用 2.4 重复注解 2.5 更好的类型推测机制 2.6 扩展 ...
- 基于Bootstrap仿淘宝分页控件实现
.header { cursor: pointer } p { margin: 3px 6px } th { background: lightblue; width: 20% } table { t ...
- 终于等到你---订餐系统之负载均衡(nginx+memcached+ftp上传图片+iis)
又见毕业 对面工商大学的毕业生叕在拍毕业照了,一个个脸上都挂满了笑容,也许是满意自己四年的修行,也许是期待步入繁华的社会... 恰逢其时的连绵细雨与满天柳絮,似乎也是在映衬他们心中那些离别的忧伤,与对 ...
- 【CF 710F】String Set Queries
在校内OJ上A了,没有加强制在线的东西..不放链接了. 这道题题意是维护一个字符串集合,支持三种操作: 1.加字符串 2.删字符串 3.查询集合中的所有字符串在给出的模板串中出现的次数 操作数\(m ...
- BZOJ 4698: Sdoi2008 Sandy的卡片
4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 106 Solved: 40[Submit][Stat ...
- Codeforces Round #342 (Div. 2) D. Finals in arithmetic(想法题/构造题)
传送门 Description Vitya is studying in the third grade. During the last math lesson all the pupils wro ...