全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:3716
推到 Plurk!
推到 Facebook!

关于Indy10中IdTcpServer与IdTcpClient断开连接的问题

答題得分者是:Hero
zhweizw
一般會員


發表:7
回覆:16
積分:9
註冊:2008-01-12

發送簡訊給我
#1 引用回覆 回覆 發表時間:2010-08-03 23:55:28 IP:117.87.xxx.xxx 訂閱
各位大大,小弟在使用Indy10中遇到了问题,此问题困扰我许久,寻遍互联网不得其解,请大大在百忙之中指点一下小弟,感激涕零!
IdTcpClient与IdTcpServer建立连接后,如果在Server端选择一条已连接的Client,执行DisConnect方法后,Client端不会触发OnDisConnected事件(但此时Client端的Connected属性是False,说明连接确实已经断开,但却没有触发OnDisconnected事件)。
例如:

[code delphi]
procedure TForm1.Btn1Click(Sender:TObject);
var
i:integer;
List:TList;
begin
List:=IdTcpServer1.Contexts.LockList;
try
for i:=0 to List.Count -1 do begin
if TIdServerContext(List.items[i]).binding.Peerip = '192.168.1.6' then begin
TIdServerContext(List.items[i]).Connection.Disconnect;
end;
end;
finally
IdTcpServer1.Contexts.UnLockList;
end;
end;
[/code]


执行上面的代码后,客户端'192.168.1.6' 链接断开(connected属性为FALSE),但没有触发OndisConnected事件。

相反,如果在Client端主动执行DisConnect方法,Server与Client端都能触发OnDisconnected事件。
由于IdTcpClient继承自TIdTcpConnection类,所以小弟查看了IdTcpConnection.pas单元的源码发现:Client端的OnDisconnected事件只在自己的DisConnect方法中触发,其他位置没发现有触发此事件的代码。所以造成了Server端断开连接而Client端不触发OnDisconnected事件的现象。
那么,如果在Server执行DisConnect 方法后,如何在Client端触发Ondisconnected事件呢,小弟想到2个方法:
1、在Client端用timer定时检测;
2、在Server端执行Disconnect方法前先通知Client。
但是上面2种方法用起来总感觉不爽,起码不够“专业”。
1、请问大大,还有什么更好的方法吗?
2、小弟不明白,IdTcpClient的OnDisconnected事件为什么只设计在自己的DisConnect方法中才触发呢?
3、是不是小弟理解错误了?如果是,请大大指点,万分感谢!!!

另外,小弟发现在TIdTCPConnection单元的TIdTCPConnection.Disconnect方法中执行了DisconnectNotifyPeer方法(519行),该方法在该单元的402行有说明:
// This is called when a protocol sends a command to tell the other side (typically client to
// server) that it is about to disconnect. The implementation should go here.
大概的意思是,当协议(Tcp/ip)发送一个命令通知对方关于断开连接时调用此方法,典型的应用时客户端发送到服务器端。
但是DisconnectNotifyPeer方法的实现部分却是空的,没有执行任何代码,并且在其子类中也未被重载或覆盖,难道需要我们自己override吗?

編輯記錄
zhweizw 重新編輯於 2010-08-03 23:58:15, 註解 無‧
zhweizw 重新編輯於 2010-08-04 00:04:45, 註解 無‧
zhweizw 重新編輯於 2010-08-04 00:07:05, 註解 無‧
zhweizw 重新編輯於 2010-08-04 00:10:36, 註解 無‧
zhweizw 重新編輯於 2010-08-04 10:36:50, 註解 無‧
zhweizw 重新編輯於 2010-08-04 10:38:17, 註解 無‧
careychen
尊榮會員


發表:41
回覆:580
積分:959
註冊:2004-03-03

發送簡訊給我
#2 引用回覆 回覆 發表時間:2010-08-10 11:14:59 IP:60.248.xxx.xxx 訂閱
就照你上面說的,用 Timer 的時間 Client 自行檢測,因為網路的環境並不一定是穩定的,單靠 Server 並不一定能絕對通知到 Client ,但 Client 可以一定時間內與 Server 做一個類似 Ping 的動作,超時沒有得到 Server 的回應,就該認定斷線,最好雙方都做這個功能,不是只有 Client 才需要
------
價值的展現,來自於你用哪一個角度來看待它!!
zhweizw
一般會員


發表:7
回覆:16
積分:9
註冊:2008-01-12

