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