我可以连接到Yahoo的SMTP服务器,但无法连接到Gmail服务器。

4

我有一个简单的SMTP邮件客户端。我可以使用这个应用程序和我的雅虎邮箱账号发送邮件。但是当我尝试使用我的Gmail账号发送邮件时,连接到Google的SMTP服务器失败了!以下是我的SMTP类:

Smtp::Smtp( const QString &user, const QString &pass, const QString &host, int port, int timeout )
{
    socket = new QSslSocket(this);

    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(socket, SIGNAL(connected()), this, SLOT(connected() ) );
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(errorReceived(QAbstractSocket::SocketError)));
    connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(stateChanged(QAbstractSocket::SocketState)));
    connect(socket, SIGNAL(disconnected()), this,SLOT(disconnected()));


    this->user = user;
    this->pass = pass;

    this->host = host;
    this->port = port;
    this->timeout = timeout;


}

void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
{
   // qDebug() << subject<<" ::: "<<body;
    message = "To: " + to + "\n";
    message.append(QString("From: " + from + "\n"));
    message.append("Subject: " + subject + "\n");
    message.append(body);

    message.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) );
    message.replace( QString::fromLatin1( "\r\n.\r\n" ),
    QString::fromLatin1( "\r\n..\r\n" ) );
    //qDebug()<<message;
    this->from = from;
    rcpt = to;
    state = Init;
    socket->connectToHostEncrypted(host, port); //"smtp.gmail.com" and 465 for gmail TLS
    if (!socket->waitForConnected(timeout)) {
         qDebug() << socket->errorString();
     }

    t = new QTextStream( socket );
    t->setCodec("UTF-8");
}

Smtp::~Smtp()
{
    delete t;
    delete socket;
}
void Smtp::stateChanged(QAbstractSocket::SocketState socketState)
{

    qDebug() <<"stateChanged " << socketState;
}

void Smtp::errorReceived(QAbstractSocket::SocketError socketError)
{
    qDebug() << "error " <<socketError;
}

void Smtp::disconnected()
{

    qDebug() <<"disconneted";
    qDebug() << "error "  << socket->errorString();
}

void Smtp::connected()
{
    qDebug() << "Connected ";
}

void Smtp::readyRead()
{

     qDebug() <<"readyRead";
    // SMTP is line-oriented

    QString responseLine;
    do
    {
        responseLine = socket->readLine();
        response += responseLine;
    }
    while ( socket->canReadLine() && responseLine[3] != ' ' );

    responseLine.truncate( 3 );

    qDebug() << "Server response code:" <<  responseLine;
    qDebug() << "Server response: " << response;

    if ( state == Init && responseLine == "220" )
    {
        // banner was okay, let's go on
        *t << "EHLO localhost" <<"\r\n";
        t->flush();

        state = HandShake;
    }
    //No need, because I'm using socket->startClienEncryption() which makes the SSL handshake for you
    /*else if (state == Tls && responseLine == "250")
    {
        // Trying AUTH
        qDebug() << "STarting Tls";
        *t << "STARTTLS" << "\r\n";
        t->flush();
        state = HandShake;
    }*/
    else if (state == HandShake && responseLine == "250")
    {
        socket->startClientEncryption();
        if(!socket->waitForEncrypted(timeout))
        {
            qDebug() << socket->errorString();
            state = Close;
        }


        //Send EHLO once again but now encrypted

        *t << "EHLO localhost" << "\r\n";
        t->flush();
        state = Auth;
    }
    else if (state == Auth && responseLine == "250")
    {
        // Trying AUTH
        qDebug() << "Auth";
        *t << "AUTH LOGIN" << "\r\n";
        t->flush();
        state = User;
    }
    else if (state == User && responseLine == "334")
    {
        //Trying User
        qDebug() << "Username";
        //GMAIL is using XOAUTH2 protocol, which basically means that password and username has to be sent in base64 coding
        //https://developers.google.com/gmail/xoauth2_protocol
        *t << QByteArray().append(user).toBase64()  << "\r\n";
        t->flush();

        state = Pass;
    }
    else if (state == Pass && responseLine == "334")
    {
        //Trying pass
        qDebug() << "Pass";
        *t << QByteArray().append(pass).toBase64() << "\r\n";
        t->flush();

        state = Mail;
    }
    else if ( state == Mail && responseLine == "235" )
    {
        // HELO response was okay (well, it has to be)

        //Apperantly for Google it is mandatory to have MAIL FROM and RCPT email formated the following way -> <email@gmail.com>
        qDebug() << "MAIL FROM:<" << from << ">";
        *t << "MAIL FROM:<" << from << ">\r\n";
        t->flush();
        state = Rcpt;
    }
    else if ( state == Rcpt && responseLine == "250" )
    {
        //Apperantly for Google it is mandatory to have MAIL FROM and RCPT email formated the following way -> <email@gmail.com>
        *t << "RCPT TO:<" << rcpt << ">\r\n"; //r
        t->flush();
        state = Data;
    }
    else if ( state == Data && responseLine == "250" )
    {

        *t << "DATA\r\n";
        t->flush();
        state = Body;
    }
    else if ( state == Body && responseLine == "354" )
    {

        *t << message << "\r\n.\r\n";
        t->flush();
        state = Quit;
    }
    else if ( state == Quit && responseLine == "250" )
    {

        *t << "QUIT\r\n";
        t->flush();
        // here, we just close.
        state = Close;
        emit status( tr( "Message sent" ) );
    }
    else if ( state == Close )
    {
        deleteLater();
        return;
    }
    else
    {
        // something broke.
        QMessageBox::warning( 0, tr( "Qt Simple SMTP client" ), tr( "Unexpected reply from SMTP server:\n\n" ) + response );
        state = Close;
        emit status( tr( "Failed to send message" ) );
    }
    response = "";
}

