【C++】GET、POST网络请求boost.asio实现

前言

想找一个轻量级、跨平台的C++网络请求函数库,然而折腾了一天也不如意。老规矩,没找到就自己动手吧。

本篇代码以GET、POST请求的方式封装了boost.asio提供的socket网络连接,返回的是std::string类型。

如果获取的json字符串之类,直接用就可以了。下一篇将奉上图片等格式的本地保存功能,尽情期待。

当然,关于boost的安装和配置还是要费一番周折的,本人用的是Mac系统,具体安装配置过程可参考:

http://blog.csdn.net/ym19860303/article/details/10155751


博文首发:http://blog.csdn.net/duzixi


源代码


NetworkRequest.h

//
//  NetworkRequest.h
//
//  Created by duzixi.com on 15/8/25.
//

#include <stdio.h>
#include <iostream>
#include <fstream>
#include "json.h"
#include "boost/asio.hpp"

using namespace std;

/// GET请求
string GetRequest(char* host, char* path);
string GetRequest(string url);

/// POST请求
string PostRequest(char* host, char* path, string form);


NetworkRequest.cpp

//
//  NetworkRequest.cpp
//
//  Created by duzixi.com on 15/8/25.
//

#include "NetworkRequest.h"

using namespace std;
using boost::asio::ip::tcp;

/// POST请求
string PostRequest(char* host, char* path, string form)
{
    long length = form.length();
    
    // 声明Asio基础: io_service(任务调度机)
    boost::asio::io_service io_service;
    
    // 获取服务器终端列表
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(host,"http");
    tcp::resolver::iterator iter = resolver.resolve(query);
    
    // 尝试连接每一个终端,直到成功建立socket连接
    tcp::socket socket(io_service);
    boost::asio::connect(socket, iter);
    
    // 构建网络请求头
    // 指定 "Connection: close" 在获取应答后断开连接,确保获文件全部数据。
    boost::asio::streambuf request;
    ostream request_stream(&request);
    request_stream << "POST " << path << " HTTP/1.1\r\n";
    request_stream << "Host: " << host << "\r\n";
    request_stream << "Accept: */*\r\n";
    request_stream << "Content-Type:application/x-www-form-urlencoded\r\n";
    request_stream << "Content-Length: " << length << "\r\n";
    request_stream << "Connection: close\r\n\r\n"; // 注意这里是两个空行
    request_stream << form; //POST 发送的数据本身不包含多于空行
    
    // 发送请求
    boost::asio::write(socket, request);
    
    // 读取应答状态. 应答缓冲流 streambuf 会自动增长至完整的行
    // 该增长可以在构造缓冲流时通过设置最大值限制
    boost::asio::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");
    
    // 检查应答是否OK.
    istream response_stream(&response);// 应答流
    string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    string status_message;
    getline(response_stream, status_message);
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
        printf("无效响应\n");
    }
    if (status_code != 200)
    {
        printf("响应返回 status code %d\n", status_code);
    }
    
    // 读取应答头部,遇到空行后停止
    boost::asio::read_until(socket, response, "\r\n\r\n");
    
    // 显示应答头部
    string header;
    int len = 0;
    while (getline(response_stream, header) && header != "\r")
    {
        if (header.find("Content-Length: ") == 0) {
            stringstream stream;
            stream << header.substr(16);
            stream >> len;
        }
    }
    
    long size = response.size();
    
    if (size > 0) {
        // .... do nothing
    }
    
    // 循环读取数据流,直到文件结束
    boost::system::error_code error;
    while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
    {
        // 获取应答长度
        size = response.size();
        if (len != 0) {
            cout << size << "  Byte  " << (size * 100) / len << "%\n";
        }
        
    }
    if (error != boost::asio::error::eof)
    {
        throw boost::system::system_error(error);
    }
    
    cout << size << " Byte 内容已下载完毕." << endl;
    
    // 将streambuf类型转换为string类型返回
    istream is(&response);
    is.unsetf(ios_base::skipws);
    string sz;
    sz.append(istream_iterator<char>(is), istream_iterator<char>());
    
    // 返回转换后的字符串
    return sz;
}

/// GET请求
string GetRequest(char* host, char* path)
{
    // 声明Asio基础: io_service(任务调度机)
    boost::asio::io_service io_service;
    
    // 获取服务器终端列表
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(host,"http");
    tcp::resolver::iterator iter = resolver.resolve(query);

    // 尝试连接每一个终端,直到成功建立socket连接
    tcp::socket socket(io_service);
    boost::asio::connect(socket, iter);
    
    // 构建网络请求头.
    // 指定 "Connection: close" 在获取应答后断开连接,确保获文件全部数据。
    boost::asio::streambuf request;
    ostream request_stream(&request);
    request_stream << "GET " << path << " HTTP/1.1\r\n";
    request_stream << "Host: " << host << "\r\n";
    request_stream << "Accept: */*\r\n";
    request_stream << "Connection: close\r\n\r\n";
    
    // 发送请求
    boost::asio::write(socket, request);
    
    // 读取应答状态. 应答缓冲流 streambuf 会自动增长至完整的行
    // 该增长可以在构造缓冲流时通过设置最大值限制
    boost::asio::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");
    
    // 检查应答是否OK.
    istream response_stream(&response);
    string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    string status_message;
    getline(response_stream, status_message);
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
        printf("响应无效\n");
    }
    if (status_code != 200)
    {
        printf("响应返回 status code %d\n", status_code);
    }
    
    // 读取应答头部,遇到空行后停止
    boost::asio::read_until(socket, response, "\r\n\r\n");
    
    // 显示应答头部
    
    string header;
    int len = 0;
    while (getline(response_stream, header) && header != "\r")
    {
        if (header.find("Content-Length: ") == 0) {
            stringstream stream;
            stream << header.substr(16);
            stream >> len;
        }
    }
    
    long size = response.size();

    if (size > 0) {
        // ... do nothing ...
    }
    
    boost::system::error_code error;  // 读取错误
    
    // 循环读取数据流,直到文件结束
    while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
    {
        // 获取应答长度
        size = response.size();
        if (len != 0) {
            cout << size << "  Byte  " << (size * 100) / len << "%" << endl;
        }
    }

    // 如果没有读到文件尾,抛出异常
    if (error != boost::asio::error::eof)
    {
        throw boost::system::system_error(error);
    }
    
    cout << size << " Byte 内容已下载完毕." << endl;
    
    // 将streambuf类型转换为string类型,并返回
    istream is(&response);
    is.unsetf(ios_base::skipws);
    string sz;
    sz.append(istream_iterator<char>(is), istream_iterator<char>());
    
    return sz;
}

/// GET请求(重载)
string GetRequest(string url)
{
    size_t index;
    
    // 去掉url中的协议头
    if (url.find("http://") != string::npos ) {
        url = url.substr(7);
    }
    printf("url:%s\n", url.c_str());
    
    // 截取host字符串
    index = url.find("/");
    char* host = new char[index];
    strcpy(host, url.substr(0, index).c_str());
    
    // 截取urlPath字符串
    char* urlPath = new char[url.length() - index + 1];
    strcpy(urlPath, url.substr(index, url.length() - index).c_str());
    
    return GetRequest(host, urlPath);
}

后语

以前GET、POST请求都是用封好的方法,现在感觉真是太方便了。

第一次自己动手拼接协议头,疏漏之处难免,敬请指正。


你可能感兴趣的:(C++,网络请求,get,post,Boost.Asio)