一、 为DataSnap系统服务程序添加描述

这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7该下岗了。

DataSnap有三种服务模式,其中Service Application方式建立的windows服务没有描述,描述部分是空的,感觉总是欠缺点什么。

现找到办法添加描述:

procedure TServerContainer2.ServiceAfterInstall(Sender: TService);

var

reg: TRegistry;

begin

reg := TRegistry.Create;

try

with reg do

begin

RootKey := HKEY_LOCAL_MACHINE;

if OpenKey('SYSTEM\CurrentControlSet\Services\' + Self.Name, false) then

begin

WriteString('Description', 'It is my service');

end;

CloseKey;

end;

finally

reg.Free;

end;

end;

一、 DataSnap服务端和客户端发布分发方法

针对服务器和客户端软件,如何发布呢?经过研究发现,分发方法非常简单!

服务器发布方法:

1.在unit ServerMethodsUnit1单元中,添加uses MidasLib;(添加MidasLib的目的是省去发布Midas.dll)

2.我用的是火鸟数据库,只需拷贝dbxfb.dll和fbclient.dll

分发的服务器软件只需三个文件:你的服务器程序、dbxfb.dll 和 fbclient.dll

客户端发布方法:

1.在客户端程序中加上uses MidasLib;(添加MidasLib的目的是省去发布Midas.dll)

2.如果服务器使用了http协议作为datasnap通讯的话,还需在客户端程序中加上 uses DSHTTPLayer;如果使用tcp协议,无需此步骤

分发的客户端软件只需一个文件:你的客户端程序

服务器和客户端无需Midas.dll,也不需要注册regsvr32 Midas.dll,看来Delphi2010的datasnap抛弃使用COM真是进步不少!

发布程序竟是如此简单!!!

二、 DataSnap服务器如何得到客户端的IP和端口

作为一个服务器软件,必须做到对客户端强有力的控制,想要控制,就必须得到客户端的网络基本信息,比如客户端IP和端口。有了客户端IP就能随心所欲操控客户端,比如终止某些客户端的连接、限制功能等等。

在delphi2010中的datasnap服务器如何获得客户端ip,的确花了我点时间,奇怪为什么这个功能不做的更人性化点呢,功能总是藏着掖着。还得让程序员像寻宝一样摸索,浪费时间。现在把我整理的结果奉献给大家,免得大家在花时间研究这个。

另外,通过研究发现,DSConnectEventObject.ChannelInfo.Id 属性实际上是内存地址,并不是一个简单的数字。

以下代码红色部分是关键。

uses IdTCPConnection;

......

procedure TServerContainer1.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);

var

ClientConnection: TIdTCPConnection;

begin

with Form1 do

begin

dsShowDataSet.Append;

dsShowDataSet['ClientConnectTime'] := Now;

if DSConnectEventObject.ChannelInfo <> nil then

begin

ClientConnection:=TIdTCPConnection(DSConnectEventObject.ChannelInfo.Id);

dsShowDataSet['ClientID'] := DSConnectEventObject.ChannelInfo.Id;

dsShowDataSet['ClientIP'] := ClientConnection.Socket.Binding.PeerIP +

':' + IntToStr(ClientConnection.Socket.Binding.PeerPort);

dsShowDataSet['ServerIP'] := ClientConnection.Socket.Binding.IP + ':' +

IntToStr(ClientConnection.Socket.Binding.Port);

end;

dsShowDataSet['ClientUserName'] := DSConnectEventObject.ConnectProperties

[TDBXPropertyNames.UserName];

dsShowDataSet['ClientUserPassword'] :=

DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password];

dsShowDataSet['ServerInfo'] := DSConnectEventObject.ConnectProperties

[TDBXPropertyNames.ServerConnection];

dsShowDataSet.Post;

end;

end;

三、 TCP keepAlive和KeepAliveInterval参数详解

Delphi2010中DataSnap,如果客户端异常掉线或拔掉网线,那么在服务端会留下一个TCP连接,这个连接会变成死连接(经过测试,如果windows的TCP保持连接禁用的话,三个小时该死连接还不消失)。如果大量客户端并发,出现的死TCP连接过多,服务器内存和端口将会增加,直到占满服务器的端口和耗尽内存为止。如果这样的话,服务器无法健壮稳定的运行。

大家可以另开线程来监控客户端连接,但是今天要给大家讲解的不是这个方法,而是使用TCP协议自带的心跳包功能解决这个问题。

大家先了解一下 TCP keep-alive原理

