草庐IT

c++ - 我可以连接到 Yahoo smtp 服务器但不能连接到 gmail 服务器

coder 2024-02-17 原文

我有一个简单的 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 作为 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 和 visual studio 2008 !

最佳答案

以下示例适用于 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() );
}

关于您的 sample 。我猜你有以下情况之一:

  1. 您在网络级别遇到连接问题。你能连接吗 没有 SSL 的 smtp.gmail.com 端口 465?
  2. 我相信你有这种情况。您正在拦截 readyRead 信号 并且可能(此代码未在此处显示)读取接收到的数据 之前您进入加密状态(即 SSL 握手数据)。因为你已经读取了握手数据,所以 QSslSocket 会等到 可以读取抓取的握手数据,这会导致超时。

希望这对您有所帮助。

关于c++ - 我可以连接到 Yahoo smtp 服务器但不能连接到 gmail 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19339269/

有关c++ - 我可以连接到 Yahoo smtp 服务器但不能连接到 gmail 服务器的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  3. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  4. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  6. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  7. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  8. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  9. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  10. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

随机推荐