HTTP(超文本传输协议)是一种客户端与服务端的传输协议,最早用于浏览器和服务器之间的通信,后来因为其使用灵活、方便等特点,广泛用于客户端与服务端的通信。文章将简单介绍HTTP协议,同时以C++方式分别实现HTTP GET、POST 请求。
HTTP报文的请求格式
HTTP请求报文的一般格式由4部分组成:请求行、请求头、空行、请求参数。如下图所示:
请求行:包含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抓包工具就可以直观的看到,如图:
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同理,只是携带参数不同罢了,日记中就不再重复写了,看一下最后实现的效果
部分内容借鉴自:https://www.jianshu.com/p/4442d7d3efd7
版权声明:
此文为本站源创文章[或由本站编辑从网络整理改编],
转载请备注出处:
[狂码一生]
https://www.sindsun.com/articles/16/134
[若此文确切存在侵权,请联系本站管理员进行删除!]
--THE END--