DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: lihhh
今日帖子: 0
在线用户: 6
导航: 论坛 -> 数据库专区 斑竹:liumazi,waterstone  
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-12 9:26:18
标题:
DataSnap的如果网络断线,如何恢复? 浏览:1588
加入我的收藏
楼主: 语言:Xe2 框架:DataSnap
问题:DataSnap很简单,把一个二层的程序改写成三层,很简单就实现了,例如:把原先的二层访问函数,堆到TServerMethods1单元即可,然后就实现了三层。但是在测试时有个问题,生产环境可能非常恶劣,经常回发生断线情况,我们要保证可以自动恢复功能,即断线一段时间,可以修复,二层很简单,直接做一个timer,如果访问失败,就定时去连接,可以成功恢复。但是在datasnap时,自动恢复功能无法实现,主要表现为,比如正常访问中间层一次后,关闭中间层,此时客户端访问失败,跟踪后用客户端timer重新连接访问中间层,每次
出现以下各种不同错误(有10054,10038,10061错误),并不能实现恢复。


 raised exception class EIdNotASocket with message 'Socket Error # 10038
Socket operation on non-socket.'.

----------
Debugger Exception Notification
----------
Project PrjV.exe raised exception class EIdCouldNotBindSocket with message 'Could not bind socket. Address and port are already in use.'.
----------
Break   Continue   Help   
----------




----------
Debugger Exception Notification
----------
Project PrjV.exe raised exception class EIdClosedSocket with message 'Disconnected.'.
----------
Break   Continue   Help   
----------


----------
Debugger Exception Notification
----------
Project PrjV.exe raised exception class EIdSocketError with message 'Socket Error # 10022
Invalid argument.'.
----------
Break   Continue   Help   
----------

尤其是有2个错误,Session has expired,最后会出现一个index of bound越界错误,
一旦出现就无法自动恢复(调用timer重复调用一个最简单的中间函数GetDbServerTime)

ps:我也改了lifecycle,session 改成Invocation,Application,好像也不行。

大家都知道datasnap底层是indy实现的,怎么可以实现断线datasnap自动恢复功能?
----------------------------------------------
ddddd
作者:
男 zhangpuqing (pupu) ▲▲▲▲△ -
注册会员
2017-1-12 9:32:38
1楼: 哈哈,这个问题很简单,非常容易解决.
之前我也遇到,终端运行1整天,中间偶尔掉个包就不能再访问了,非要把终端再重启才行.
用定时器是很落后的方法,很费系统资源的.
我只需要加1句话就解决了.而且这样一改后服务端即使偶尔DOWN掉了只要再重开服务端就行。
----------------------------------------------
-
作者:
男 chengcti (馒头) ▲▲▲▲▲ -
普通会员
2017-1-12 9:36:19
2楼: 哪句話?

"請給我重開?"
----------------------------------------------
-
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-12 9:37:42
2楼: 如果成功恢复,timer是不再调用的,你说的终端是客户端吗?
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-12 9:40:32
3楼: 客户端 192.168.1.100 中间层 192.168.1.200 数据库 192.168.1.160,如果各种断线情况,比如中间层断线,数据库断线,这些情况都要处理的,因为中间层可能挂几十台机器,所以中间层重新启动,是无法接受的。
----------------------------------------------
ddddd
作者:
男 vmao (毛小毛) ★☆☆☆☆ -
盒子活跃会员
2017-1-12 9:46:13
4楼: 记得以前有人问过这个问题的,你搜索一下“心跳包”这个关键词看看。
----------------------------------------------
-
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-12 9:51:31
4楼: 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自动生成的客户端访问中间层的方法
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-12 9:56:25
5楼: 谢谢,我看下心跳包,有哪位大佬对indy,datasnap比较熟悉的,请指教
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-12 10:55:23
6楼: 网上找到三种方法:
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

