-2

我正在尝试使用 boost asio 库来实现一个 http 客户端。但我面临一些我无法修复的链接器错误。

这是代码

yql_asio.cpp

#include "common.h"
#include "yql.h"

int main(int argc, char* argv[])
{
    try
    {
        int thread_num = 2;
        if ( argc > 1 )
            thread_num = boost::lexical_cast<int>(argv[1]);


        boost::asio::io_service io_service;

        Yql yql_main(io_service, "http://www.google.com");
        yql_main.GetResponse();

        io_service.run();

    }
    catch(std::exception & e)
    {
        std::cerr<<e.what()<<std::endl;
    }

    return 0;

}

yql.h

#ifndef YQL_H
#define YQL_H

#include "yql_conn.h"
#include "common.h"

typedef std::deque<io_service_ptr> ios_deque;

class Yql //: public boost::noncopyable
{
private:
    std::string m_url;
    std::string m_response;
    //boost::shared_ptr<Connection> m_conn;
    Connection *m_conn;
    boost::asio::io_service &io_service_;

public:
    Yql(boost::asio::io_service &io_service, std::string p_url);
    ~Yql(){}
    void GetResponse();
};


#endif

yql.cpp

#include "yql.h"


Yql::Yql(boost::asio::io_service& io_services, std::string p_url) 
    : m_url(p_url)
    , io_service_(io_services)
{
    m_conn = new Connection(io_service_, p_url);
    //m_conn = Connection::create(io_service_, m_url);
}

void Yql::GetResponse()
{
    m_conn->start();
}

yql_conn.h

#ifndef YQL_CONN_H
#define YQL_CONN_H

#include "common.h"
#include <map>

class Connection //: public boost::enable_shared_from_this<Connection>
{
public:
    typedef boost::shared_ptr<Connection> pointer;

    static pointer create(ba::io_service & io_service, std::string p_url)
    {
        return pointer(new Connection(io_service, p_url));
    }

    Connection(ba::io_service & io_service, std::string p_url);

    /*ba::ip::tcp::socket& socket()
    {
        return socket_;
    } */

    void start();

private:

    /*void handle_browser_write(const bs::error_code & errc, size_t len);*/
    void handle_read_headers(const bs::error_code & errc, size_t len);
    void handle_server_write(const bs::error_code & errc, size_t len);
    void handle_server_read_headers(const bs::error_code & errc, size_t len);
    void handle_server_read_body(const bs::error_code & errc, size_t len);

    void start_connect();
    void start_write_to_server();
    void shutdown();

    void handle_resolve(const boost::system::error_code & err, ba::ip::tcp::resolver::iterator endpoint_iterator);
    void handle_connect(const boost::system::error_code & err, ba::ip::tcp::resolver::iterator endpoint_iterator);

    ba::io_service& io_service_;
    ba::ip::tcp::socket socket_;
    ba::ip::tcp::resolver resolver_;
    //bool proxy_closed;
    //bool isPersistent;
    int32_t RespLen;
    int32_t RespReaded;

    //boost::array<char, 8192> bbuffer;
    boost::array<char, 8192> sbuffer;

    std::string m_response;

    std::string m_url;
    std::string m_headers;
    std::string m_new_url;
    std::string m_method;
    std::string m_req_version;
    std::string m_server;
    std::string m_port;
    //bool m_is_open;

    std::string fReq;

    typedef std::map<std::string, std::string> headersMap;
    headersMap reqHeaders, respHeaders;

    void parseHeaders(const std::string& h, headersMap& m);
};


#endif

yql_conn.cpp

#include "yql_conn.h"

Connection::Connection(ba::io_service & io_service, std::string p_url)
    : io_service_(io_service)
    , socket_(io_service)
    , resolver_(io_service)
    //, proxy_closed(false)
    //, isPersistent(false)
    //, m_is_open(false)
    , m_url(p_url)
{
} //end of Connection()


void Connection::start() //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;

    m_headers.clear();
    reqHeaders.clear();
    respHeaders.clear();

    start_connect();

    //boost::asio::async_read(bsocket_, ba::buffer(bbuffer), ba::transfer_at_least(1),
    //  boost::bind(&Connection::handle_browser_read_headers,
    //              shared_from_this(),
    //              ba::placeholders::error,
    //              ba::placeholders::bytes_transferred) );

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //start() 