發送簡訊給我
#3 引用回覆 回覆 發表時間:2010-08-10 13:20:51 IP:58.218.xxx.xxx 訂閱
感谢Carey的解答,看来我只能使用你说的方法了。
另外,大大们有时间看一下IdConnection.pas源码,我发现TIdTcpClient.OndisConnected事件,仅仅在TIdTcpClient.Disconnect发放中触发,这样设计不合理呀?
是不是我没搞懂呢?
===================引 用 careychen 文 章===================
就照你上面說的,用 Timer 的時間 Client 自行檢測,因為網路的環境並不一定是穩定的,單靠 Server 並不一定能絕對通知到 Client ,但 Client 可以一定時間內與 Server 做一個類似 Ping 的動作,超時沒有得到 Server 的回應,就該認定斷線,最好雙方都做這個功能,不是只有 Client 才需要
Hero
一般會員


發表:3
回覆:10
積分:12
註冊:2002-07-11

發送簡訊給我
#4 引用回覆 回覆 發表時間:2010-08-10 17:12:09 IP:114.136.xxx.xxx 訂閱
Indy的client端元件都不是事件驅動的,所以要靠自己來,client這邊只有在讀、寫或是呼叫Connected()才會檢查連線
一般來說會用Thread來進行操作(實在搞不懂Indy都發展到10了,為什這段不一起寫呢?)
我自己寫過一個TCPClient的小程式來debug Server的,參考看看

[code cpp]

void __fastcall TReceiveThread::Execute()
{
UnicodeString Msg;
TIdBytes Buffer;
int Count;
bool NeedToConnect = true;


while (!Terminated) {
try {
if (!Enabled) {
WaitForSingleObject(FEnabledEvent, INFINITE);
continue;
}

if (FClient->Connected()) {
FClient->IOHandler->CheckForDataOnSource(100);
//先讀
if ((Count = FClient->IOHandler->InputBuffer->Size) > 0) {
FClient->IOHandler->ReadBytes(Buffer, Count, false);
if (FReceiveRingQueue)
FReceiveRingQueue->Push(&Buffer[0], Count);
}

//後寫
if ((Count = FExportRingQueue->Count) > 0) {
Buffer.set_length(Count);
FExportRingQueue->Pop(&Buffer[0], Count);
FClient->IOHandler->Write(Buffer, Count, 0);
}
}
else {
//當Server端主動斷開連線時,若剛好不是在讀取、寫入操作則不會引起Exception而跳入catch區段
//這樣會導致非預期自動重連
//加入NeedToConnect用來判斷是否需要連線,
//若不需要則跳入catch區段進行是否要自動重連與定時重連的檢查
if (NeedToConnect) {
NeedToConnect = false;
Msg = Format("Ready to connect to %s:%d...", ARRAYOFCONST((FHost, FPort)));
SendMessage(FHandle, JS_MSG, 0, (DWORD)&Msg);
if (!CompareText(FBoundIP, "Default"))
FClient->BoundIP = EmptyStr;
else
FClient->BoundIP = FBoundIP;
FClient->Host = FHost;
FClient->Port = FPort;
FClient->Connect();
}
else
throw Exception("");
}
}
catch (Exception &E) {
if (!E.Message.IsEmpty())
SendMessage(FHandle, JS_MSG, 0, (DWORD)&(E.Message));

WaitForSingleObject(FEvent, 1000);

//If you simply need to close a connection,
//you probably should call the close method in the IOHandler or Socket (when assigned).
//直接呼叫FClient->Disconnect()可能會發生例外
if (FClient->Socket)
FClient->Socket->Close();
//檢查是否連線有問題就自動斷線
if (FAutoDisconnect) {
FEnabled = false;
PostMessage(FHandle, JS_AUTODISCONNECT, 0, 0);
continue;
}
for (int i=FReconnectSecond; i>0; --i) {
if (Terminated || !Enabled) break;
Msg = Format("After %d seconds, automatically re-connect to %s:%d.", ARRAYOFCONST((i, FHost, FPort)));
SendMessage(FHandle, JS_MSG, 0, (DWORD)&Msg);
WaitForSingleObject(FEvent, 1000);
}
NeedToConnect = true;
}
}
}
[/code]
編輯記錄
Hero 重新編輯於 2010-08-10 17:20:18, 註解 無‧
Hero 重新編輯於 2010-08-10 17:21:48, 註解 無‧
Hero 重新編輯於 2010-08-10 17:27:31, 註解 無‧
Hero 重新編輯於 2010-08-11 13:27:08, 註解 無‧
Hero 重新編輯於 2010-08-13 18:22:42, 註解 無‧
Hero 重新編輯於 2010-08-20 09:15:34, 註解 無‧
系統時間:2017-12-12 8:29:53
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!