我使用smtp.mail.yahoo.com作为雅虎邮件的SMTP服务器,smtp.gmail.com作为谷歌邮件的SMTP服务器。两者都使用端口465。
以下是调试输出(虽然我认为它没什么用):

stateChanged  QAbstractSocket::HostLookupState 
stateChanged  QAbstractSocket::ConnectingState 
stateChanged  QAbstractSocket::UnconnectedState 
error  QAbstractSocket::SocketTimeoutError 
stateChanged  QAbstractSocket::UnconnectedState 
"Socket operation timed out" 

我使用的操作系统是Windows 7 64位,开发工具是Qt 4.8.5 和 Visual Studio 2008


2
失败的原因是什么?你收到了什么错误信息? - Jonathan Potter
2
我建议使用端口993连接smtp.gmail.com并启用SSL加密。 - vahancho
@JonathanPotter ~> 连接超时! - s4eed
@saeed 我认为,Gmail会将某些SMTP命令解析为其他意义,这在弱协议和大公司中很常见。 - PersianGulf
2个回答

3

如果要使用 Gmail 发送邮件,建议使用端口号 587,并启用 ssltlsauth。以下是我用 Java 写的可行方案,虽然此代码没有使用 Qt 框架,但可以指导你如何正确配置 smtp 服务器以通过 Gmail 发送电子邮件。

