Грешка на гнездото 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: Ако пътят работи при зареждане на същия файл в Memo, тогава трябва да работи и за 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