Ошибка сокета 110: время ожидания подключения истекло — Android Delphi SMTP Gmail

Я пишу программу для Android, используя Delphi для отправки электронной почты с данными. Как есть, у моего приложения есть проблемы с подключением.

у меня есть

  • указать хост smtp.gmail.com,

  • введите мое имя пользователя и пароль для моей учетной записи gmail,

  • введите действительную информацию для полей адреса и тела TIdMessage,

  • сделал и добавил вложенный файл,

  • установите параметры SSL для TIdSSLIOHandlerSocketOpenSSL в соответствии с онлайн-примерами и

  • добавлены все механизмы SASL, которые предлагает Indy.

Я использую порт 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 := '[email protected]';
  IdMessage1.Recipients.EMailAddresses := '[email protected]';
  IdMessage1.BccList.Add.Address := '';
  IdMessage1.CCList.Add.Address := '';
  IdMessage1.Priority := mpHigh;
  IdMessage1.Sender.Address := '[email protected]';
  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 таким образом?


person DelphiAndroid12    schedule 23.06.2016    source источник


Ответы (1)


В этой строке:

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

Вы создаете и назначаете новый SSLIOHandler, инициализированный по умолчанию, вместо того, чтобы использовать существующий SSLIOHandler, который находится в форме.

Вместо этого строка должна быть такой:

IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;

На самом деле, все, что вы делаете в FormCreate(), можно (и нужно) делать в конструкторе форм во время разработки. Вам не нужно делать это в коде.

Кроме того, я не вижу TIdUserPassProvider в вашей форме. Большинство компонентов SASL, которые вы используете, требуют его для своего имени пользователя/пароля. Свойства TIdSMTP.UserName и TIdSMTP.Password используются только тогда, когда AuthType равно satDefault, а не satSASL.

Кроме этого, я бы предложил некоторые дополнительные изменения в SendBtnClick():

  1. Вы должны вызвать IdMessage1.Clear(), чтобы не переносить существующие данные из предыдущих отправок. Вы добавляете новые значения к IdMessage1.BccList, IdMessage1.CCList, IdMessage1.Body и IdMessage1.MessageParts, не удаляя предварительно старые значения.

  2. Вам не нужно звонить Authenticate(), Send() сделает это за вас.

  3. Send() должен находиться в блоке try/finally или try/except, чтобы вы могли вызывать Disconnect() даже в случае сбоя Send().

  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 := '[email protected]';
  IdMessage1.Recipients.EMailAddresses := '[email protected]';
  IdMessage1.Priority := mpHigh;
  IdMessage1.Sender.Address := '[email protected]';
  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;
person Remy Lebeau    schedule 23.06.2016
comment
Это был очень обстоятельный ответ! Спасибо большое за помощь. Однако это не позволяет моему приложению отправлять электронное письмо. Я получил сообщение об ошибке, в котором говорилось, что нужно войти в систему через веб-браузер, и электронное письмо от Google, в котором говорилось, что приложение без достаточной безопасности пыталось получить доступ к моей учетной записи. Любые мысли о том, как обойти это? Я думал, что мы соблюдаем стандарты безопасности, используя TLS. Еще раз спасибо! - person DelphiAndroid12; 24.06.2016
comment
Вы не получаете ошибку TLS, вы получаете ошибку аутентификации. Шифрование (безопасность передачи сокетов) и аутентификация (ваша идентификация Google) — это две разные вещи. Вам нужно войти в свой аккаунт Google и либо 1) включить Небезопасные приложения, затем Indy может войти в систему, используя ваш обычный пароль Google; 2) включите двухэтапную аутентификацию, а затем сгенерируйте пароль приложения для Indy. использовать вместо обычного пароля. - person Remy Lebeau; 24.06.2016
comment
В настоящее время Indy не реализует OAuth, особенно через SASL (хотя я думаю, что могут быть некоторые сторонние реализации), поэтому TIdSMTP не может войти в систему, используя свой обычный пароль Google, если вы не снизите настройки безопасности Google или не создадите отдельный пароль только для Indy. - person Remy Lebeau; 24.06.2016
comment
Вау! Это круто! Это интересно и полезно знать. Однако приставка так не работает. Программа просто зависает при нажатии на кнопку отправки, но работает, когда закомментированы две строчки о вложении. - person DelphiAndroid12; 24.06.2016
comment
@ DelphiAndroid12: я не могу на это ответить. вам придется отладить его самостоятельно и узнать, что на самом деле зависает. Файл существует? Вы уверены, что используете текущий путь к нему? У вас есть права доступа к файлу? В любом случае вам не следует с самого начала выполнять блокирующие операции в основном потоке пользовательского интерфейса, особенно на Android (это может убить ваше приложение!). Ваша кнопка запускает рабочий поток для обработки отправки электронной почты за пределы потока пользовательского интерфейса. - person Remy Lebeau; 24.06.2016
comment
Вы правы - я не должен использовать правильный путь к файлу. GetHomePath+'/Test.txt' в этом случае не работает. Это работает, чтобы прочитать содержимое моего файла в заметку. Есть мысли, что с этим делать? - person DelphiAndroid12; 27.06.2016
comment
@DelphiAndroid12: Если путь работает при загрузке того же файла в памятку, то он также должен работать и для TIdAttachmentFile. Путь есть путь независимо от того, как он используется. Убедитесь, что файл еще не открыт для записи, когда вы отправляете электронное письмо, так как TIdAttachmentFile открывает файл с доступом fmShareDenyWrite. - person Remy Lebeau; 27.06.2016
comment
Как именно это проверить и что с этим делать? - person DelphiAndroid12; 27.06.2016
comment
@ DelphiAndroid12: Это обсуждение выходит далеко за рамки первоначального вопроса. Пожалуйста, опубликуйте новый вопрос о проблеме TIdAttachmentFile, исходный вопрос о проблеме тайм-аута сокета был решен. И, пожалуйста, отлаживайте свой код, чтобы понять, что происходит на самом деле. Вы не должны замерзнуть. Если есть проблема с доступом к файлу, вы должны получить исключение. - person Remy Lebeau; 27.06.2016
comment
В ПОРЯДКЕ. Спасибо за вашу помощь! - person DelphiAndroid12; 27.06.2016