连接正常关闭 Indy TCPServer Mobile App Delphi XE8
Connection Closed Gracefully Indy TCPServer Mobile App Delphi XE8
我正在使用 Indy TCPClient/TCPServer 来验证移动设备的注册。这个过程相当简单,我在服务器端读取一个标识符,根据数据库控制文件对其进行验证,然后将响应发送回客户端。
大部分情况下一切似乎都正常工作,但我会定期在服务器端收到 EIDConnClosedGracefully 异常。我似乎无法准确指出连接未正确关闭的位置。事实上,服务器似乎在不应该的时候(连接关闭后)执行了 readln,我不知道为什么。可能我没有正确同步。我在 Tools/Options/Debugger 选项中将 Indy Silent Exceptions 设置为忽略,但我想知道是什么引发了异常。我可以执行注册函数4、5次就抛出异常但是很不一致。
如有任何建议,我们将不胜感激。
以下是我的代码:
服务器
try
MIRec.RecType := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I')
or (MIRec.RecType = 'R') then
begin
// Verify the connecting device is registered
MIRec.Identifier := AContext.Connection.IOHandler.ReadLn;
qryMobileDevice.Close;
qryMobileDevice.Parameters.ParamByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.Open;
AContext.Connection.IOHandler.WriteLn(qryMobileDevice.FindField('ACTIVE').AsString);
MIRec.DeviceName := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') then
LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
else
begin
// Register the Device in STIKS
if qryMobileDevice.EOF then
begin
LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
// If Record Does not exist Add to the Control File;
NextId := GetNextId('NEXT_MOBILE_DEVICE_ID');
qryMobileDevice.Insert;
qryMobileDevice.FieldByName('MOBILE_DEVICE_ID').Value := NextId;
qryMobileDevice.FieldByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.FieldByName('DESCRIPTION').Value := MiRec.Text;
qryMobileDevice.FieldByName('ACTIVE').Value := 'T';
qryMobileDevice.FieldByName('OPERATOR_SAVED').Value := 'From App';
qryMobileDevice.FieldByName('DATE_SAVED').Value := Now;
qryMobileDevice.Post;
end
else
begin
// Device has been Flagged and registration refused.
if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
LogEntry := '** Registration Successful: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now)
else
LogEntry := '** Registration Refused: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
end;
qryMobileDevice.Close;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(LogEntry);
end);
end;
except
on e: exception do
begin
Memo1.Lines.Add('** An error occurred Receiving File ' + #13#10 + 'With a message: ' + E.Message);
end;
end;
客户
if MessageDlg('Register Device With Server?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbNo, TMsgDlgBtn.mbYes], 0) = mrNo then Exit;
try
IdTCPClient1.Connect;
try
MainForm.IdTCPClient1.IOHandler.WriteLn('R'); // Tell Server we are sending a Registration Record
Device := TUIDevice.Wrap(TUIDevice.OCClass.currentDevice);
IdTCPClient1.IOHandler.WriteLn(NSStrToStr(Device.identifierForVendor.UUIDString));
Registered := IdTCPClient1.IOHandler.ReadLn; // Get response from server
Authenticated := (Registered = 'T');
IdTCPClient1.IOHandler.WriteLn(NSStrToStr(Device.Name));
finally
IdTCPClient1.DisConnect;
if Registered <> 'T' then
MessageDlg('Registration Failed!', TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0)
else
begin
Authenticated := True;
MessageDlg('Registration Has Completed Successfully!', TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0);
end;
end;
except
on e: exception do
begin
MessageDlg('** An error occurred Registering Device ' + #13#10 + 'With a message: ' + E.Message, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0);
end;
end;
我的日志输出。
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:36:54 AM
A Client Disconnected
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:37:00 AM
A Client Disconnected
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:37:04 AM
** An error occurred Receiving File
With a message: Connection Closed Gracefully.
A Client Disconnected
服务器代码是否在OnConnect
或OnExecute
事件中?
假设 OnExecute
,这是一个 looped 事件,它会在连接的生命周期内不断循环。在每次迭代中,您将再次调用 ReadLn
以从客户端读取下一个命令。如果客户端断开连接,则必须返回套接字以获取更多数据的下一次读取(在 IOHandler.InputBuffer
已用完之后)将相应地引发异常。这是正常行为,也是 Indy 设计的运作方式。
真正的问题是你有一个异常处理程序,它无条件地将所有异常记录为错误,甚至是优雅的断开连接。并且你的异常处理程序在将错误消息添加到你的备忘录时没有与 UI 线程同步,并且它没有重新引发任何捕获的 Indy 异常所以 TIdTCPServer
可以根据需要处理它(例如停止 OnExecute
循环并触发 OnDisconnect
事件)。
尝试更像这样的东西:
// if registration is only done once, this code should
// be in the OnConnect event instead...
procedure TMyForm.MyTCPServerExecute(AContext: TIdContext);
begin
try
MIRec.RecType := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') or
(MIRec.RecType = 'R') then
begin
// Verify the connecting device is registered
MIRec.Identifier := AContext.Connection.IOHandler.ReadLn;
qryMobileDevice.Close;
qryMobileDevice.Parameters.ParamByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.Open;
AContext.Connection.IOHandler.WriteLn(qryMobileDevice.FindField('ACTIVE').AsString);
MIRec.DeviceName := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') then
LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
else
begin
// Register the Device in STIKS
if qryMobileDevice.EOF then
begin
LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
// If Record Does not exist Add to the Control File;
NextId := GetNextId('NEXT_MOBILE_DEVICE_ID');
qryMobileDevice.Insert;
qryMobileDevice.FieldByName('MOBILE_DEVICE_ID').Value := NextId;
qryMobileDevice.FieldByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.FieldByName('DESCRIPTION').Value := MiRec.Text;
qryMobileDevice.FieldByName('ACTIVE').Value := 'T';
qryMobileDevice.FieldByName('OPERATOR_SAVED').Value := 'From App';
qryMobileDevice.FieldByName('DATE_SAVED').Value := Now;
qryMobileDevice.Post;
end
else
begin
// Device has been Flagged and registration refused.
if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
LogEntry := '** Registration Successful: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now)
else
LogEntry := '** Registration Refused: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
end;
qryMobileDevice.Close;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(LogEntry);
end
);
end;
except
on E: Exception do
begin
if not (E is EIdSilentException) then
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('** An error occurred Receiving File ' + #13#10 + 'With a message: ' + E.Message);
end
);
end;
// optionally remove the below 'if' to close the
// connection on any exception, including DB errors, etc...
if E is EIdException then
raise;
end;
end;
end;
另一方面,我建议完全删除 try/except
并改用服务器的 OnException
事件。让任何异常关闭连接,然后在最后记录原因:
procedure TMyForm.MyTCPServerExecute(AContext: TIdContext);
begin
MIRec.RecType := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') or
(MIRec.RecType = 'R') then
begin
// Verify the connecting device is registered
MIRec.Identifier := AContext.Connection.IOHandler.ReadLn;
qryMobileDevice.Close;
qryMobileDevice.Parameters.ParamByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.Open;
AContext.Connection.IOHandler.WriteLn(qryMobileDevice.FindField('ACTIVE').AsString);
MIRec.DeviceName := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') then
LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
else
begin
// Register the Device in STIKS
if qryMobileDevice.EOF then
begin
LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
// If Record Does not exist Add to the Control File;
NextId := GetNextId('NEXT_MOBILE_DEVICE_ID');
qryMobileDevice.Insert;
qryMobileDevice.FieldByName('MOBILE_DEVICE_ID').Value := NextId;
qryMobileDevice.FieldByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.FieldByName('DESCRIPTION').Value := MiRec.Text;
qryMobileDevice.FieldByName('ACTIVE').Value := 'T';
qryMobileDevice.FieldByName('OPERATOR_SAVED').Value := 'From App';
qryMobileDevice.FieldByName('DATE_SAVED').Value := Now;
qryMobileDevice.Post;
end
else
begin
// Device has been Flagged and registration refused.
if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
LogEntry := '** Registration Successful: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now)
else
LogEntry := '** Registration Refused: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
end;
qryMobileDevice.Close;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(LogEntry);
end
);
end;
end;
procedure TMyForm.MyTCPServerException(AContext: TIdContext; AException: Exception);
begin
if not (AException is EIdSilentException) then
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('** An error occurred' + sLineBreak + 'With a message: ' + AException.Message);
end
);
end;
end;
顺便说一句,将 TThread.Synchronize()
与 TIdTCPServer
一起使用时必须非常小心。如果在服务器事件处理程序调用 Synchronize()
时主 UI 线程正忙于停用服务器,则会在 UI 线程和同步线程之间发生死锁(主线程正在等待服务器完成停用,但服务器正在等待线程终止,但线程正在等待 UI 线程完成停用服务器)。对于您所展示的简单日志记录,我建议使用 TThread.Queue()
来避免任何潜在的死锁。或者在工作线程中停用服务器,以便 UI 线程可以自由地继续处理 Synchronize()
请求。
我正在使用 Indy TCPClient/TCPServer 来验证移动设备的注册。这个过程相当简单,我在服务器端读取一个标识符,根据数据库控制文件对其进行验证,然后将响应发送回客户端。
大部分情况下一切似乎都正常工作,但我会定期在服务器端收到 EIDConnClosedGracefully 异常。我似乎无法准确指出连接未正确关闭的位置。事实上,服务器似乎在不应该的时候(连接关闭后)执行了 readln,我不知道为什么。可能我没有正确同步。我在 Tools/Options/Debugger 选项中将 Indy Silent Exceptions 设置为忽略,但我想知道是什么引发了异常。我可以执行注册函数4、5次就抛出异常但是很不一致。
如有任何建议,我们将不胜感激。
以下是我的代码:
服务器
try
MIRec.RecType := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I')
or (MIRec.RecType = 'R') then
begin
// Verify the connecting device is registered
MIRec.Identifier := AContext.Connection.IOHandler.ReadLn;
qryMobileDevice.Close;
qryMobileDevice.Parameters.ParamByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.Open;
AContext.Connection.IOHandler.WriteLn(qryMobileDevice.FindField('ACTIVE').AsString);
MIRec.DeviceName := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') then
LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
else
begin
// Register the Device in STIKS
if qryMobileDevice.EOF then
begin
LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
// If Record Does not exist Add to the Control File;
NextId := GetNextId('NEXT_MOBILE_DEVICE_ID');
qryMobileDevice.Insert;
qryMobileDevice.FieldByName('MOBILE_DEVICE_ID').Value := NextId;
qryMobileDevice.FieldByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.FieldByName('DESCRIPTION').Value := MiRec.Text;
qryMobileDevice.FieldByName('ACTIVE').Value := 'T';
qryMobileDevice.FieldByName('OPERATOR_SAVED').Value := 'From App';
qryMobileDevice.FieldByName('DATE_SAVED').Value := Now;
qryMobileDevice.Post;
end
else
begin
// Device has been Flagged and registration refused.
if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
LogEntry := '** Registration Successful: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now)
else
LogEntry := '** Registration Refused: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
end;
qryMobileDevice.Close;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(LogEntry);
end);
end;
except
on e: exception do
begin
Memo1.Lines.Add('** An error occurred Receiving File ' + #13#10 + 'With a message: ' + E.Message);
end;
end;
客户
if MessageDlg('Register Device With Server?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbNo, TMsgDlgBtn.mbYes], 0) = mrNo then Exit;
try
IdTCPClient1.Connect;
try
MainForm.IdTCPClient1.IOHandler.WriteLn('R'); // Tell Server we are sending a Registration Record
Device := TUIDevice.Wrap(TUIDevice.OCClass.currentDevice);
IdTCPClient1.IOHandler.WriteLn(NSStrToStr(Device.identifierForVendor.UUIDString));
Registered := IdTCPClient1.IOHandler.ReadLn; // Get response from server
Authenticated := (Registered = 'T');
IdTCPClient1.IOHandler.WriteLn(NSStrToStr(Device.Name));
finally
IdTCPClient1.DisConnect;
if Registered <> 'T' then
MessageDlg('Registration Failed!', TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0)
else
begin
Authenticated := True;
MessageDlg('Registration Has Completed Successfully!', TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0);
end;
end;
except
on e: exception do
begin
MessageDlg('** An error occurred Registering Device ' + #13#10 + 'With a message: ' + E.Message, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0);
end;
end;
我的日志输出。
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:36:54 AM
A Client Disconnected
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:37:00 AM
A Client Disconnected
A Client connected
** Registration Successful: ; 7FFC0274-AFB1-4E35-B8D9-F987B587804D; Wed. September 30/2015, 9:37:04 AM
** An error occurred Receiving File
With a message: Connection Closed Gracefully.
A Client Disconnected
服务器代码是否在OnConnect
或OnExecute
事件中?
假设 OnExecute
,这是一个 looped 事件,它会在连接的生命周期内不断循环。在每次迭代中,您将再次调用 ReadLn
以从客户端读取下一个命令。如果客户端断开连接,则必须返回套接字以获取更多数据的下一次读取(在 IOHandler.InputBuffer
已用完之后)将相应地引发异常。这是正常行为,也是 Indy 设计的运作方式。
真正的问题是你有一个异常处理程序,它无条件地将所有异常记录为错误,甚至是优雅的断开连接。并且你的异常处理程序在将错误消息添加到你的备忘录时没有与 UI 线程同步,并且它没有重新引发任何捕获的 Indy 异常所以 TIdTCPServer
可以根据需要处理它(例如停止 OnExecute
循环并触发 OnDisconnect
事件)。
尝试更像这样的东西:
// if registration is only done once, this code should
// be in the OnConnect event instead...
procedure TMyForm.MyTCPServerExecute(AContext: TIdContext);
begin
try
MIRec.RecType := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') or
(MIRec.RecType = 'R') then
begin
// Verify the connecting device is registered
MIRec.Identifier := AContext.Connection.IOHandler.ReadLn;
qryMobileDevice.Close;
qryMobileDevice.Parameters.ParamByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.Open;
AContext.Connection.IOHandler.WriteLn(qryMobileDevice.FindField('ACTIVE').AsString);
MIRec.DeviceName := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') then
LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
else
begin
// Register the Device in STIKS
if qryMobileDevice.EOF then
begin
LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
// If Record Does not exist Add to the Control File;
NextId := GetNextId('NEXT_MOBILE_DEVICE_ID');
qryMobileDevice.Insert;
qryMobileDevice.FieldByName('MOBILE_DEVICE_ID').Value := NextId;
qryMobileDevice.FieldByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.FieldByName('DESCRIPTION').Value := MiRec.Text;
qryMobileDevice.FieldByName('ACTIVE').Value := 'T';
qryMobileDevice.FieldByName('OPERATOR_SAVED').Value := 'From App';
qryMobileDevice.FieldByName('DATE_SAVED').Value := Now;
qryMobileDevice.Post;
end
else
begin
// Device has been Flagged and registration refused.
if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
LogEntry := '** Registration Successful: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now)
else
LogEntry := '** Registration Refused: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
end;
qryMobileDevice.Close;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(LogEntry);
end
);
end;
except
on E: Exception do
begin
if not (E is EIdSilentException) then
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('** An error occurred Receiving File ' + #13#10 + 'With a message: ' + E.Message);
end
);
end;
// optionally remove the below 'if' to close the
// connection on any exception, including DB errors, etc...
if E is EIdException then
raise;
end;
end;
end;
另一方面,我建议完全删除 try/except
并改用服务器的 OnException
事件。让任何异常关闭连接,然后在最后记录原因:
procedure TMyForm.MyTCPServerExecute(AContext: TIdContext);
begin
MIRec.RecType := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') or
(MIRec.RecType = 'R') then
begin
// Verify the connecting device is registered
MIRec.Identifier := AContext.Connection.IOHandler.ReadLn;
qryMobileDevice.Close;
qryMobileDevice.Parameters.ParamByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.Open;
AContext.Connection.IOHandler.WriteLn(qryMobileDevice.FindField('ACTIVE').AsString);
MIRec.DeviceName := AContext.Connection.IOHandler.ReadLn;
if (MIRec.RecType = 'I') then
LogEntry := 'A Connection Has Been Established With: ' + MIRec.DeviceName
else
begin
// Register the Device in STIKS
if qryMobileDevice.EOF then
begin
LogEntry := 'Registering: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
// If Record Does not exist Add to the Control File;
NextId := GetNextId('NEXT_MOBILE_DEVICE_ID');
qryMobileDevice.Insert;
qryMobileDevice.FieldByName('MOBILE_DEVICE_ID').Value := NextId;
qryMobileDevice.FieldByName('IDENTIFIER').Value := MIRec.Identifier;
qryMobileDevice.FieldByName('DESCRIPTION').Value := MiRec.Text;
qryMobileDevice.FieldByName('ACTIVE').Value := 'T';
qryMobileDevice.FieldByName('OPERATOR_SAVED').Value := 'From App';
qryMobileDevice.FieldByName('DATE_SAVED').Value := Now;
qryMobileDevice.Post;
end
else
begin
// Device has been Flagged and registration refused.
if qryMobileDevice.FindField('ACTIVE').AsString = 'T' then
LogEntry := '** Registration Successful: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now)
else
LogEntry := '** Registration Refused: ' + MiRec.Text + '; ' + MIRec.Identifier + '; ' + FormatDateTime('ddd. mmmm d/yyyy, h:mm:ss AM/PM', Now);
end;
qryMobileDevice.Close;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(LogEntry);
end
);
end;
end;
procedure TMyForm.MyTCPServerException(AContext: TIdContext; AException: Exception);
begin
if not (AException is EIdSilentException) then
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('** An error occurred' + sLineBreak + 'With a message: ' + AException.Message);
end
);
end;
end;
顺便说一句,将 TThread.Synchronize()
与 TIdTCPServer
一起使用时必须非常小心。如果在服务器事件处理程序调用 Synchronize()
时主 UI 线程正忙于停用服务器,则会在 UI 线程和同步线程之间发生死锁(主线程正在等待服务器完成停用,但服务器正在等待线程终止,但线程正在等待 UI 线程完成停用服务器)。对于您所展示的简单日志记录,我建议使用 TThread.Queue()
来避免任何潜在的死锁。或者在工作线程中停用服务器,以便 UI 线程可以自由地继续处理 Synchronize()
请求。