如何使用 Boost::Asio 访问 Web 服务?

Posted

技术标签:

【中文标题】如何使用 Boost::Asio 访问 Web 服务?【英文标题】:How to access web service using Boost::Asio? 【发布时间】:2017-06-06 09:59:02 【问题描述】:

我想知道是否有可能使用 boost asio 库访问网络服务。

我尝试了从 boost asio 文档中获得的以下代码(在 ios,C++11 中)。 但它抛出了以下内容。

try

    boost::asio::io_service io_service;
    std::string address = "http://www.thomas-bayer.com/axis2/services/BLZService/";

    // Get a list of endpoints corresponding to the server name.
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(address,boost::asio::ip::resolver_query_base::numeric_service);


    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    endpoint_iterator->host_name() = "www.thomas-bayer.com/axis2/services/BLZService/";

    std::cout<<"Print Query --"<<std::endl;

    // Try each endpoint until we successfully establish a connection.
    tcp::socket socket(io_service);

    boost::asio::connect(socket, endpoint_iterator);

    // Form the request. We specify the "Connection: close" header so that the
    // server will close the socket after transmitting the response. This will
    // allow us to treat all data up until the EOF as the content.
    boost::asio::streambuf request;
    std::ostream request_stream(&request);
    request_stream << "POST: HTTP/1.0\r\n";
    request_stream << "Host: " << address << "\r\n";
    request_stream << "Accept: */*\r\n";
    request_stream << "Connection: close\r\n\r\n";

    // Send the request.
    boost::asio::write(socket, request);

    // Read the response status line. The response streambuf will automatically
    // grow to accommodate the entire line. The growth may be limited by passing
    // a maximum size to the streambuf constructor.

    boost::asio::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");

    // Check that response is OK.
    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    std::string status_message;
    std::getline(response_stream, status_message);

    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    
        std::cout << "Invalid response\n";
        return;
    
    if (status_code != 200)
    
        std::cout << "Response returned with status code " << status_code << "\n";
        return;
    

    // Read the response headers, which are terminated by a blank line.
    boost::asio::read_until(socket, response, "\r\n\r\n");

    // Process the response headers.
    std::string header;
    while (std::getline(response_stream, header) && header != "\r")
        std::cout << header << "\n";
    std::cout << "\n";

    // Write whatever content we already have to output.
    if (response.size() > 0)
        std::cout << &response;

    // Read until EOF, writing data to output as we go.
    boost::system::error_code error;
    while (boost::asio::read(socket, response,
                             boost::asio::transfer_at_least(1), error))
        std::cout << &response;
    if (error != boost::asio::error::eof)
        throw boost::system::system_error(error);

catch (std::exception& e)

    std::cout << "Exception: " << e.what() << "\n";

例外:连接:无法分配请求的地址

或者

异常:解决:找不到主机(权威)

代码有什么问题?还是我做错了?

谢谢

【问题讨论】:

我认为问题与访问Web服务无关,相反,我认为上面的代码无法解析主机名。也许这两个链接可以帮助你。 Boost not working over network 和这个boost asio: “host not found (authorative)” 解析器使用主机名而不是 URL。使用thomas-bayer.com 作为解析器。 tcp::resolver::query query(address,boost::asio::ip::resolver_query_base::numeric_service);解决主机未找到错误但产生无法分配请求的地址。基本上我得到上述错误之一。我将尝试使用您提供的第一个链接并回来。谢谢 嗨@JohnTracid 我已尝试将网址设置为如下tcp::resolver::query query("thomas-bayer.com", boost::asio::ip::resolver_query_base::numeric_service); 但我无法分配请求地址错误。谢谢 【参考方案1】:

名称解析失败,因为您混淆了什么是请求、URL、协议、主机名和 IP 地址。