public void sendMail2(){   
        Email email = new SimpleEmail();
        try {
            DateFormat dateFormat = new SimpleDateFormat(
                                                        "yyyy/MM/dd HH:mm:ss");
            Calendar c = new GregorianCalendar();
          String authuser="email@gmail.com";String authpwd="passtoyouraccount";
            email.setSmtpPort(587);
            email.setAuthenticator(new DefaultAuthenticator(authuser,authpwd));
            email.setDebug(true);
            email.setHostName("smtp.gmail.com");
          email.getMailSession().getProperties().put("mail.smtps.auth","true");
          email.getMailSession().getProperties().put("mail.debug", "true");
          email.getMailSession().getProperties().put("mail.smtps.port","587");
            email.getMailSession().getProperties().put(
            "mail.smtps.socketFactory.port", "587");
          email.getMailSession().getProperties().put(
            "mail.smtps.socketFactory.class",
            "javax.net.ssl.SSLSocketFactory");
          email.getMailSession().getProperties().put(
            "mail.smtps.socketFactory.fallback", "false");
          email.getMailSession().getProperties().put(
            "mail.smtp.starttls.enable", "true");
          email.setFrom("me@gmail.com", "my_site");
          email.setSubject("new message");
          Date date = new Date();
          email.setMsg("from: "+userMail+"\ndate:"                  
                               +dateFormat.format(date)+"\n\n"+mailContent);
          email.addTo("addMeToo@gmail.com","ToName");
          email.send();
        } catch (EmailException e) {
            e.printStackTrace();
        }

对于那些在C++中创建或使用自己的SMTP客户端的人来说,这是完全无用的。SimpleEmail Java类处理了连接到SMTP服务器并通过SSL进行通信的所有细节。你认为你的解决方案实际上会对OP有什么帮助? - Captain Obvlious
好的,实际上每一行都很重要。我记得当我尝试编写可工作的解决方案时遇到了问题,这里的每个细节都很重要,让我们从端口587开始。 - 4pie0

3

以下示例在GMAIL上运行正常:

SmtpSsl::SmtpSsl(QObject *parent) :
    QObject(parent) ,
    smtp( new QSslSocket ) ,
    istd( new QFile ) ,
    ostd( new QFile )
{
    qDebug() << "constructing";


    // QIODevice
    QObject::connect( smtp , SIGNAL(aboutToClose()) , this , SLOT(sck_aboutToClose()) );
    QObject::connect( smtp , SIGNAL(bytesWritten(qint64)) , this , SLOT    (sck_bytesWritten(qint64)) );
    QObject::connect( smtp , SIGNAL(readChannelFinished()) , this , SLOT(sck_readChannelFinished()) );
    QObject::connect( smtp , SIGNAL(readyRead()) , this , SLOT(sck_readyRead()) );


    // QAbstractSocket
    QObject::connect( smtp , SIGNAL(connected()) , this , SLOT(sck_connected()) );
    QObject::connect( smtp , SIGNAL(disconnected()) , this , SLOT(sck_disconnected()) );
    QObject::connect( smtp , SIGNAL(error(QAbstractSocket::SocketError)) , this , SLOT(sck_error(QAbstractSocket::SocketError)) );
    QObject::connect( smtp , SIGNAL(hostFound()) , this , SLOT(sck_hostfound()) );
    QObject::connect( smtp , SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)) , this , SLOT(sck_proxyAuthRequired()) );
    QObject::connect( smtp , SIGNAL(stateChanged(QAbstractSocket::SocketState)) , this , SLOT(sck_stateChanged(QAbstractSocket::SocketState)) );


    // QSslSocket
    QObject::connect( smtp , SIGNAL(encrypted()) , this , SLOT(sck_encrypted()) );
    QObject::connect( smtp , SIGNAL(encryptedBytesWritten(qint64)) , this , SLOT(sck_encryptedBytesWritten(qint64)) );
    QObject::connect( smtp , SIGNAL(modeChanged(QSslSocket::SslMode)) , this , SLOT(sck_modeChanged(QSslSocket::SslMode)) );
    QObject::connect( smtp , SIGNAL(sslErrors(QList<QSslError>)) , this , SLOT(sck_sslErrors(QList<QSslError>)) );
    QObject::connect( smtp , SIGNAL(peerVerifyError(QSslError)) , this , SLOT(sck_peerVerifyError(QSslError)) );


    // public part
    QObject::connect( smtp , SIGNAL(encrypted()) , this , SLOT(start_session()) );
    QObject::connect( smtp , SIGNAL(disconnected()) , this , SLOT(deleteLater()) );

    smtp->setPeerVerifyMode( QSslSocket::VerifyPeer );
    smtp->connectToHostEncrypted( "smtp.gmail.com" , 465 );
    istd->open( stdin  , QIODevice::ReadOnly );
    ostd->open( stdout , QIODevice::WriteOnly );

    qDebug() << "constructed";
}

SmtpSsl::~SmtpSsl()
{
    qDebug() << "destroying";
    delete smtp;
    qDebug() << "destroyed";
}   

void
SmtpSsl::start_session()
{
    QObject::connect( smtp , SIGNAL(readyRead()) , this , SLOT(receive()) );
}   

void    SmtpSsl::receive()
{
    ostd->write( smtp->readAll() );
    ostd->flush();

    smtp->write( istd->readLine() );
}

关于你的样本,我猜测你可能有以下几种情况:
  1. 你在网络层面上存在连接问题。你能否在没有SSL的情况下连接smtp.gmail.com的465端口?
  2. 我认为你就是这种情况。你正在拦截readyRead信号并且可能(这里没有展示代码)在进入加密状态(即SSL握手数据)之前读取接收到的数据。由于你已经读取了握手数据,QSslSocket将等待直到它可以读取获取的握手数据,这将导致超时。
希望这能帮到你。

我是新手,不知道在哪里可以找到所有这些插槽。请帮忙。 - McLan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接