timer代码很简单:
var adbsevertime :TDateTime;
begin
try
adbsevertime :=
ClientModule1.ServerMethods1Client.GetDbServerTime;
if adbsevertime <> 0 then
begin
gdbStatus := true;
TimerReconnet.Enable:=false;
end;
except
on E: Exception do
begin
TimerReconnet.Enable:=true;
end;
end;
end;
GetDbServerTime是dephi自动生成的客户端访问中间层的方法
网上找到三种方法:
http://www.cnblogs.com/yagzh2000/archive/2013/04/27/3046637.html
http://blog.csdn.net/sunstone/article/details/5023718
(http://blog.163.com/bin0315@126/blog/static/4066264220128414025829/
类似)
http://bbs.2ccc.com/topic.asp?topicid=396438
我每个测试一下,到时候结果通知大家。
我来说下这一天的成果:
因为自己很少用datasnap,然后网上对indy有偏见言辞,我抱着这个心态开始实验:
因为我用的是 datasnap rest 服务,也就是没有用tcp,用的是http,网上的资料对我来说初看没有用。xe2对于tcpconnection设置了心跳包属性,也对我来说无用。
因为用的是http,用后即关闭,所以心跳包是否有用,我也不清楚。
怎么办呢?最后我找到TIdHTTPWebBrokerBridge这个实例Fserver里面有个onconnect事件,可以用,于是就手写了事件照猫画虎把delphi2010的代码粘贴下来了,不知道是否用。
代码如下:
FServer := TIdHTTPWebBrokerBridge.Create(Self);
FServer.OnConnect:=ServerConnect;
FServer.OnDisconnect:= ServerDisConnect ;
实现代码:
procedure TForm1.ServerConnect(AContext: TIdContext);
var
Val: TCP_KeepAlive;
Ret: DWORD;
conne: tidtcpconnection;
aip, aport, ausername, apass: string;
begin
//验证
conne := TIdTCPConnection(AContext.Connection);
if conne <> nil then
begin
aip := conne.Socket.Binding.PeerIP;
aport := inttostr(conne.Socket.Binding.PeerPort);
// ausername := DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName];
// apass := DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password];
// if (ausername <> 'jiangbin') and (apass <> '2010') then
// DSConnectEventObject.DbxConnection.Destroy
// else
// fr_main.memo1.Lines.add(aip + ':' + aport + ' 名称:' + ausername);
//心跳包代码
Val.OnOff := 1;
Val.KeepAliveTime := 5000;
Val.KeepAliveInterval := 3000;
WSAIoctl(conne.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4, @Val,
SizeOf(Val), nil, 0, @Ret, nil, nil);
end;
end;
procedure TForm1.ServerDisConnect(AContext: TIdContext);
var
conne: tidtcpconnection;
aip, aport, ausername, apass: string;
i: integer;
begin
//断开后清除连接IP及端口
conne := TIdTCPConnection(AContext.Connection);
if conne <> nil then
begin
aip := conne.Socket.Binding.PeerIP;
aport := IntToStr(conne.Socket.Binding.PeerPort);
end;
end;
但是不知道是否有用,待测。
另外,我在调试过程中,发现indy并不是想象的那么差,很完善
其实delphi事实是调用tidhttp 完成rest服务的,只是我这边有个问题,就是如果断线,调用异常,再次调用服务,就会出现服务器的index of bounds越界错误,(主要是构建http头,出现异常),这个应该是一个Indy bug吧,所以我准备临界来处理datasnap的中间层请求,保证异常后,不能同时再被访问,这样就可以了。
解决如果出现HTTP/1.1 403 Session has expired,datasnap不能重连功能,刚开始如果客户端正常连接中间层,然后中间层杀掉 ,客户端由于是用老的session去连接中间层,会被重新启动的中间层认为session过期。所以一直是返回raise,无限期不能重连。
解决方案,出现session错误时,把sessionid清空。
我发现问题跟tidhttp一毛钱关系没有,说明indy代码还是很不错的啊:)只是datasnap的代码未考虑异常。
解决如下:找到delphi源代码DSClientRest文件,复制到你的项目文件夹中,找到
procedure ExecuteCommand(ACommand: TDSRestCommand; const ARequestFilter, AAccept: string); overload;
把原先的1129行
ExecuteRequest(ACommand.Connection.HTTP, LURL, ACommand.RequestType, ACommand.Connection.LoginProperties.UserName, ACommand.Connection.LoginProperties.Password, ACommand.Parameters, ARequestFilter, AAccept,
LSessionID,
procedure
begin
ACommand.Connection.SessionExpired;
end,
procedure(ASessionID: string)
begin
if ASessionID <> '' then
ACommand.Connection.SessionID := ASessionID;
end);
改为:
try
ExecuteRequest(ACommand.Connection.HTTP, LURL, ACommand.RequestType, ACommand.Connection.LoginProperties.UserName, ACommand.Connection.LoginProperties.Password, ACommand.Parameters, ARequestFilter, AAccept,
LSessionID,
procedure
begin
ACommand.Connection.SessionExpired;
end,
procedure(ASessionID: string)
begin
if ASessionID <> '' then
ACommand.Connection.SessionID := ASessionID;
end);
except
on E:Exception do
begin
if Pos('Session has expired',E.Message)>0 then
begin
ACommand.Connection.SessionID:='';
end;
raise;
end;
end;
既可,这样,重连中间层可以实现了。
经过二个多月对于datasnap的一个项目学习,发现emb的人喜欢把程序员当做刚做程序的小白,所以代码很完善,让你很简单就建立了一个rest 服务(内部细节都由emb帮助你做好了)。但是他们处于实验室阶段,有时候没有考虑真实环境的差异,特别是一些异常未做处理。也就是说 有些坑需要你自己去填,但是填完后,发现datasnap还是不错的。:-)
http://bbs.2ccc.com/topic.asp?topicid=524327