在FQDN 上提出请求。除非您知道,否则您需要提供服务。在这种情况下,服务遵循协议¹,`http:// 通常在端口 80 上提供:

std::string const address = "www.thomas-bayer.com";
tcp::resolver::query query(address, "80", boost::asio::ip::resolver_query_base::numeric_service);

请注意,在大多数系统上,您可以等效地使用:

std::string const address = "www.thomas-bayer.com";
tcp::resolver::query query(address, "http");

看到http://www.thomas-bayer.com 部分去了哪里?

现在/axis2/services/BLZService/ 是查询路径,就像您在 GET 请求中所写的那样:

request_stream << "GET /axis2/services/BLZService/ HTTP/1.1\r\n";

注意事项:

    POST 不是标头(所以没有冒号!)它是 HTTP“动词”(GET、POST、DELETE、PUT...)

    “主机”标头主机名:

     request_stream << "Host: " << address << "\r\n";
    

    是正确的 iff address 确实是主机的逻辑名称

    这样设置主机名:

     endpoint_iterator->host_name() = "www.thomas-bayer.com/axis2/services/BLZService/";
    

    是我以前从未见过的东西,我不确定它应该实现什么。也许只是错了?

¹ 按照惯例,可能是其他

修复

#include <boost/asio.hpp>
#include <iostream>
using boost::asio::ip::tcp;

void test() try 
    boost::asio::io_service io_service;
    std::string const address = "www.thomas-bayer.com";

    // Get a list of endpoints corresponding to the server name.
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(address, "80", boost::asio::ip::resolver_query_base::numeric_service);

    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    std::cout << "Print Query --" << std::endl;

    // Try each endpoint until we successfully establish a connection.
    tcp::socket socket(io_service);

    boost::asio::connect(socket, endpoint_iterator);

    // Form the request. We specify the "Connection: close" header so that the
    // server will close the socket after transmitting the response. This will
    // allow us to treat all data up until the EOF as the content.
    boost::asio::streambuf request;
    std::ostream request_stream(&request);
    request_stream << "GET /axis2/services/BLZService HTTP/1.0\r\n";
    request_stream << "Host: " << address << "\r\n";
    request_stream << "Accept: */*\r\n";
    request_stream << "Connection: close\r\n\r\n";

    // Send the request.
    boost::asio::write(socket, request);

    // Read the response status line. The response streambuf will automatically
    // grow to accommodate the entire line. The growth may be limited by
    // passing a maximum size to the streambuf constructor.
    boost::asio::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");

    // Check that response is OK.
    std::istream response_stream(&response);
    std::string http_version, status_message;
    unsigned int status_code;
    std::getline(response_stream >> http_version >> status_code, status_message);

    if (!response_stream || http_version.substr(0, 5) != "HTTP/") 
        std::cout << "Invalid response\n";
        return;
    
    if (status_code != 200) 
        std::cout << "Response returned with status code " << status_code << "\n";
        return;
    

    // Read the response headers, which are terminated by a blank line.
    boost::asio::read_until(socket, response, "\r\n\r\n");

    // Process the response headers.
    std::string header;
    while (std::getline(response_stream, header) && header != "\r")
        std::cout << header << "\n";
    std::cout << "\n";

    // Write whatever content we already have to output.
    if (response.size() > 0)
        std::cout << &response;

    // Read until EOF, writing data to output as we go.
    boost::system::error_code error;
    while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
        std::cout << &response;
    if (error != boost::asio::error::eof)
        throw boost::system::system_error(error);
 catch (std::exception &e) 
    std::cout << "Exception: " << e.what() << "\n";


int main()  test(); 

【讨论】:

您好其实我尝试将我的请求发布到网络服务,但是为什么你改为 GET 呢? @Kid 为什么不呢?我只是忘记改回来了。我倾向于在发布答案之前测试我的代码,并且我不会向我一无所知的服务器发送 POST 请求。 哦,好的。它返回方法`GET -- 响应返回状态码500`和POST -- Exception: read_until: End of file。但我还没有添加实际的 xml 请求。可能这会导致这些错误。我目前正在研究它。谢谢

以上是关于如何使用 Boost::Asio 访问 Web 服务?的主要内容,如果未能解决你的问题,请参考以下文章

Boost.Asio 的同步和并发数据结构模式

多线程应用程序上的 boost::asio::ssl 访问冲突

Boost Asio 访问 asio::ip::address 底层数据和字节顺序

C++ Boost ASIO:如何读取/写入超时?

如何使用 boost::asio::serial_port 支持硬件流控制?

boost::asio: “strand”类型的同步原语有啥名字吗?