一个TCP keep-alive 包是一个简单的ACK,该ACK包内容为一个比当前连接sequence number 小于一的包。主机接受到这些ACKs会返回一个包含当前sequence number 的ACK包。

Keep-alives一般被用来验证远端连接是否有效。如果该连接上没有其他数据被传输,或者更高level 的 keep-alives被传送,keep-alives 在每个KeepAliveTime被发送。(默认是 7,200,000 milliseconds ,也就是2个小时)。

如果没有收到 keep-alive 应答,keep-alive 将在每 KeepAliveInterval 秒重发一次。KeepAliveInterval 默认为1秒。如 Microsoft 网络功能中很多部分中采用的 NETBT 连接,更常见的是发送 NETBios keep-alives,所以,在 NetBios 连接中通常不发送TCP keep-alives。

TCP保持连接默认被禁用,但是微软Sockets应用程序可以使用SetSockOpt函数去启用他们。

请看下面的类

type

TCP_KeepAlive = record

OnOff: Cardinal;

KeepAliveTime: Cardinal; // 多长时间(ms)没有数据就开始send心跳包

KeepAliveInterval: Cardinal // 每隔多长时间(ms)send一个心跳包,发5次(系统值)

end;

KeepAliveTime: TCP连接多长时间(毫秒)没有数据就开始发送心跳包,有数据传递的时候不发送心跳包

KeepAliveInterval: 每隔多长时间(毫秒)发送一个心跳包,发5次(系统默认值)

如果客户端网络中断,服务器系统发送心跳包后,服务器会自动解除TCP连接。这一点,大家可以使用 netstat -p -tcp 命令查看

接下来我们将结合Delphi2010 DataSnap技术使用心跳包功能!敬请关注

四、 建立稳定服务程序之TCP心跳包的使用

为了能让我们的服务程序更加稳定,有些细节问题必须解决。就如上一讲中提到的客户端拔掉网线,造成服务器上TCP变成死连接,如果死连接数量过多,对服务器能长期稳定运行是一个巨大的威胁。

另外,经过测试,如果服务器上有TCP死连接,那么服务程序连接数据库,也会产生那个一个死连接。这样的话,给数据库服务器也造成威胁。所以,服务器程序编写的好坏,直接影响系统的稳定性!

如何解决TCP死连接的问题,有多种方法,其中最有效的就是心跳包技术。

我们在DSServer的OnConnect事件中加入心跳包代码

uses IdTCPConnection,IdWinsock2

........

type

TCP_KeepAlive = record

OnOff: Cardinal;

KeepAliveTime: Cardinal;

KeepAliveInterval: Cardinal;

end;

........

procedure TServerContainer1.DSServer1Connect

(DSConnectEventObject: TDSConnectEventObject);

var

Val: TCP_KeepAlive;

Ret: DWord;

ClientConnection: TIdTCPConnection;

begin

ClientConnection := TIdTCPConnection(DSConnectEventObject.ChannelInfo.Id);

Val.OnOff := 1;

Val.KeepAliveTime := 5000;

Val.KeepAliveInterval := 3000;

WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4,

@Val, SizeOf(Val), nil, 0, @Ret, nil, nil);

end;

观察上述代码,我们把心跳包放到服务端上执行,如果服务器的某个TCP连接在5秒钟没有收到数据,将会发送向对端发送心跳包,间隔3秒钟,连续发送5次(参数详解见上一讲高级技术4)。如果5次以后对端还没有应答,服务器将结束该TCP连接。TCP的连接可以使用 netstat -p tcp 命令查看。

当该TCP结束后,delphi编写的服务程序会自动结束和数据库的连接。我用的是FireBird数据库,大家可以使用命令查看

SELECT MON$USER, MON$REMOTE_ADDRESS,MON$REMOTE_PID,MON$TIMESTAMP FROM MON$ATTACHMENTS

现在服务器的tcp死连接和数据库的死连接都清除了,我们的系统将能长期稳定的运行。

六、加强服务程序对访问者的控制能力

1)作为一个服务程序,如果不限制客户端访问数量,后果将是很可怕的。如果有人恶搞,服务器不堪重负,内存将耗尽,最终服务器将宕机。如何限制访问者的数量呢?

我们可以设置一个变量,来记录来访者的数量,如果超过我们既定的数字,那么后续的连接服务器请求,都将被断掉。

2)限制了访问数量,但是如果不做密码身份认证,无关的人员也将能登陆服务器!解决办法是客户端传入用户名和密码,如果用户名和密码不正确,连接将被挂断。

