經典的判斷資料庫連接斷開問題,翻遍了許多地方沒有答案。 Delphi / Windows SDK/API
http://www.delphi2007.net/DelphiDB/html/delphi_20061215202145283.html
程式正常串連狀態下,伺服器重啟、或網路斷開等原因將導致資料庫連接非正常斷開,這時ADOConnection1.Connected、ADOConnection1.State都顯示是串連狀態(分別為True和stOpen),但其實串連已經斷開,伺服器、網路正常後一樣,目前唯一的辦法是關閉並重開程式,這顯然不人性化。
每次查詢資料不經判斷串連狀態都來一次重新串連資料庫明顯不現實。
請問,如何判斷AdoConnection是否失去了串連?好讓我們重新串連資料庫:
ADOConnection1.Connected := False;
ADOConnection1.Connected := True;
try
ADOConnection1.Connected := False;
ADOConnection1.Connected := True;
except
有問題
end;
每次查詢資料不經判斷串連狀態都來一次重新串連資料庫明顯不現實。
樓上這樣不就是每次都重新串連資料庫嗎。
重點在於如何“判斷”ADOConnection1.Connected = True但AdoConnection其實已經失去了串連?
用timer 刷,
動態建立 TADOConection ,設定 ConnectionString ,然後 Connected := True;
異常判斷就可以!
發分吧!!!
function ConnectAppServer: boolean; //判斷是否串連上了應用伺服器
begin
Result := True;
ADOConn.Connected :=false;
try //測試連接
ADOConn.Connected := true;
except //未串連上
Result := False;
end;
end;
調用時:
if ConnectAppServer then
begin
進行處理功能;
end
else
begin
showmessage('對不起!資料庫失去串連');
進行失敗處理;
end;
在查詢的時候用
try
except
重連
end;
樓上幾位都是每次都要重新串連資料庫才能判斷出來,這樣做不理想,起碼響應速度慢。
就怕你們這麼答,我才事先聲明了,可能你們急著要分漏了看題?:(
如能解決好這個問題,我再加200分。
需要時串連,平時斷開
你即時去判斷他斷沒有斷開,是會耗費大量的網路資源的.
ADOConnection1BeforeDisconnect事件
或者
ADOConnection1AfterDisconnect事件
或者
ADOConnection1Disconnect事件來判斷出發
樓上的有創意,學習了
我怎麼感覺事件一點用都沒有!?
你可以判斷資料庫的連接埠是否開放,如果返回false說明通訊有中斷...
可以先執行操作,如果出現操作異常時,判斷是否是因為資料庫連接失敗引起的,再重新執行你所要執行的操作了。
這種問題根本就沒有討論的價值
1、如果網路真的斷開了,這個時候不管你的程式判斷結果是什麼,你都得關閉程式
2、如果沒有斷開,系統會自動嘗試去串連的
如果每次都判斷,那會造成系統很大的負擔。
一般是選 去 “幹事” 要是出錯了,查一個出錯類型才會知是網路斷了
當然某些用戶端很久用一次的,但又需要顯示串連狀態的,做個心跳查詢定時檢測下串連也是有必要的,只不過時間設長一點。。。
Hank(星星農場)
----
這種問題根本就沒有討論的價值
1、如果網路真的斷開了,這個時候不管你的程式判斷結果是什麼,你都得關閉程式
2、如果沒有斷開,系統會自動嘗試去串連的
如果每次都判斷,那會造成系統很大的負擔。
----
Hank(星星農場)的回答還有些許切題,不過也是沒心思認真看題。
1、網路徹底斷開了,還用問你幹嘛,如來佛祖都沒辦法。
我寫的是“伺服器、網路正常後一樣”,ADOConnection1顯示是串連狀態,但其實串連已經斷開。
2、系統會自動嘗試去串連的,系統如何去自動嘗試?
3、如果每次都判斷,那會造成系統很大的負擔。的確是這麼回事。
麻煩各位都認真看下題。
我認為 Hank(星星農場) 的觀點是值得參考的
判斷網路聯結是否正常要看實際的應用程式中是否需要,而且如果用timer等來斷開聯結是非常耗費資源的.
我有個想法,假設我們認為聯結是正常的,每次從資料庫系統資料表中查詢:
在sqlserver中可以select sysdate,
在oracle中可以 select sysdate from dual
如果不出錯,則我們就認為是正常的,如果出錯,剛關閉聯結,再聯結,你們覺得如何
說明,我已經仔細看過題目了,也知道你的意思
1、系統啟動並執行好好的,伺服器重起,這個時候用戶端是沒有提示的,但是當用戶端進行資料庫相關操作時系統會提示斷開,你想這個時候能給客戶一個提示是沒有意義的,因為在實際過程中如果重起要麼管理員提前通知要麼電話去問。
2、重起完後如果進行系統資料操作,系統會自己嘗試去串連的,這不是自動連接嗎?
3、如果真的要判斷,那隻有單獨建立一個ADOConnection去嘗試串連資料庫,這樣就太羅嗦了
4、如果你的應用是那種如果伺服器連不上資料暫時儲存在本地的玩法最好通過交易處理去做
前幾天的系統中也出現了這樣的需求.
試了一些辦法:
...............
1.用你的串連執行一個簡單的查詢操作,如select getdate()看有無傳回值.
2.採用多線程的方式,用一個ADOConnection專門來判斷網路.並給系統一個全域變數來記錄網路狀態,在操作時只需判斷變數狀態值.
3.採用Indy方式,客戶機和伺服器分別放一個.客戶機每發一個訊息出去後,若伺服器接收到則馬上返回一個值.如客戶機發一個"1+1"的訊息,伺服器馬上返回"2".當然裡邊要加一些你自己的約定格式串,不然客戶機多了分不清.在指定的時間內沒接收到傳回值的話說明網路已經出現故障.
基本上所有的系統都會面臨這種問題的,我的解決辦法是:
定義一個Timer,週期性執行一個最簡單的查詢操作,比如Select SYSDATE from DUAL 根據結果判斷是否斷線,然後ReConnect
樓主你要知道,你又要判斷是否串連,又不允許人家重新整理檢測,我覺得是不對的.
我估計你是怕重新整理檢測時時間太長,那麼,你可以單獨設定一個adoConnection,把它的timeout設定短一點,然後用一個資料集查詢如: select 1 as testnum
如果出錯就說明串連可能有問題了.
你如果想靠檢測網路是否串連來判斷資料庫連接是否正常的話,那是不行的.
你可以試一下:把網路拔了,馬上又插上.1秒鐘絕對能搞定,但資料庫卻斷開了串連.
感覺沒有好的辦法.
因為網路斷開也不是一個就能確定的.就像樓上所說的那樣. windows系統也不能一下就確定網路連接不上. 還需要判斷一會的時間, 就像本地串連那樣. 把網線撥了, 也不是立即就顯示斷開了. 還要在那動一會就提示 " 網路連接失敗 "
請大家注意我的重點:
如何不通過查詢來判斷AdoConnection是否失去了串連,通過狀態屬性什麼的。
我的一個想法,不知道可不可啊?
一般我們都是用
Try
except
end
來捕捉異常的。
自訂一個異常來顯示不能與資料庫連接???
每當有這個異常的時間,就自動重新串連資料庫就可以了??
這樣就不用重新整理判斷了??只有出現這個串連不上資料庫的時候才會重串連了
GoldShield(李柏岑) ( ) 信譽:106 Blog 2006-12-22 13:34:48 得分: 0
前幾天的系統中也出現了這樣的需求.
試了一些辦法:
...............
1.用你的串連執行一個簡單的查詢操作,如select getdate()看有無傳回值.
2.採用多線程的方式,用一個ADOConnection專門來判斷網路.並給系統一個全域變數來記錄網路狀態,在操作時只需判斷變數狀態值.
3.採用Indy方式,客戶機和伺服器分別放一個.客戶機每發一個訊息出去後,若伺服器接收到則馬上返回一個值.如客戶機發一個"1+1"的訊息,伺服器馬上返回"2".當然裡邊要加一些你自己的約定格式串,不然客戶機多了分不清.在指定的時間內沒接收到傳回值的話說明網路已經出現故障.
支援
如果你想快可以用 IDIcmpClient為判斷,直接用Adoconnection有個回應時間,比較長了點
function conip_only: Boolean;
begin
result := false;
IdIcmpClient1.Host:='192.168.0.2';
try
IdIcmpClient1.Ping;
result := true;
except
on e:exception do
showmessage(e.Message+'網路通訊中斷.');
end;
end;
procedure TdtmConnect.tiKeepConnectTimer(Sender: TObject);
begin
try
conMain.Execute('Select @@CONNECTIONS');
//MsgBox('伺服器正常串連中...');
except
tiKeepConnect.Enabled := False; //停止時鐘走動
ErrBox('網路斷開,與伺服器的串連已經中斷。');
if not Assigned(frmConnect) then
frmConnect := TfrmConnect.Create(self);
try
frmConnect.ShowModal;
finally
FreeAndNil(frmConnect);
end;
end;
end;
什麼情況下使用呢?
通常,網路斷開的話,系統會出錯的,這時候就需要檢查網路了
TAdoConnect組件有多個事件可用於判斷
我將程式中所有用到查詢、執行SQL、執行預存程序的地方封裝成幾個函數,然後在這幾個函數執行失敗的時候調用如下函數:
//再次聯結
function TDataModuleForm.ReConnect(Connection: TADOConnection):boolean;
var i,j:integer; FBreak,FErr:boolean;
label lbl_try;
begin
result:=false;
FBreak:=false;
FErr:=false;
for i:=0 to connection.Errors.Count-1 do
begin
if FBreak then Break;
if connection.Errors[i].Number=-2147467259 then
begin
FBreak:=true;
connection.Errors.Clear;
if messagedlg('系統檢測到資料庫聯結已斷開,需要系統嘗試重新聯結資料庫嗎?',mtinformation,[mbOk,mbCancel],0)=1 then
begin
lbl_try:
connection.Close;
FErr:=false;
j:=connection.ConnectionTimeout;
connection.ConnectionTimeout:=5;
screen.Cursor:=-11;
application.ProcessMessages;
try
try
connection.Connected:=true;
except
FErr:=True;
end;
finally
connection.ConnectionTimeout:=j;
screen.Cursor:=0;
connection.Errors.Clear;
end;
if FErr and (Messagedlg('聯結失敗,再次嘗試聯結嗎?',mtinformation,[mbOk,mbCancel],0)=1) then goto lbl_try;
end;
end;
end;
result:=not FErr;
end;
try
except
判斷是否是中斷連線的異常
end;
kaper()與zczb(zczb)的解答比較有建設性,謝謝。
嗨,這個真的沒必要搞得那麼複雜,樓主這樣才比較耗資源啊
這樣啦:還是用timer1
多放個 adodataset1,
在timer1.ontimer裡寫:
try
adodataset1.close;
adodataset1.commandtext:='select getdate()';
adodataset1.open;
expect
showmessage('串連斷開');
end;
我不喜歡做自動連接。因為例如日誌寫滿。或者其它原因。你這樣的動作,會導致系統長時間處於檢查資料庫的串連中,但事實上卻是暫時連不上來的,務必會導致死迴圈! 我想樓主的意在尋找能夠檢測到資料中斷連線應該也是為了能夠自動連接吧,退一步說,軟體的使用出現串連不上資料庫的時候。無非幾個原因。1,網路 2,資料庫伺服器,3,資料庫軟體。4。程式本身。 5。其它 這些原因都會迫使你停下來調整。沒有檢測和重新串連的必要! 個人愚見,
樓主想複雜了.
try
......
.....
.....
except
adoconnection.close;// 為下一次串連作準備.
end
還是未搞懂我的意思。
我們這裡的軟體遇到這個問題,解決的辦法是,每次用資料庫的時候都把connection和query開啟一次,同時在介面上留有panel來提示網路斷開還是資料庫斷開(下面兩個函數是我們用來判斷網路斷開還是資料庫斷開的函數):
function CheckNetConnection(IP: string): Boolean;
var
Echo: TIdEcho;
begin
Echo := TIdEcho.Create(nil);
with Echo do
begin
try
Host := IP;
Connect(1000);
Result := True;
except
Result := False;
end;
end;
Echo.Free;
end;
function CheckDBConnection(IP: string): Boolean;
var
Telnet: TIdTelnet;
begin
Telnet := TIdTelnet.Create(nil);
try
with Telnet do
begin
if Connected then Disconnect;
Host := IP;
Port := 1433;//資料庫服務用的連接埠號碼(Sql server使用這個連接埠號碼)
try
Connect(1000);
Result := True;
except //否則重連
Result := False;
end;
end;
finally
Telnet.Disconnect;
Telnet.Free;
end;
end;
上面兩個函數在Timer中周期性檢查,我們的timer時間間隔為10秒,顯示稍微之後,不過客戶能夠容忍,另外斷開網路的時候檢索資料庫還是偶爾卡一點,不過無需重起軟體,只要處理網路和資料庫就行了
每次有人找我問起樓主那個經典問題,我都是同樣的回答..
可幾乎每次問問題的人總以為我在開玩笑.
當資料庫連接斷開時,這時候需要做什麼呢?是試圖重連?不斷檢查串連狀態?
NO! 不要這樣做!
資料庫為什麼斷開?多半網路問題或者資料庫當機或重啟.事實上,這時候試圖程式自動重連,
毫無意義!這樣做反而會為負擔很重的網路更沒有恢複的機會.
這時候該做什麼? 是的,釋放連線物件,很多封裝底層串連的組件,都會暫存串連狀態,
當底層實際串連失敗,封裝對象的狀態卻是串連正常,這時候,串連不可能得到恢複.
怎麼讓封裝對象與底層串連狀態一致? 很簡單,讓連線物件CLOSE!
怎麼知道串連失敗? 在對資料庫操作時! 對於VCL,組件會扔出資料庫類型的異常,要做的,就是抓住它.對於使用資料感知控制項,在Application.OnException事件中也可以得到它.
在處理這樣異常時,就按剛才說的那樣,"讓連線物件CLOSE"!
對於狀態為CLOSE的連線物件,不需要再操心什麼.在使用VCL封裝Ado時,當連線物件AdoConnection狀態為關閉時,任何一個資料庫的操作,它都會自動去串連.這是預設的屬性設計.
同意樓上,也許我們考慮的樓上的意見以後可以解決我們軟體僅有的一頓的問題,那樣提示\重連\而且還不頓,就比較好了
TDataForm = class(TDataModule)
//...
procedure DataModuleCreate(Sender: TObject);
private
FOldApplicationOnException : TExceptionEvent;
procedure HandleException(Sender: TObject; E: Exception);
...
end.
procedure TDataForm.DataModuleCreate(Sender: TObject);
begin
...
FOldApplicationOnException := Application.OnException;
Application.OnException := HandleException;
end;
procedure TDataForm.HandleException(Sender: TObject; E: Exception);
begin
if E.ClassNameIs('EOleException') then
if ShowMsg(Screen.ActiveForm, format(
'資料庫連接被意外中止,要重新串連嗎?'#10#13+
'錯誤原因:%s',
[E.Message])) = IDYES then
begin
MainConn.Connected := False;
MainConn.Connected := True;
Exit;
end;
if Assigned(FOldApplicationOnException) then
FOldApplicationOnException(Sender, E)
end;
mark
我也遇到了類似的問題, 大家有時間看看
http://community.csdn.net/Expert/topic/5460/5460676.xml?temp=7.497805E-02