Delphi2010中DataSnap技术
文章来源: https://blog.csdn.net/xieyunc/article/details/47865227?_t_t_t=0.3049736963513836
一、为DataSnap系统服务程序添加描述
这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7该下岗了。
DataSnap有三种服务模式,其中Service Application方式建立的Windows服务没有描述,描述部分是空的,可用如下方法添加服务描述:
- procedure TServerContainer.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', '机房管理系统核心服务');
- end;
- CloseKey;
- end;
- finally
- reg.Free;
- end;
- end;
二、DataSnap服务端和客户端发布分发方法
服务器发布方法:
1.在unit ServerMethodsUnit1单元中,添加uses MidasLib;(添加MidasLib的目的是省去发布Midas.dll)
2.如果用的是火鸟数据库,只需拷贝dbxfb.dll和fbclient.dll,如果用的是SQLite,则什么都不用拷贝。
分发的服务器软件只需三个文件:你的服务器程序、dbxfb.dll 和 fbclient.dll
客户端发布方法:
1.在客户端程序中加上uses MidasLib;(添加MidasLib的目的是省去发布Midas.dll)
2.如果服务器使用了http协议作为DataSnap通讯的话,还需在客户端程序中加上users DSHTTPLayer,如果使用tcp协议,无需此步骤。
分发的客户端软件只需一个文件:你的客户端程序
服务器和客户端无需Midas.dll,也不需要注册regsvr32 Midas.dll,看来Delphi2010的datasnap抛弃使用COM真是进步不少!
三、DataSnap服务器如何得到客户端的IP和端口
作为一个服务器软件,必须做到对客户端强有力的控制,想要控制,就必须得到客户端的网络基本信息,比如客户端IP和端口。有了客户端IP就能随心所欲操控客户端,比如终止某些客户端的连接、限制功能等等。
在Delphi2010中的DataSnap服务器如何获得客户端ip,的确花了我点时间,奇怪为什么这个功能不做的更人性化点呢,功能总是藏着掖着。还得让程序员像寻宝一样摸索,浪费时间。现在把我整理的结果奉献给大家,免得大家在花时间研究这个。
另外,通过研究发现,DSConnectEventObject.ChannelInfo.Id属性实际上是内存地址,并不是一个简单的数字。
以下代码中if .. then 里面的内容是关键。
- 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;
或者也可以这样:
- procedure TServerContainer1.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);
- var
- ClientConnection: TDBXClientInfo;
- Val: TTCP_KeepAlive;
- Ret: DWord;
- begin
- ClientConnection := DSConnectEventObject.ChannelInfo.ClientInfo;
- AddLog(
- ClientConnection.IpAddress
- +':'+
- ClientConnection.ClientPort
- +'登录服务器');
- UpdateLinkToList(ClientConnection.IpAddress
- +':'+
- ClientConnection.ClientPort
- ,IntToStr(DSConnectEventObject.ChannelInfo.Id)
- ,);
- end;
四、DataSnap中的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 := ;
- Val.KeepAliveTime := ;
- Val.KeepAliveInterval := ;
- WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or ,
- @Val, SizeOf(Val), nil, , @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 >= ) 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[, ]) <> '' then
- ShowConnections.RowCount := ShowConnections.RowCount + ;
- ShowConnections.Cells[, ShowConnections.RowCount - ] := IntToStr
- (DSConnectEventObject.ChannelInfo.Id);
- ShowConnections.Cells[, ShowConnections.RowCount - ] :=
- ClientConnection.Socket.Binding.PeerIP + ':' + IntToStr
- (ClientConnection.Socket.Binding.PeerPort);
- ShowConnections.Cells[, ShowConnections.RowCount - ] :=
- DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName];
- ShowConnections.Cells[, ShowConnections.RowCount - ] :=
- DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password];
- ShowConnections.Cells[, ShowConnections.RowCount - ] := FormatDateTime
- ('yyyy-mm-dd hh:nn:ss', Now);
- // ShowConnections.Cells[6, ShowConnections.RowCount - 1] :=
- // DSConnectEventObject.ConnectProperties
- // [TDBXPropertyNames.ServerConnection];
- // 设置心跳包
- val.OnOff := ;
- val.KeepAliveTime := ;
- val.KeepAliveInterval := ;
- WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or , @val, SizeOf(val), nil, , @Ret, nil, nil);
- end;
- end;
当然,之后的版本已无需手动加入心跳包代码了,因为TDSTCPServerTransport本身已经增加了相应的属性和功能,如:
Delphi2010中DataSnap技术的更多相关文章
- Delphi2010中DataSnap技术网摘
一.为DataSnap系统服务程序添加描述 这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7该下岗了. DataSnap有三种服务模式,其中 ...
- Delphi 中DataSnap技术网摘
Delphi2010中DataSnap技术网摘 一.为DataSnap系统服务程序添加描述 这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7 ...
- Delphi2010中DataSnap高级技术(转)
一. 为DataSnap系统服务程序添加描述 这几天一直在研究Delphi 2010的DataSnap,感觉功能真是很强大,现在足有理由证明Delphi7该下岗了. DataSnap有三种服务模式,其 ...
- Delphi2010中DataSnap高级技术(7)—TDSServerClass中Lifecycle生命周期三种属性说明
Lifecycle 三种属性: Session.Invocation.Server 这三种属性都用在什么情况,有什么要注意的事项,Delphi2010中罕有说明. 如果乱用这三种属性,你的服务程序有可 ...
- php中CURL技术模拟登陆抓取数据实战,抓取某校教务处学生成绩。
这两天有基友要php中curl抓取教务处成绩的源码,用于微信公众平台的开发.下面笔者只好忍痛割爱了.php中CURL技术模拟登陆抓取数据实战,抓取沈阳工学院教务处学生成绩. 首先,教务处登录需要验证码 ...
- 转:LoadRunner中参数化技术详解
LoadRunner中参数化技术详解 LoadRunner在录制脚本的时候,只是忠实的记录了所有从客户端发送到服务器的数据,而在进行性能测试的时候,为了更接近真实的模拟现实应用,对于某些信息需要每次提 ...
- 谈谈书本《c#物联网程序设计基础》中的技术瑕疵,如果你将要读本书,请进来看看!
今天去书店看到一本名为<c#物联网程序设计基础>的书,对物联网感兴趣的我抓起来就看,书中的项目都是上位机开发项目,较简单,如果物联网开发只是这样,看起来我做物联网开发也是绰绰有余.这边书我 ...
- 关于PHP中会话技术的知识点分享
前言:在PHP中会话技术也是特别重要的,主要应用在免登录,保存一些持久化数据等等的方面,但是后期的介绍中,我将会放弃这种技术改用redis方法来替换这种方法. (一)cookie技术(即数据缓存在客户 ...
- 转:DDR中端接技术基本概念
DDR中端接技术基本概念 版权声明:转载请注明出处:http://blog.csdn.net/lg2lh https://blog.csdn.net/lg2lh/article/details/90 ...
随机推荐
- unity-------------UI的界面调节
Rect Transform 我们都知道,Unity3D中所有的GameObject都必须要携带一个Transform组件,且该组件无法移除,那么作为UI显示的GameObject则不是携带Trans ...
- Linux 内存buffer与cache区别_003
一.首先大概了解一下计算机CPU.Cache.Buffer.内存.硬盘.SWAP CPU也称为中央处理器(CPU,Central Processing Unit)是一块超大规模的集成电路,是一台计算机 ...
- 【苏勇老师Linux 入门笔记】网络基础
IP 地址 IP 编制时一个双层编制方案,一个 IP 地址标示一个主机 (或一个网卡接口). 一个 IP 地址分为两个部分:网络部分(所属区域)和主机部分(标示区域中的哪个主机).IPv4 共32位, ...
- C# 指定ip段生成ip地址
private void button1_Click(object sender, EventArgs e) { string StartIp = ""; string EndIp ...
- asp.net导出excel 问题及服务器的部署dcom组件配置
一.服务器上没有装office 如果要用MS的,这个问题基本不用考虑,只有安装才能解决,没有其它办法! (即使有牛人弄出来 了,估计也是给自己找麻烦) 不过,我只在服务器上装了一个2003精简版, 我 ...
- yield 举例
示例代码: 神奇的地方在于yield返回的是一个IEumerable,可以直接枚举. // yield-example.cs using System; using System.Collection ...
- Java时间日期字符串格式转换大全
import java.text.*; import java.util.Calendar; public class VeDate { /** * 获取现在时间 * * @return 返回时间类型 ...
- CentOS 7中添加一个新用户并授权
Linux 创建web用户组及用户: groupadd www-data useradd -g www-data www-data 笔记本安装了一个CentOS,想要让别人也可以登录访问,用自己的账号 ...
- AES五种加密模式
分组密码在加密时明文分组的长度是固定的,而实用中待加密消息的数据量是不定的,数据格式可能是多种多样的.为了能在各种应用场合安全地使用分组密码,通常对不同的使用目的运用不同的工作模式. 一.电码本模式( ...
- SpringMVC------报错:java.lang.ClassNotFoundException: org.springframework.web.filter.CharacterEncodingFilter
详细信息: java.lang.ClassNotFoundException: org.springframework.web.filter.CharacterEncodingFilter 严重: E ...