void Connection::start_connect() //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;
    m_server = "";
    //std::string port = "80";
    m_port = "80";
    boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
    boost::smatch m;

    if ( boost::regex_search(m_url, m, rHTTP, boost::match_extra) )
    {
        m_server = m[1].str();
        if ( m[2].str() != "" )
        {
            m_port = m[3].str();
        }
        m_new_url = m[4].str();
    }

    if ( m_server.empty() )
    {
        std::cout<<"Can't parse URL "<<std::endl;
        return;
    }

    //if ( !m_is_open || (server != m_server) || (port != m_port) )
    /*if ( port != m_port) )
    {
        m_server = server;
        m_port = port;
    */  
    boost::asio::ip::tcp::resolver::query query(m_server, m_port);

    resolver_.async_resolve(query, 
        boost::bind(&Connection::handle_resolve, this,//shared_from_this(),
            boost::asio::placeholders::error,
            boost::asio::placeholders::iterator));
    //}
    /*else
    {
        start_write_to_server();
    }*/

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
}//start_connect() 


void Connection::handle_resolve(const boost::system::error_code & err,
                                    boost::asio::ip::tcp::resolver::iterator endpoint_iterator) //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;
    if ( !err )
    {
        std::cout<<"Remote address resolved..."<<std::endl;
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
        socket_.async_connect(endpoint, boost::bind(&Connection::handle_connect,
                                            //shared_from_this(),
                                            this,
                                            boost::asio::placeholders::error,
                                            ++endpoint_iterator));
    }
    else
    {
        shutdown();
    }
    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //handle_resolve()