在客户端的SQLConnection1中driver分类的username和password属性设置好用户名和密码。

3)尽量不要设置DSTCPServerTransport1的Maxthreads属性,还有数据库连接池也不要设置,delphi2010会有内存泄露,这两个参数保存默认即可。

在dsserver1控件的onconnect事件中加入如下代码(使用的是tcp/ip连接):

procedure TMainForm.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);

var

val: TCP_KeepAlive;

Ret: Integer;

ClientConnection: TIdTCPConnection;

begin

// 最大连接数量,验证来访者密码

if (DSConnectEventObject.ChannelInfo = nil) or (Connections >= 500) or

(DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName]

<> 'sunstone') or (DSConnectEventObject.ConnectProperties

[TDBXPropertyNames.Password] <> 'mypassword') then

begin

DSConnectEventObject.DbxConnection.Destroy;

// ClientConnection.Disconnect;

end

else

begin

// 获取socket连接

ClientConnection := TIdTCPConnection(DSConnectEventObject.ChannelInfo.Id);

ClientConnection.OnDisconnected := ClientDisconnectEvent;

// 记录来访者数量

inc(Connections);

lblShowConnections.Caption := IntToStr(Connections);

if Trim(ShowConnections.Cells[0, 1]) <> '' then

ShowConnections.RowCount := ShowConnections.RowCount + 1;

ShowConnections.Cells[0, ShowConnections.RowCount - 1] := IntToStr

(DSConnectEventObject.ChannelInfo.Id);

ShowConnections.Cells[1, ShowConnections.RowCount - 1] :=

ClientConnection.Socket.Binding.PeerIP + ':' + IntToStr

(ClientConnection.Socket.Binding.PeerPort);

ShowConnections.Cells[2, ShowConnections.RowCount - 1] :=

DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName];

ShowConnections.Cells[3, ShowConnections.RowCount - 1] :=

DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password];

ShowConnections.Cells[4, ShowConnections.RowCount - 1] := FormatDateTime

('yyyy-mm-dd hh:nn:ss', Now);

// ShowConnections.Cells[6, ShowConnections.RowCount - 1] :=

// DSConnectEventObject.ConnectProperties

// [TDBXPropertyNames.ServerConnection];

// 设置心跳包

val.OnOff := 1;

val.KeepAliveTime := 5000;

val.KeepAliveInterval := 1000;

WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4,

@val, SizeOf(val), nil, 0, @Ret, nil, nil);

end;

end;

七、 TDSServerClass中Lifecycle生命周期三种属性说明

Lifecycle 三种属性: Session、Invocation、Server

这三种属性都用在什么情况,有什么要注意的事项,Delphi2010中罕有说明。

如果乱用这三种属性,你的服务程序有可能崩溃,数据混乱,内存占用大,效率低等问题!

下面我对这三种属性的使用环境逐一介绍:

1. Session

说明:这是delphi2010中默认属性,也是delphi推荐设置。Session会为每个来自客户端的链接,建立一个线程来实例化。实例化是什么概念呢?就是这个线程把所有你将要用到的类、函数等等都建立好了,等待你客户端直接使用。这个线程和实例化并不释放,直到客户端中断连接。如果有300个客户端,那么你的服务器将会有300线程和实例,对服务器硬件和内存是个考验。

适用环境:这个设置是线程安全的!

客户端数量少,每台服务器不超过连接数量: 200 x CPU个数 x (每个CPU核数x0.7) (这是经验值,稳定连接的数量,不是极限数量,别误解^_^),内存现在很便宜了,想加多大就多大!

客户端频繁调用服务器数据,无论连接数量是多少,最好都用这种设置。如果客户端很多,建议采用负载平衡和多台服务器来解决。

2. Invocation

说明:服务器只是建立连接,但是先不做实例化,只有当客户端请求功能的时候,服务器才开线程并实例化,当客户端用完后,服务器就释放线程和实例。

适用环境:这个设置是线程安全的!

如果客户端调用服务器数据频率低,这种方法很不错,会节约很多内存。

3. Server

说明:服务器对所有客户端连接使用一个实例,不是线程安全的。所以要自己控制客户端并发调用的问题(可以使用互斥、原子量等方法),让客户端的调用排成一队使用服务器资源。

适用环境:这个设置不是线程安全的!!

配置较低的服务器

服务器连接的另一端只能是单线程工作的模式

提前祝大家2010年春节快乐!

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sunstone/archive/2010/02/02/5282666.aspx

