我正在尝试使用 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 ==========
请帮我修复错误。