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作为 yahoo smtp 服务器,使用smtp.gmail.com作为 gmail 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 和视觉工作室 2008

4

2 回答 2

3

您应该将端口587用于 gmail、sslenabletlsauth. 这是我用 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();
        }
于 2013-10-12T21:37:28.667 回答
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 将等待直到 可以读取抓取的握手数据,这将导致超时。

希望,这有帮助。

于 2013-10-17T23:28:06.277 回答