Delphi2010中DataSnap高级技术(转)的更多相关文章

  1. Delphi2010中DataSnap高级技术(7)—TDSServerClass中Lifecycle生命周期三种属性说明

    Lifecycle 三种属性: Session.Invocation.Server 这三种属性都用在什么情况,有什么要注意的事项,Delphi2010中罕有说明. 如果乱用这三种属性,你的服务程序有可 ...

  2. Delphi2010中DataSnap技术网摘

    一.为DataSnap系统服务程序添加描述 这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7该下岗了. DataSnap有三种服务模式,其中 ...

  3. Delphi2010中DataSnap技术

    文章来源: https://blog.csdn.net/xieyunc/article/details/47865227?_t_t_t=0.3049736963513836 一.为DataSnap系统 ...

  4. DataSnap高级技术(7)—TDSServerClass中Lifecycle生命周期三种属性说明

    From http://blog.csdn.net/sunstone/article/details/5282666 DataSnap高级技术(7)—TDSServerClass中Lifecycle生 ...

  5. Delphi 中DataSnap技术网摘

    Delphi2010中DataSnap技术网摘 一.为DataSnap系统服务程序添加描述 这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7 ...

  6. 译:DOM2中的高级事件处理(转)

    17.2. DOM2中的高级事件处理(Advanced Event Handling with DOM Level 2)        译自:JavaScript: The Definitive Gu ...

  7. paip.java 架构师之路以及java高级技术

    paip.java 架构师之路以及java高级技术 1.    Annotation 设计模式... 概念满天飞.ORM,IOC,AOP. Validator lambda4j memcache. 对 ...

  8. 【python游戏编程之旅】第七篇---pygame中的冲突检测技术

    本系列博客介绍以python+pygame库进行小游戏的开发.有写的不对之处还望各位海涵. 上一个博客我们一起学习了pygame中的Sprite模块和如何加载动画:http://www.cnblogs ...

  9. 【ASP.NET Identity系列教程(三)】Identity高级技术

    注:本文是[ASP.NET Identity系列教程]的第三篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...

随机推荐

  1. 多线程-闭锁CountDownLatch

    闭锁相当于相当于一扇门,在闭锁到达结束状态之前,这扇门一直是关着的,所有的线程都不可以通过.它可以使一个或者一组线程等待某个时间发生.闭锁状态包括一个计数器,初始化的时候传入一个正数,这个数字表示等待 ...

  2. python处理数据的风骚操作[pandas 之 groupby&agg]

    https://segmentfault.com/a/1190000012394176 介绍 每隔一段时间我都会去学习.回顾一下python中的新函数.新操作.这对于你后面的工作是有一定好处的.本文重 ...

  3. (转)Java编译后产生class文件的命名规则

      今天刚好有同学问了下Java编译后产生的.class文件名的问题,虽然一直都在使用Java做开发,但是之前对编译后产生的.class文件名的规范也基本没做了解过,也真的是忏愧啊!今天无论如何都要总 ...

  4. thinkphp <eq> <if>标签 condition中可以写PHP的判断逻辑

    <ul> <volist name="monthArray" id="monthItem"> <if condition=&quo ...

  5. hackerrankWeek of Code 31

    hackerrankWeek of Code 31 A.Beautiful Word B.Accurate Sorting C.Zero-One Game D.Spanning Tree Fracti ...

  6. mongodb笔记(一) 分片 &&文档连接

    版本:mongodb3.4 ; 分片: 工作顺序:router=>config=>shards 一,配置config: 3.4中config必须为replSet.下面配置两个config. ...

  7. MFC,ATL,CLR简介

    MFC.ATL和CLR是VC2005内置的3大库,涵盖了windows的各种开发方法和开发应用.当然关于 C++ 开发的库不止这3个,不过这3个是微软推荐.从编程所处层次而言,WIN32为最底层,其次 ...

  8. 如何学好C++语言

    前段时间写了一篇如何学好C语言,就有人回复问我如何学好C++,所以,我把我个人的一些学习经验写在这里,希望对大家有用.首先,因为如何学好C语言中谈到了算法和系统,所以这里就只谈C++语言. C++是最 ...

  9. 四边形不等式优化_石子合并问题_C++

    在动态规划中,经常遇到形如下式的状态转移方程: m(i,j)=min{m(i,k-1),m(k,j)}+w(i,j)(i≤k≤j)(min也可以改为max) 上述的m(i,j)表示区间[i,j]上的某 ...

  10. 32-THREE.JS 球体

    <!DOCTYPE html> <html> <head> <title>Example 05.05 - Basic 3D geometries - S ...