C++简单实现HTTP协议的GET/POST请求

HTTP(超文本传输协议)是一种客户端与服务端的传输协议,最早用于浏览器和服务器之间的通信,后来因为其使用灵活、方便等特点,广泛用于客户端与服务端的通信。文章将简单介绍HTTP协议,同时以C++方式分别实现HTTP GET、POST 请求。



HTTP报文的请求格式


HTTP请求报文的一般格式由4部分组成:请求行、请求头、空行、请求参数。如下图所示:

image.png

请求行:包含3部分内容:请求方法,URL,协议版本。形式如:GET /?aaa=1 HTTP/1.1。请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS等。URL指请求服务端的地址,可以是相对地址或域名形式的绝对地址。协议版本主要有HTTP/1.1 HTTP/1.0 HTTP/0.9,后面两种已很少使用了。


请求头:以key/value形式成对表示头部参数,以英文冒号分隔。key名称的约定写法为Key,Key-Name,自定义key名称一般以“X-”开头。如php的声明“X-Powered-By:PHP/5.5.4-1”


空行:用来标识请求头部的数据已结束。


请求参数:可选项,这块内容只在POST方式下使用,作为POST的数据表示区域。使用这块内容,要在请求头部以Content-Length声明请求数据长度,以Content-Type声明请求数据类型。


对于请求的完整参数格式,我们可以利用Fiddler抓包工具就可以直观的看到,如图:

image.png


HTTP GET请求

HTTP GET方式是把请求参数放到HTTP请求报文的请求行URL中,所以请求行就是“GET /?aaa=1&bbb=2 HTTP/1.1\r\n”。URL最大长度通常浏览器取255,这和文件路径最大长度有关。虽然HTTP允许更大长度,但不建议怎么做,如果太长了,可以考虑换成POST方式。

下面直接贴出我自己测试的代码:

/**********
* C++简单实现HTTP协议的GET/POST请求
**********/
#include <iostream>
#include <string>
#include <winsock2.h>

using namespace  std;

//发送http请求
void sendHttpRequest(string hostName, string api, string param);
//通过域名获取Ip地址
string getIpAddr(string hostName);

int main(){
    cout << "程序开始执行" << endl;

    sendHttpRequest("www.sindsun.com", "/articles/8/130", "test=1");

    cout << "程序执行完毕" << endl;
    return 0;
}

void sendHttpRequest(string hostName, string api, string param){
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);

    string ip = hostName;
    //检查是域名还是ip,如果是域名则,获取ip地址
    for(unsigned i=0; i<hostName.length(); i++){
        if(hostName[i] < '0' || hostName[i] > '9'){
            ip = getIpAddr(hostName);
            break;
        }
    }
    //地址与端口参数
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(80);
    sin.sin_addr.s_addr = inet_addr(ip.c_str());
    //初始化socket
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock == SOCKET_ERROR){
        cout << "初始化socket失败" << endl;
        exit(-1);
    }
    //连接服务器
    int connectStatus = connect(sock, (const struct sockaddr*)&sin, sizeof (sin));
    if(connectStatus == INVALID_SOCKET){
        cout << "连接服务器失败" << endl;
        exit(-1);
    }
    //初始化发送的数据
    string data = "";
    data += "GET " + api + " HTTP/1.1\r\n";
    //HEADER信息
    data += "Host: " + hostName + "\r\n";
    data += "Connection: keep-alive\r\n";
    data += "Content-Length: " + to_string(param.length());
    data += "\r\n";
    data += "Cache-Control: max-age=0\r\n";
    data += "Upgrade-Insecure-Requests: 1\r\n";
    data += "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n";
    data += "Content-Type:  application/x-www-form-urlencoded\r\n";
    data += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n";
    data += "Accept-Encoding: gzip, deflate, br\r\n";
    data += "Accept-Language: zh-CN,zh;q=0.9\r\n";
    //内容信息
    data += "\r\n";
    data += param;

    //将string转为c字符串,不然请求会失败
    int len = data.length() + 1;
    char content[len];
    memset(content, 0, len);
    strcpy(content, data.c_str());

    //cout << data << endl; exit(0);
    //发送请求
    int sendStatus = send(sock, content, sizeof (content), 0);
    if(sendStatus == -1){
        cout << "发送请求失败" << endl;
        exit(-1);
    }

    //获取返回的信息
    char recvStr[4096] = {0};
    memset(recvStr, 0, 4096);
    int recvStatus = 0;
    recvStatus = recv(sock, recvStr, 4096, 0);
    if(recvStatus == -1){
        cout << "接收数据失败" << endl;
        exit(-1);
    }
    cout << recvStr << endl;

    closesocket(sock);
    WSACleanup();

    exit(0);
}

string getIpAddr(string hostName){
    struct hostent *hostAddr = gethostbyname(hostName.c_str());
    if(hostAddr == nullptr){
        cout << "无法找到主机" << endl;
        exit(0);
    }
    string ipv4 = "";

    for(int i=0; hostAddr->h_addr_list[i]; i++){
        struct in_addr tmpAddr = *reinterpret_cast<struct in_addr*>(hostAddr->h_addr_list[i]);
        string ch = inet_ntoa(tmpAddr);
        ipv4 += ch;
    }
    return ipv4;
}


对于POST或者其它请求方式与GET同理,只是携带参数不同罢了,日记中就不再重复写了,看一下最后实现的效果

QQ截图20200530001820.jpg


部分内容借鉴自:https://www.jianshu.com/p/4442d7d3efd7


版权声明: 此文为本站源创文章[或由本站编辑从网络整理改编],
转载请备注出处:
[狂码一生] https://www.sindsun.com/articles/16/134
[若此文确切存在侵权,请联系本站管理员进行删除!]


--THE END--