TIdTCPConnection.Disconnect 和 TIdIOHandler.Close 的区别
the Difference of TIdTCPConnection.Disconnect and TIdIOHandler.Close
我使用 Indy 10.6.2.5298。
TIdTCPConnection.Disconnect和TIdIOHandler.Close有什么区别?他们都断开了线路,但有时前者会出现访问冲突错误。
很抱歉,通过帮助文档及其源代码无法理解。
type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
procedure FormClick(Sender: TObject);
procedure IdTCPServer1Execute(AContext: TIdContext);
private
TestContext: TIdContext;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
TestContext.Connection.Disconnect; // access violation
TestContext.Connection.IOHandler.Close; // always works well
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
TestContext := AContext;
AContext.Connection.Disconnect; // works well
end;
TIdTCPConnection.Disconnect()
在内部调用 IOHandler.Close()
,如果分配了 IOHandler
并已打开(它还会调用 TIdTCPConnection.DisconnectNotifyPeer()
并触发 OnDisconnected
和 OnStatus
事件):
procedure TIdTCPConnection.Disconnect(ANotifyPeer: Boolean);
var
// under ARC, convert a weak reference to a strong reference before working with it
LIOHandler: TIdIOHandler;
begin
try
// Separately to avoid calling .Connected unless needed
if ANotifyPeer then begin
// TODO: do not call Connected() here if DisconnectNotifyPeer() is not
// overriden. Ideally, Connected() should be called by overridden
// DisconnectNotifyPeer() implementations if they really need it. But
// to avoid any breakages in third-party overrides, we could check here
// if DisconnectNotifyPeer() has been overridden and then call Connected()
// to maintain existing behavior...
//
try
if Connected then begin
DisconnectNotifyPeer;
end;
except
// TODO: maybe allow only EIdConnClosedGracefully and EIdSocketError?
end;
end;
finally
{
there are a few possible situations here:
1) we are still connected, then everything works as before,
status disconnecting, then disconnect, status disconnected
2) we are not connected, and this is just some "rogue" call to
disconnect(), then nothing happens
3) we are not connected, because ClosedGracefully, then
LConnected will be false, but the implicit call to
CheckForDisconnect (inside Connected) will call the events
}
// We dont check connected here - we realy dont care about actual socket state
// Here we just want to close the actual IOHandler. It is very possible for a
// socket to be disconnected but the IOHandler still open. In this case we only
// care of the IOHandler is still open.
//
// This is especially important if the socket has been disconnected with error, at this
// point we just want to ignore it and checking .Connected would trigger this. We
// just want to close. For some reason NS 7.1 (And only 7.1, not 7.0 or Mozilla) cause
// CONNABORTED. So its extra important we just disconnect without checking socket state.
LIOHandler := IOHandler;
if Assigned(LIOHandler) then begin
if LIOHandler.Opened then begin
DoStatus(hsDisconnecting);
LIOHandler.Close;
DoOnDisconnected;
DoStatus(hsDisconnected);
//LIOHandler.InputBuffer.Clear;
end;
end;
end;
end;
TIdIOHandler.Close()
简单地关闭套接字,如果一个已经分配:
procedure TIdIOHandlerSocket.Close;
begin
if FBinding <> nil then begin
FBinding.CloseSocket;
end;
inherited Close;
end;
procedure TIdIOHandler.Close;
//do not do FInputBuffer.Clear; here.
//it breaks reading when remote connection does a disconnect
var
// under ARC, convert a weak reference to a strong reference before working with it
LIntercept: TIdConnectionIntercept;
begin
try
LIntercept := Intercept;
if LIntercept <> nil then begin
LIntercept.Disconnect;
end;
finally
FOpened := False;
WriteBufferClear;
end;
end;
您的访问冲突错误的原因可能是因为您的测试代码一开始就不是线程安全的。 TIdTCPServer
是一个多线程组件。它的 OnConnect
、OnDisconnect
、OnExecute
和 OnException
事件在管理 TIdContext
对象的工作线程的上下文中触发。您的 OnClick
处理程序正在访问该线程外的 TIdContext
对象。一旦套接字关闭,TIdTCPServer
将检测到并停止线程,销毁 TIdContext
及其 TIdTCPConnection
和 TIdIOHandler
对象。由于线程计时和上下文切换,您的 OnClick
处理程序很可能在它们被销毁后继续访问这些对象。 OnExecute
处理程序内部没有这个问题,因为当线程为 运行.
时对象仍然有效
为了使您的 OnClick
代码与 TIdTCPServer
兼容,您需要锁定 TIdTCPServer.Contexts
列表,这样 TIdContext
对象就不会在 OnClick
还在尝试使用,eg:
procedure TForm1.FormClick(Sender: TObject);
var
List: TIdContextList;
begin
List := IdTCPServer1.Contexts.LockList;
try
//has the context already been removed?
if List.IndexOf(TestContext) <> -1 then
TestContext.Connection.Disconnect;
finally
IdTCPServer1.Contexts.UnlockList;
end;
end;
我使用 Indy 10.6.2.5298。
TIdTCPConnection.Disconnect和TIdIOHandler.Close有什么区别?他们都断开了线路,但有时前者会出现访问冲突错误。
很抱歉,通过帮助文档及其源代码无法理解。
type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
procedure FormClick(Sender: TObject);
procedure IdTCPServer1Execute(AContext: TIdContext);
private
TestContext: TIdContext;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
TestContext.Connection.Disconnect; // access violation
TestContext.Connection.IOHandler.Close; // always works well
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
TestContext := AContext;
AContext.Connection.Disconnect; // works well
end;
TIdTCPConnection.Disconnect()
在内部调用 IOHandler.Close()
,如果分配了 IOHandler
并已打开(它还会调用 TIdTCPConnection.DisconnectNotifyPeer()
并触发 OnDisconnected
和 OnStatus
事件):
procedure TIdTCPConnection.Disconnect(ANotifyPeer: Boolean);
var
// under ARC, convert a weak reference to a strong reference before working with it
LIOHandler: TIdIOHandler;
begin
try
// Separately to avoid calling .Connected unless needed
if ANotifyPeer then begin
// TODO: do not call Connected() here if DisconnectNotifyPeer() is not
// overriden. Ideally, Connected() should be called by overridden
// DisconnectNotifyPeer() implementations if they really need it. But
// to avoid any breakages in third-party overrides, we could check here
// if DisconnectNotifyPeer() has been overridden and then call Connected()
// to maintain existing behavior...
//
try
if Connected then begin
DisconnectNotifyPeer;
end;
except
// TODO: maybe allow only EIdConnClosedGracefully and EIdSocketError?
end;
end;
finally
{
there are a few possible situations here:
1) we are still connected, then everything works as before,
status disconnecting, then disconnect, status disconnected
2) we are not connected, and this is just some "rogue" call to
disconnect(), then nothing happens
3) we are not connected, because ClosedGracefully, then
LConnected will be false, but the implicit call to
CheckForDisconnect (inside Connected) will call the events
}
// We dont check connected here - we realy dont care about actual socket state
// Here we just want to close the actual IOHandler. It is very possible for a
// socket to be disconnected but the IOHandler still open. In this case we only
// care of the IOHandler is still open.
//
// This is especially important if the socket has been disconnected with error, at this
// point we just want to ignore it and checking .Connected would trigger this. We
// just want to close. For some reason NS 7.1 (And only 7.1, not 7.0 or Mozilla) cause
// CONNABORTED. So its extra important we just disconnect without checking socket state.
LIOHandler := IOHandler;
if Assigned(LIOHandler) then begin
if LIOHandler.Opened then begin
DoStatus(hsDisconnecting);
LIOHandler.Close;
DoOnDisconnected;
DoStatus(hsDisconnected);
//LIOHandler.InputBuffer.Clear;
end;
end;
end;
end;
TIdIOHandler.Close()
简单地关闭套接字,如果一个已经分配:
procedure TIdIOHandlerSocket.Close;
begin
if FBinding <> nil then begin
FBinding.CloseSocket;
end;
inherited Close;
end;
procedure TIdIOHandler.Close;
//do not do FInputBuffer.Clear; here.
//it breaks reading when remote connection does a disconnect
var
// under ARC, convert a weak reference to a strong reference before working with it
LIntercept: TIdConnectionIntercept;
begin
try
LIntercept := Intercept;
if LIntercept <> nil then begin
LIntercept.Disconnect;
end;
finally
FOpened := False;
WriteBufferClear;
end;
end;
您的访问冲突错误的原因可能是因为您的测试代码一开始就不是线程安全的。 TIdTCPServer
是一个多线程组件。它的 OnConnect
、OnDisconnect
、OnExecute
和 OnException
事件在管理 TIdContext
对象的工作线程的上下文中触发。您的 OnClick
处理程序正在访问该线程外的 TIdContext
对象。一旦套接字关闭,TIdTCPServer
将检测到并停止线程,销毁 TIdContext
及其 TIdTCPConnection
和 TIdIOHandler
对象。由于线程计时和上下文切换,您的 OnClick
处理程序很可能在它们被销毁后继续访问这些对象。 OnExecute
处理程序内部没有这个问题,因为当线程为 运行.
为了使您的 OnClick
代码与 TIdTCPServer
兼容,您需要锁定 TIdTCPServer.Contexts
列表,这样 TIdContext
对象就不会在 OnClick
还在尝试使用,eg:
procedure TForm1.FormClick(Sender: TObject);
var
List: TIdContextList;
begin
List := IdTCPServer1.Contexts.LockList;
try
//has the context already been removed?
if List.IndexOf(TestContext) <> -1 then
TestContext.Connection.Disconnect;
finally
IdTCPServer1.Contexts.UnlockList;
end;
end;