【问题标题】:Socket Error 110: Connection Timed Out - Android Delphi SMTP Gmail套接字错误 110:连接超时 - Android Delphi SMTP Gmail
【发布时间】:2016-10-26 05:12:32
【问题描述】:

我正在使用 Delphi 为 Android 编写一个程序来发送包含数据的电子邮件。照原样,我的应用程序存在连接问题。

我有

  • 将主机设置为 smtp.gmail.com,

  • 输入我的 gmail 帐户的用户名和密码,

  • 为 TIdMessage 的地址和正文字段输入有效信息,

  • 制作并添加了附件,

  • 根据在线示例为 TIdSSLIOHandlerSocketOpenSSL 设置 SSL 选项,并且

  • 添加了 Indy 提供的所有 SASL 机制。

我正在使用端口 587 并已明确连接到 TLS。

type
  TForm1 = class(TForm)
    SendBtn: TButton;
    IdSMTP1: TIdSMTP;
    IdMessage1: TIdMessage;
    IdSASLAnonymous1: TIdSASLAnonymous;
    IdSASLCRAMMD51: TIdSASLCRAMMD5;
    IdSASLCRAMSHA11: TIdSASLCRAMSHA1;
    IdSASLDigest1: TIdSASLDigest;
    IdSASLExternal1: TIdSASLExternal;
    IdSASLLogin1: TIdSASLLogin;
    IdSASLOTP1: TIdSASLOTP;
    IdSASLOTP2: TIdSASLOTP;
    IdSASLPlain1: TIdSASLPlain;
    IdSASLSKey1: TIdSASLSKey;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;

    procedure SendBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Attachment : TIdAttachmentFile;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
  IdSMTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(idSMTP1);
  IdSMTP1.UseTLS := utUseExplicitTLS;
  IdSMTP1.AuthType := satSASL;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMSHA11;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLAnonymous1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMMD51;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLDigest1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLExternal1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLLogin1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP2;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLPlain1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLSKey1;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmUnassigned;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode := [];
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0;
end;

procedure TForm1.SendBtnClick(Sender: TObject);
begin
  if IdSMTP1.Connected=True then IdSMTP1.Disconnect;
  IdMessage1.From.Address := 'myemail@gmail.com';
  IdMessage1.Recipients.EMailAddresses := 'other@gmail.com';
  IdMessage1.BccList.Add.Address := '';
  IdMessage1.CCList.Add.Address := '';
  IdMessage1.Priority := mpHigh;
  IdMessage1.Sender.Address := 'myemail@gmail.com';
  IdMessage1.Subject := 'Test Data';   //Add Date/time
  IdMessage1.Body.Add('Hello!');
  Attachment := TIdAttachmentFile.Create(IdMessage1.MessageParts, (GethomePath+'/Test.txt'));
  IdSMTP1.Connect;
  IdSMTP1.Authenticate;
  IdSMTP1.Send(IdMessage1);
  IdSMTP1.Disconnect;
end;

失败于:

IdSMTP1.Connect;    

以这种方式连接到 Android 是否存在已知问题?

