关于boost::asio::read_until()之后再boost::asio::read()可能出现的问题

    这两日在工作中,新接手前人留下的代码,在修改bug过程中, 发现当报文内容很短时,在boost::asio::read_until()之后调用boost::asio::read()未读出预期的数据。部分代码如下:

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

    std::istream    response_stream(&response);

    String  http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;

    String  status_message;
    std::getline(response_stream, status_message);

    if(!response_stream || http_version.substr(0, 5) != "HTTP/"){
        return 500;//"Invalid response\n"
    }

    if(status_code != 200){
        return status_code;
    }

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

    String header, content_len;
    int	   len = 0;
    int    body_len = 0;
    while(std::getline(response_stream, header) && header != "\r"){
        //do something ...
	if((len = header.find("Content-Length:")) != std::string::npos){
	    content_len = header.substr(len + 16);//strlen("Content-Length: ")
	    body_len = atoi(content_len.c_str());
	}
    }

    if(body_len == 0){
        body_len = 10240;
    }

    while(boost::asio::read(socket, response,
                            boost::asio::transfer_at_least(1), ec)){
        std::ostringstream ss2;
        ss2<<&response;

        httpbody.append(ss2.str());

        body_len -= ss2.str().length();
        if(body_len <= 0 || ec == boost::asio::error::eof){
            break;
        }
..................................
    }
 

    在第二次read_until

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

后,&response指向了"\r\n\r\n"后的数据,为期望数据开始的地方,但其中的数据大小为512-("\r\n\r\n")这之前的数据。

 

    当进行再次read,

while(boost::asio::read(socket, response,
                            boost::asio::transfer_at_least(1), ec))
能够读取报文(TCP) data段 512字节以后的内容。response string流还是指向"\r\n\r\n"之后的内容。并且重新为最多512字节数据。(每次循环最多读512字节?)

但如果(TCP)报文数据段小于512字节(HTTP头+数据),那么该段代码在read时就会返回0,读不到数据。while中对数据的处理就未进行。


    为了完善功能,先将response中剩余的数据存储起来,判断是否还有数据,再进行read读取。如下:

std::ostringstream ss;
ss<<&response;
httpbody.append(ss.str());
body_len -= ss.str().length();
    while((body_len>0)&&boost::asio::read(socket, response,
                            boost::asio::transfer_at_least(1), ec)){
        std::ostringstream ss2;
        ss2<<&response;

        httpbody.append(ss2.str());

        body_len -= ss2.str().length();
        if(body_len <= 0 || ec == boost::asio::error::eof){
            break;
        }
..................................
    }

    本人没有boost库使用经验,查了些资料,没有看到太多关于asio::read的用法,讲得较多的是asio::read_some()和asio::async_read()。也不知道它们能避免上述问题不,目前问题已解决,不做深入研究了。

Boost.Asio 是一个 C++ 网络编程库,可以用于开发高性能的网络应用程序。使用 Boost.Asio,你可以轻松地实现异步网络编程,包括 TCP、UDP、SSL、HTTP 等协议。 下面是一个简单的 Boost.Asio 示例,演示如何使用异步 TCP 客户端: ```cpp #include <boost/asio.hpp> #include <iostream> using boost::asio::ip::tcp; int main() { boost::asio::io_context io_context; tcp::resolver resolver(io_context); tcp::resolver::results_type endpoints = resolver.resolve("www.baidu.com", "80"); tcp::socket socket(io_context); boost::asio::connect(socket, endpoints); boost::asio::streambuf request; std::ostream request_stream(&request); request_stream << "GET / HTTP/1.1\r\n"; request_stream << "Host: www.baidu.com\r\n"; request_stream << "Connection: close\r\n\r\n"; boost::asio::async_write(socket, request, [](const boost::system::error_code& error, std::size_t bytes_transferred) { if (!error) { std::cout << "Sent " << bytes_transferred << " bytes." << std::endl; } else { std::cerr << "Error: " << error.message() << std::endl; } }); boost::asio::streambuf response; boost::asio::async_read_until(socket, response, "\r\n", [&response](const boost::system::error_code& error, std::size_t bytes_transferred) { if (!error) { std::istream response_stream(&response); std::string http_version; response_stream >> http_version; std::cout << "HTTP version: " << http_version << std::endl; } else { std::cerr << "Error: " << error.message() << std::endl; } }); io_context.run(); return 0; } ``` 这个示例中,我们使用了 Boost.Asio 的异步 API,首先通过 DNS 解析获取到百度的 IP 地址,然后连接到百度的 80 端口,发送一个 HTTP GET 请求,最后读取响应并输出 HTTP 版本号。 需要注意的是,Boost.Asio 的异步 API 需要使用 io_context 来驱动事件循环,我们在最后调用 io_context.run() 来启动事件循环。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值