You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单TIdSMTP实例重连SMTP时触发EIdConnClosedGracefully异常求助

问题解答

这种单TIdSMTP实例复用触发EIdConnClosedGracefully的情况很常见,核心原因是SSLIOHandler在连接关闭后状态未彻底重置,导致第二次握手时被服务器主动断开。以下是针对性的解决思路和代码调整方案:

原因分析

TIdSSLIOHandlerSocketOpenSSL维护了SSL会话的上下文状态,当调用Disconnect关闭连接后,这些状态(比如残留的会话缓存、未清理的缓冲区)并没有被完全重置。再次调用Connect时,旧状态会干扰新的SSL握手流程,服务器检测到异常后会主动关闭连接,从而抛出该异常。而每次新建实例时,所有状态都是全新的,自然不会有问题。

解决方案

方案1:复用实例时重置SSLIOHandler

在每次断开连接后,彻底重置SSLIOHandler的状态,最可靠的方式是销毁旧的IOHandler并重新创建,同时重新配置SSL参数:

procedure TMainMailSender.SendMessage(var pSendingResult: Boolean);
var
  Sent: Boolean;
begin
  pSendingResult := False;
  Sent := False;
  try
    try
      if IsConnectionForEachMessage then
      begin
        if not fIdSMTP.Connected then
          fIdSMTP.Connect;
      end;
      try
        if fIdSMTP.Connected then
        begin
          fIdSMTP.Send(fIdMessage);
          Sent := True;
        end;
      finally
        if IsConnectionForEachMessage then
        begin
          if fIdSMTP.Connected then
            fIdSMTP.Disconnect;
          // 销毁旧的SSLIOHandler并重新创建
          fIdSSLIOHandlerSocketOpenSSL.Free;
          fIdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
          fIdSMTP.IOHandler := fIdSSLIOHandlerSocketOpenSSL;
          // 重新应用SSL配置(复制你之前configuration里的SSLOptions等设置)
          // 例如:fIdSSLIOHandlerSocketOpenSSL.SSLOptions.SSLVersions := [sslvTLSv1_2];
        end;
      end;
    except
      on E: Exception do
      begin
        fErrorMessage := E.Message;
        fIdSMTP.Disconnect(False);
        if Assigned(fIdSMTP.IOHandler) then
          fIdSMTP.IOHandler.InputBuffer.Clear;
      end;
    end;
  finally
    pSendingResult := Sent;
  end;
end;

方案2:简化Disconnect逻辑,避免手动操作IOHandler

TIdSMTP的Disconnect方法会自动负责关闭并清理IOHandler资源,手动调用IOHandler.Close会导致重复操作,破坏内部状态。去掉不必要的手动IOHandler操作:

procedure TMainMailSender.SendMessage(var pSendingResult: Boolean);
var
  Sent: Boolean;
begin
  pSendingResult := False;
  Sent := False;
  try
    try
      if IsConnectionForEachMessage then
      begin
        if not fIdSMTP.Connected then
          fIdSMTP.Connect;
      end;
      try
        if fIdSMTP.Connected then
        begin
          fIdSMTP.Send(fIdMessage);
          Sent := True;
        end;
      finally
        if IsConnectionForEachMessage then
        begin
          if fIdSMTP.Connected then
            fIdSMTP.Disconnect;
          // 仅清空缓冲区即可,无需手动关闭IOHandler
          if Assigned(fIdSMTP.IOHandler) then
            fIdSMTP.IOHandler.InputBuffer.Clear;
        end;
      end;
    except
      on E: Exception do
      begin
        fErrorMessage := E.Message;
        fIdSMTP.Disconnect(False);
        if Assigned(fIdSMTP.IOHandler) then
          fIdSMTP.IOHandler.InputBuffer.Clear;
      end;
    end;
  finally
    pSendingResult := Sent;
  end;
end;

额外建议

检查你使用的Indy组件版本,旧版本存在一些复用SSL连接时的状态残留bug,升级到最新稳定版可以解决很多已知问题。

内容的提问来源于stack exchange,提问作者JBnew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:35:55