【问题讨论】:

    标签: android delphi smtp gmail


    【解决方案1】:

    在这一行:

    IdSMTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(idSMTP1);
    

    您正在创建和分配一个新的默认初始化SSLIOHandler,而不是使用表单上现有的SSLIOHandler

    该行应该是这样的:

    IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
    

    事实上,您在FormCreate() 中所做的一切都可以(并且应该)在设计时在表单设计器中完成。您无需在代码中执行此操作。

    另外,我在您的表单上没有看到TIdUserPassProvider。您使用的大多数 SASL 组件的用户名/密码都需要一个。 TIdSMTP.UserNameTIdSMTP.Password 属性仅在 AuthTypesatDefault 而不是 satSASL 时使用。

    除此之外,我建议对SendBtnClick() 进行一些额外的更改:

    1. 您应该调用IdMessage1.Clear(),这样您就不会继承先前发送的现有数据。您正在将新值附加到 IdMessage1.BccListIdMessage1.CCListIdMessage1.BodyIdMessage1.MessageParts,而没有先清除旧值。

    2. 您无需调用Authenticate()Send() 在内部为您完成。

    3. Send() 应该在 try/finallytry/except 块中,这样即使 Send() 失败,您也可以调用 Disconnect()

    4. 您没有正确配置 TIdMessage 以将文本和附件混合在一起。您应该TIdText 添加到MessageParts 而不是使用TIdMessage.Body(但是,如果TIdMessage.ConvertPreamble 为真,它会将TIdMessage.Body 转换为TIdText是否存在任何附件)。但无论哪种方式,您都需要将TIdMessage.ContentType 属性设置为'multipart/mixed',以便接收者知道有多个部分。

    试试这个:

    type
      TForm1 = class(TForm)
        SendBtn: TButton;
        IdSMTP1: TIdSMTP;
        IdMessage1: TIdMessage;
        IdSASLAnonymous1: TIdSASLAnonymous;
        IdSASLCRAMMD51: TIdSASLCRAMMD5;
        IdSASLCRAMSHA11: TIdSASLCRAMSHA1;
        IdSASLDigest1: TIdSASLDigest;
        IdSASLExternal1: TIdSASLExternal;
        IdSASLLogin1: TIdSASLLogin;
        IdSASLOTP1: TIdSASLOTP;
        IdSASLOTP2: TIdSASLOTP;
        IdSASLPlain1: TIdSASLPlain;
        IdSASLSKey1: TIdSASLSKey;
        IdUserPassProvider1: TIdUserPassProvider;
        IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
    
        procedure SendBtnClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      // all of this code can be handled at design-time instead!
    
      IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1;
      IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmUnassigned;
      IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode := [];
      IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0;
    
      IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
      IdSMTP1.UseTLS := utUseExplicitTLS;
      IdSMTP1.AuthType := satSASL;
    
      IdSASLCRAMSHA11.UserPassProvider := IdUserPassProvider1;
      IdSASLCRAMMD51.UserPassProvider := IdUserPassProvider1;
      IdSASLDigest1.UserPassProvider := IdUserPassProvider1;
      IdSASLLogin1.UserPassProvider := IdUserPassProvider1;
      IdSASLOTP1.UserPassProvider := IdUserPassProvider1;
      IdSASLOTP2.UserPassProvider := IdUserPassProvider1;
      IdSASLPlain1.UserPassProvider := IdUserPassProvider1;
      IdSASLSKey1.UserPassProvider := IdUserPassProvider1;
    
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMSHA11;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLAnonymous1;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMMD51;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLDigest1;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLExternal1;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLLogin1;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP1;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP2;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLPlain1;
      IdSMTP1.SASLMechanisms.Add.SASL := IdSASLSKey1;
    
      // end design-time capable hookups
    
      IdSMTP1.Host := ...;
      IdSMTP1.Port := ...;
      IdUserPassProvider1.UserName := ...;
      IdUserPassProvider1.Password := ...;
    end;
    
    procedure TForm1.SendBtnClick(Sender: TObject);
    var
      Text: TIdText;
      Attachment : TIdAttachmentFile;
    begin
      if IdSMTP1.Connected then IdSMTP1.Disconnect;
    
      IdMessage1.Clear;
      IdMessage1.From.Address := 'myemail@gmail.com';
      IdMessage1.Recipients.EMailAddresses := 'other@gmail.com';
      IdMessage1.Priority := mpHigh;
      IdMessage1.Sender.Address := 'myemail@gmail.com';
      IdMessage1.Subject := 'Test Data';   //Add Date/time
      //IdMessage1.Body.Add('Hello!');
      Text := TIdText.Create(IdMessage1.MessageParts);
      Text.ContentType := 'text/plain';
      Text.Body.Add('Hello!');
      Attachment := TIdAttachmentFile.Create(IdMessage1.MessageParts, (GethomePath+'/Test.txt'));
      IdMessage1.ContextType := 'multipart/mixed';             
    
      IdSMTP1.Connect;
      try
        IdSMTP1.Send(IdMessage1);
      finally
        IdSMTP1.Disconnect;
      end;
    end;
    

    【讨论】:

    • 这是一个非常彻底的答案!非常感谢您的帮助。但是,这不允许我的应用程序实际发送电子邮件。我收到一条错误消息,说要通过我的网络浏览器登录,还有一封来自谷歌的电子邮件,说一个没有足够安全性的应用程序试图访问我的帐户。关于如何解决这个问题的任何想法?我认为我们使用 TLS 符合安全标准。再次感谢!
    • 您没有收到 TLS 错误,您收到的是身份验证错误。加密(套接字传输的安全性)和身份验证(您的 Google 身份)是两个不同的东西。您需要进入您的 Google 帐户,并且 1) 启用 "Less secure apps",然后 Indy 可以使用您的正常 Google 密码登录; 2) 打开两步验证,然后生成一个"App-specific password" 供 Indy 使用,而不是您的普通密码。
    • 此时,Indy 没有实现 OAuth,尤其是在 SASL 上(尽管我认为可能会有一些 3rd 方实现浮动),这就是为什么 TIdSMTP 无法使用您的正常 Google 密码登录,除非您降低了 Google 安全设置,或仅为 Indy 生成单独的密码。
    • 哇!这太棒了!这很有趣,很高兴知道。但是,附件不能以这种方式工作。程序在点击发送按钮时挂起,但在附件的两行被注释掉后才可以运行。
    • @DelphiAndroid12:我无法回答。您将不得不自己调试它并找出实际冻结的内容。文件是否存在?您确定您使用的是当前路径吗?您是否有权访问该文件?在任何情况下,您都不应该在主 UI 线程中执行阻塞操作,尤其是在 Android 上(它可能会杀死您的应用程序!)。您的按钮启动一个工作线程来处理在 UI 线程之外发送的电子邮件。
    猜你喜欢
    • 1970-01-01
    • 2016-10-19
    • 2014-07-18
    • 2014-10-16
    • 1970-01-01
    • 2014-07-10
    • 2020-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多