C++ 封装HTTP Client
- 简介
- HTTP请求头封装
- HTTP请求头基本格式
- post请求头
- 封装post请求头
- HTTP Client 以及HTTP Server交互
- HTTP消息解析
- TCP封装HTTP Client的坑。
- Transfer-Encoding: chunked简介
- 注意
简介
C++封装HTTP协议底层是通过TCP协议实现。
HTTP协议是应用层协议,TCP协议是底层的传输层协议。
使用TCP协议封装HTTP Client进行通信,那么需要封装HTTP请求头、接收HTTP Server的消息以及HTTP消息解析。
HTTP请求头封装
HTTP请求头基本格式
<HTTP METHOD> <URI> <HTTP VERSION>
<HEADER FIELD 1>: <VALUE 1>
<HEADER FIELD 2>: <VALUE 2>
<HEADER FIELD 3>: <VALUE 3>
...
<HEADER FIELD N>: <VALUE N>
<REQUEST BODY>
post请求头
POST /api/login HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 27
{"username":"johndoe","password":"password123"}
封装post请求头
string HttpClient::postRequest(string host, string path, string url, string head_content, string body_content)
{
stringstream stream;
stream << "POST " << path << url;
stream << " HTTP/1.1\r\n";
stream << "Host: " << host << "\r\n";
stream << "Content-Type:application/json\r\n";
stream << "Content-Length:" << body_content.length() << "\r\n";
stream << head_content;
stream << "Connection:close\r\n\r\n";
stream << body_content << "\r\n";
return socketHttp(host, stream.str());
}
我们对HTTP POST请求头的封装就完毕了,对HTTP请求的其余方法GET、PUT、DELETE等方法的封装如上就不一一列举了然后就是与HTTP Server通信了。
HTTP Client 以及HTTP Server交互
string HttpClient::socketHttp(string host, string request)
{
sockaddr_in addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_port = htons(HTTP_PORT);
hostent* hptr = NULL;
hptr = gethostbyname(host.c_str());
if (hptr == NULL) {
std::cout << "gethostbyname default!" << std::endl;
return "";
}
memcpy((char*)&addr.sin_addr.s_addr, (char *)hptr->h_addr, hptr->h_length);
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
std::cout << "Create a Socket Error: " << WSAGetLastError() << std::endl;
return "";
}
int nRecv = 204800;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecv, sizeof(int));
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&nRecv, sizeof(int));
if (SOCKET_ERROR == connect(sockfd, (SOCKADDR*)&addr, sizeof(addr))) {
std::cout << "Connect Http Error: " << WSAGetLastError() << std::endl;
AfxMessageBox(L"服务器连接失败");
closesocket(sockfd);
return "";
}
send(sockfd, request.c_str(), request.size(), 0);
memset(m_recvbuf, 0, 204800);
int offset = 0, rc = 0;
wchar_t wcText[204800];
while (rc = recv(sockfd, m_recvbuf+offset, 204800-offset, 0)) {
if (rc <= 0) {
int id = WSAGetLastError();
break;
}
offset += rc;
}
closesocket(sockfd);
m_recvbuf[offset] = 0;
string str1 = m_recvbuf;
return str1;
}
通过调入如上两个函数就能实现与HTTP Server交互。
HTTP消息解析
HTTP消息解析,根据通信协议的不同而不同,所以具体解析这里就不列举了。
TCP封装HTTP Client的坑。
Transfer-Encoding: chunked简介
在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。
每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。
最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。
注意
当HTTP Server采用Transfer-Encoding: chunked方式传输数据时会出现这种问题。
在socketHttp(…)中
while (rc = recv(sockfd, m_recvbuf+offset, 204800-offset, 0)) {
if (rc <= 0) {
int id = WSAGetLastError();
break;
}
offset += rc;
}
当HTTP服务发送的消息,接收时如果发送的body分为了两块,那么接收消息的时候,body的前面都会带一个body的十六进制的字节数,我们需要把这个值处理掉才能接收到正确的数据。
如下图:
红色标红就为body长度需要自行处理才能的到正确的数据。
分享相关封装HTTP Client心得。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)