我每个测试一下,到时候结果通知大家。
----------------------------------------------
ddddd
作者:
男 zhangpuqing (pupu) ▲▲▲▲△ -
注册会员
2017-1-12 11:36:31
7楼: 中间层DOWN掉是正常的,比如服务器死机了,访问量大了,肯定有这种情况。
中间层DOWN后,终端只是访问不了了,不用重启。只有服务端重启就OK,终端能自动连。
----------------------------------------------
-
作者:
男 aizaixiiii (天黑请闭眼) ▲▲▲▲▲ -
普通会员
2017-1-12 11:42:13
8楼: 加哪一句?
----------------------------------------------
-烟是情人,酒是兄弟。
作者:
男 chonghai (DBlue) ▲▲▲▲▲ -
盒子活跃会员
2017-1-13 0:01:17
9楼: 使用过心跳包技术,主要还是服务器端清除断开的连接。
客户端自动重连似乎不行。
----------------------------------------------
喜欢Delphi,关注Delphi,愿和广大爱好者交朋友。
作者:
男 zhangpuqing (pupu) ▲▲▲▲△ -
注册会员
2017-1-13 10:06:45
10楼: 再等2天再公布答案,等等人气.
----------------------------------------------
-
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-13 10:37:10
11楼: 我来说下这一天的成果:
因为自己很少用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的中间层请求,保证异常后,不能同时再被访问,这样就可以了。
----------------------------------------------
ddddd
作者:
男 wang_80919 (Flying Wang) ▲▲▲▲△ -
普通会员
2017-1-13 10:41:09
12楼: indy 在tcp udp 方面还是很有用的。
http 方面 EMB 新版本都去掉 INDY 了。换成 操作系统级别的 THttpClient 了。
http 好像也支持 一直连接状态,但是很少有人用这个状态。
所以不知道 http 是不是支持 心跳包。

其实 indy 的 http 也是不错的。
bug 多,那是 d7 的时代。
人家 indy 也一直在进化的。
现在的 indy 支持 ipv6 等 新的特性的。
只是使用方法比较复杂而又。
THttpClient 是 操作系统负责的,使用比较简便。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-1-13 15:12:30
13楼: 解决如果出现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;


既可,这样,重连中间层可以实现了。
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-1 14:01:19
14楼: 今天把程序放到客户那里运行了,说是现场运行20分钟后,连接出现HTTP/1.1 403 Session has expired,经过google查询文档,把一个参数 sessiontimeout 设置为0,自己测试1个半小时后,没有问题,正等待现场的反馈。ps:我的程序lifecycle是session
----------------------------------------------
ddddd
作者:
男 lsz100 (lsz) ▲▲▲▲▲ -
盒子活跃会员
2017-3-1 17:35:48
15楼: 你是用IDHTTP 实现C/S数据传输 这样的话你服务端为何要开SESSION 关了就是了
----------------------------------------------
我为人人为我
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-1 18:45:28
16楼: 我用的http,没有用tcp,用的datasnap的rest服务。
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-1 18:46:50
17楼: datasnap里面实现了session 的管理,实现rest服务,idhttp仅仅是访问完毕就关闭,就是http访问
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-2 10:51:42
18楼: 汇报一下:经过现场14台客户端,一个中间层,运行实施,表现不错,现在20分钟session自动过期,客户端20分钟后访问中间层的rest服务出现raise应当解决了。
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-2 10:56:18
19楼: 经过二个多月对于datasnap的一个项目学习,发现emb的人喜欢把程序员当做刚做程序的小白,所以代码很完善,让你很简单就建立了一个rest 服务(内部细节都由emb帮助你做好了)。但是他们处于实验室阶段,有时候没有考虑真实环境的差异,特别是一些异常未做处理。也就是说 有些坑需要你自己去填,但是填完后,发现datasnap还是不错的。:-)
----------------------------------------------
ddddd
作者:
男 jjwwang (jjwwang) ▲▲▲▲▲ -
注册会员
2017-3-2 19:56:45
20楼: 给楼主点个赞!
----------------------------------------------
学无止境
作者:
男 zyp1984 (小李他妈的飞刀) ▲▲▲▲▲ -
普通会员
2017-3-3 10:00:26
21楼: 希望楼主多分享经验吧.
----------------------------------------------
山外青山楼外楼,能人背后有能人弄..
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-22 14:11:35
22楼: 继续datasnap继续研究,看了一本书,李维老师的,如何让datasnap的rest服务,支持上千个客户端连接呢?
 找到delphi的源文件Web.WebReq,拷贝到你的程序目录下,找到