void Connection::handle_connect(const boost::system::error_code & err, 
                boost::asio::ip::tcp::resolver::iterator endpoint_iterator) //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;

    if ( !err )
    {
        boost::asio::ip::tcp::endpoint remote_host = socket_.remote_endpoint();
        boost::asio::ip::address remote_host_addr = remote_host.address();
        std::string addr_repr = remote_host_addr.to_string();
        std::cout<<"Connected to "<<addr_repr<<std::endl;
        //m_is_open = true;
        start_write_to_server();
    }
    else if ( endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
    {
        socket_.close();
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
        socket_.async_connect(endpoint, boost::bind(&Connection::handle_connect, 
                                //shared_from_this(),
                                this,
                                boost::asio::placeholders::error,
                                ++endpoint_iterator));
    }
    else
    {
        shutdown();
    }

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //handle_connect()


void Connection::start_write_to_server() //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;

    fReq = m_method;
    fReq += " ";
    //fReq += m_new_url;
    fReq += m_url;
    fReq += " HTTP/";
    fReq += "1.0";
    fReq += "\r\n";
    fReq += m_headers;

    boost::asio::async_write(socket_, boost::asio::buffer(fReq),
        boost::bind(&Connection::handle_server_write, this, //shared_from_this(), 
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

    m_headers.clear();

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //start_write_to_server()


void Connection::handle_server_write(const bs::error_code & err, size_t len) //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;

    if ( !err )
    {
        std::cout<<"Bytes sent to server :: "<<len<<std::endl;
        boost::asio::async_read(socket_, boost::asio::buffer(sbuffer), boost::asio::transfer_at_least(1),
            boost::bind(&Connection::handle_server_read_headers,
                        //shared_from_this(),
                        this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred ) );
    }
    else
    {
        shutdown();
    }

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //handle_server_write


void Connection::handle_server_read_headers(const boost::system::error_code & err, size_t len) //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;

    if ( !err )
    {
        std::string::size_type idx;

        if ( m_headers.empty() )
            m_headers = std::string(sbuffer.data(), len);
        else
            m_headers += std::string(sbuffer.data(), len);

        idx = m_headers.find("\r\n\r\n");

        if ( idx == std::string::npos )
        {
            boost::asio::async_read(socket_, boost::asio::buffer(sbuffer),
                                        boost::asio::transfer_at_least(1),
                                        boost::bind(&Connection::handle_read_headers,
                                            //shared_from_this(),
                                            this,
                                            boost::asio::placeholders::error,
                                            boost::asio::placeholders::bytes_transferred));
        }
        else
        {
            RespReaded = len - idx - 4;
            idx = m_headers.find("\r\n");
            std::string respString = m_headers.substr(0, idx);
            RespLen = -1;
            parseHeaders(m_headers.substr(idx+2), respHeaders);
            std::string reqConnString = "", respConnString = "";
            std::string respVersion = respString.substr(respString.find("HTTP/")+5,3);
            headersMap::iterator it = respHeaders.find("Content-Length");
            if ( it != respHeaders.end() )
                RespLen = boost::lexical_cast<int>(it->second);
            it = respHeaders.find("Connection");
            if ( it != respHeaders.end() )
                respConnString = it->second;
            it = respHeaders.find("Connection");
            if ( it != respHeaders.end() )
                reqConnString = it->second;

            //isPersistent = (
            //  ((m_req_version == "1.1" && reqConnString != "close") ||
            //   (m_req_version == "1.0" && reqConnString == "keep-alive" )) &&
            //  ((respVersion == "1.1" && respConnString != "close") ||
            //   (respVersion == "1.0" && respConnString == "kepp-alive" )) &&
            //  RespLen != -1 );

            std::cout<<"Header Received :: "<<m_headers;

            boost::asio::async_read(socket_, boost::asio::buffer(sbuffer, len),
                boost::asio::transfer_at_least(1),
                boost::bind(&Connection::handle_server_read_body,
                                //shared_from_this(),
                                this,
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred));

            //boost::asio::async_write(bsocket_, boost::asio::buffer(m_headers),
            //  boost::bind(&Connection::handle_browser_write,
            //              shared_from_this(),
            //              boost::asio::placeholders::error,
            //              boost::asio::placeholders::bytes_transferred));
        }
    }
    else
    {
        shutdown();
    }

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //handle_server_read_headers


void Connection::handle_server_read_body(const bs::error_code & err, size_t len) //called
{
    std::cout<<__FUNCTION__<<"BEGINS"<<std::endl;

    if ( !err || err == boost::asio::error::eof )
    {
        std::cout<<"Data received :: "<<std::string(sbuffer.begin(), sbuffer.end())<<std::endl;
        RespReaded += len;

        boost::asio::async_read(socket_, boost::asio::buffer(sbuffer, len),
        boost::asio::transfer_at_least(1),
        boost::bind(&Connection::handle_server_read_body,
                        //shared_from_this(),
                        this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
        //if ( err == boost::asio::error::eof )
        //  proxy_closed = true;
        //boost::asio::async_write(bsocket_, boost::asio::buffer(sbuffer, len),
        //  boost::bind(&Connection::handle_browser_write,
        //              shared_from_this(),
        //              boost::asio::placeholders::error,
        //              boost::asio::placeholders::bytes_transferred));
    }
    else
    {
        shutdown();
    }

    std::cout<<__FUNCTION__<<"ENDS"<<std::endl;
} //handle_server_read_body

void Connection::handle_read_headers(const bs::error_code & err, size_t len)
{
    if (!err)
    {
        std::cout<<"Bytes Received ... :: "<<len<<std::endl;
        if ( m_headers.empty())
        {
            m_headers = std::string(sbuffer.data(), len);
        }
        else
        {
            m_headers += std::string(sbuffer.data(), len);
        }
        if ( m_headers.find("\r\n\r\n") == std::string::npos )
        {
            boost::asio::async_read(socket_, ba::buffer(sbuffer), ba::transfer_at_least(1),
                boost::bind(&Connection::handle_read_headers, 
                            //shared_from_this(),
                            this,
                            ba::placeholders::error,
                            ba::placeholders::bytes_transferred));
        }
        else
        {
            std::string::size_type idx = m_headers.find("\r\n");
            std::string reqString = m_headers.substr(0, idx);
            m_headers.erase(0, idx+2);

            idx = reqString.find(" ");
            if ( idx == std::string::npos )
            {
                std::cout<<"Bad first line : "<<reqString<<std::endl;
                return;
            }

            m_method = reqString.substr(0, idx);
            reqString = reqString.substr(idx+1);
            idx = reqString.find(" ");
            if ( idx == std::string::npos )
            {
                std::cout<<"Bad first line of request : "<< reqString << std::endl;
                return;
            }

            m_url = reqString.substr(0,idx);
            m_req_version = reqString.substr(idx+1);
            idx = m_req_version.find("/");
            if ( idx == std::string::npos )
            {
                std::cout<<"Bad first line of request : "<<reqString<<std::endl;
                return;
            }

            m_req_version = m_req_version.substr(idx+1);
            parseHeaders(m_headers, reqHeaders);

            //start_connect();
        }
    }
    else
    {
        shutdown();
    }
}


void Connection::parseHeaders(const std::string & h, headersMap & hm)
{
    std::string str(h);
    std::string::size_type idx;
    std::string t;

    while ( (idx=str.find("\r\n")) != std::string::npos )
    {
        t = str.substr(0, idx);
        str.erase(0, idx+2);
        if ( t == "" )
            break;
        idx = t.find(": ");
        if ( idx == std::string::npos )
        {
            std::cout<<"Bad header line: "<<t<<std::endl;
            break;
        }

        hm.insert(std::make_pair(t.substr(0, idx), t.substr(idx+2)));
    }
} //parseHeaders


void Connection::shutdown()
{
    std::cout<<"Closing socket..."<<std::endl;
    socket_.close();
    //bsocket_.close();
} //shutdown

常见的.h

#ifndef COMMON_H
#define COMMON_H

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include <boost/regex.hpp>

#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>

#include <iostream>
#include <string>
#include <boost/utility.hpp>

namespace ba=boost::asio;
namespace bs=boost::system;

typedef boost::shared_ptr<ba::ip::tcp::socket> socket_ptr;
typedef boost::shared_ptr<ba::io_service> io_service_ptr;

#endif

链接器错误是

1>------ Rebuild All started: Project: yql_asio, Configuration: Debug Win32 ------
1>Build started 20-03-2013 AM 1:06:33.
1>InitializeBuildStatus:
1>  Touching "Debug\yql_asio.unsuccessfulbuild".
1>ClCompile:
1>  stdafx.cpp
1>  yql_conn.h
1>  Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:
1>  - add -D_WIN32_WINNT=0x0501 to the compiler command line; or
1>  - add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.
1>  Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
1>  yql_conn.cpp
1>  Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:
1>  - add -D_WIN32_WINNT=0x0501 to the compiler command line; or
1>  - add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.
1>  Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
1>  yql_asio.cpp
1>  Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:
1>  - add -D_WIN32_WINNT=0x0501 to the compiler command line; or
1>  - add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.
1>  Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
1>  Yql.cpp
1>  Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:
1>  - add -D_WIN32_WINNT=0x0501 to the compiler command line; or
1>  - add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.
1>  Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
1>  Generating Code...
1>Debug\yql_conn.obj : warning LNK4042: object specified more than once; extras ignored
1>Yql.obj : error LNK2019: unresolved external symbol "public: __thiscall Connection::Connection(class boost::asio::io_service &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0Connection@@QAE@AAVio_service@asio@boost@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "public: __thiscall Yql::Yql(class boost::asio::io_service &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0Yql@@QAE@AAVio_service@asio@boost@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
1>Yql.obj : error LNK2019: unresolved external symbol "public: void __thiscall Connection::start(void)" (?start@Connection@@QAEXXZ) referenced in function "public: void __thiscall Yql::GetResponse(void)" (?GetResponse@Yql@@QAEXXZ)
1>C:\Users\asit\Documents\Visual Studio 2010\Projects\yql_asio\Debug\yql_asio.exe : fatal error LNK1120: 2 unresolved externals
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:31.88
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

请帮我修复错误。

4

1 回答 1

1

您有两个 LNK2019 错误。第一个说它找不到 2 参数连接构造函数。第二个说它无法从连接类中找到启动方法。还有一个 LNK4042 警告。此警告表明您意识到链接到 Connection 类的问题,该类在 yql_conn.h 和 yql_conn.cpp 中定义,然后您尝试显式链接到该文件。您的显式链接可能是正确的,但由于有两个版本浮动,它选择了另一个并忽略了显式链接。这可以解释为什么您会收到此警告。

因此,我怀疑问题出在版本控制中,或者您正在选择另一个由其他人编写的 Connection 类。您可能在其他地方定义了连接类 - 在不同的文件或相同的文件但在不同的文件夹中。该文件的旧版本可能没有正确定义 Connection 构造函数,也没有定义 start 方法 - 这将解释未解决的链接错误。因此,您可以尝试访问 yql.h 或 yql.cpp 并右键单击 Connection。选择转到定义。如果它带您进入具有 start 定义的文件,并且构造函数看起来正确,则尝试再次右键单击 Connection 并选择 Find All References。您也可以尝试在所有文件中搜索 Connection。

于 2013-03-19T21:46:11.263 回答