constructor TWebRequestHandler.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FCriticalSection := TCriticalSection.Create;
  FActiveWebModules := TList.Create;
  FInactiveWebModules := TList.Create;
  FWebModuleFactories := TWebModuleFactoryList.Create;
  FMaxConnections := 32;
  FCacheConnections := True;
end;

把  FMaxConnections := 32; 改为 FMaxConnections := 0;

就可以支持上千个客户端连接了,原来官方认为我们同时支持的客户端不超过32个,所以很多人认为datasnap只能在企业运用 :(
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-22 14:26:00
23楼: 发现一个datasnap的大臭虫(bug),居然客户端提交的数据在服务端接收到的参数会变化。

如果你用了xe2,可能xe3,xe4也有(其他没有测试,但是D10.1.2 没有此问题),rest 服务端,同时客户端 使用datasnap rest  client module 生成的,也就是说你用了TDSRestConnection 这个类时,如果你在你的示例方法 ,reverstring 填写数据 80895E46 ,你会发现  数据被篡改了。
当然用 datasnap client module生成的客户端没有此问题,也就是你的客户端是类是TSQLConnection ,没有问题。

解决办法:找到官方源码文件Datasnap.DSService,复制到你的项目文件夹下
找到BuildParamArray 这个方法,把方法最后3行代码
         if TDBXPlatform.TryJsonToFloat(S, LValue) then
          ParamArray.AddElement(TJSONNumber.Create(S))
         else
          ParamArray.AddElement(TJSONString.Create(S));

改为: ParamArray.AddElement(TJSONString.Create(S));
即可。
----------------------------------------------
ddddd
作者:
男 wuxiangyang (wxy) ▲▲▲▲▲ -
盒子活跃会员
2017-3-22 14:57:07
24楼: 楼主的学习探索精神,值得敬佩,感谢分享!
----------------------------------------------
-
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-22 15:42:23
25楼: 吐槽一下,用过remobject的向导生成函数,也用过rtc的function, kbmkw的clientquery, 感觉还是datasnap的最简单,想要什么函数,只要在TServerMethods1 下写个public函数,就解决问题了。
----------------------------------------------
ddddd
作者:
男 142857 (142857) ▲▲▲▲▲ -
盒子活跃会员
2017-3-22 15:46:40
26楼: 跟踪http包,发现datasnap client module生成的客户端,向datasnap服务时的包有2个包,一个put,一个get包,如下示例:
PUT /datasnap/tunnel?dss=305442.156876.620565&c=71 HTTP/1.1
Host: localhost:8080
Accept: text/html, */*
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)
Content-Length: 71

{"method":"execute","params":[{"handle":[1]},{"data":[10,(80895E46繻}]}



GET /datasnap/tunnel?dss=305442.156876.620565&c=32768 HTTP/1.1
Host: localhost:8080
Accept: text/html, */*
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)

而用TDSRestConnection 发送的http包只有一个,标准rest服务包,

GET /datasnap/rest/TServerMethods1/ReverseString/80895E46/ HTTP/1.1
Connection: Keep-Alive
Pragma: dssession=21020.273621.139287
Content-Type: text/plain;charset=UTF-8
Accept: application/JSON
If-Modified-Since: Mon, 1 Oct 1990 05:00:00 GMT
User-Agent: Embarcadero URI Client/1.0
Host: 192.168.1.187:8089


所以如果可能,尽量用TDSRestConnection,不用TSQLConnection ,可以减少网络传输。
----------------------------------------------
ddddd
作者:
男 foryour (sean) ▲▲▲▲△ -
普通会员
2017-3-22 16:13:28
27楼: mark
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行83.98438毫秒 RSS