@[TOC] 目录
# 摘要
本文实现c++使用libcurl开源库,对websocket服务的客服端post请求遇到的一些坑的反思记录。
libcurl版本:curl-7.76.0.zip
libcurl下载、编译、函数说明、接口调用可以参照如下网址,不做过多的画蛇添足。
https://www.cnblogs.com/heluan/p/10177475.html
备注:可以参考如下链接方式,搭建postman的mock server服务端测试post请求。
https://blog.csdn.net/hbiao68/article/details/102757845
遇到的问题列表如下:
1.post发送服务端未接收到数据
2.多字节工程与Http要求UTF_8编码格式冲突
3.无法接受post返回数据
4.如何发送接受json数据,接受到的数据转JSON失败的坑
# 1.post发送服务端未接收到数据
原因:未指定发送数据长度
解决方案:设置curl_easy_setopt函数的CURLOPT_POSTFIELDS、CURLOPT_POSTFIELDSIZE字段的值。
已知std::string requestUTF8("post send data");
字段名 | 意义 | 参数案例 |
CURLOPT_POSTFIELDS | 发送的数据字段 | requestUTF8.data() |
CURLOPT_POSTFIELDSIZE | 发送数据的长度 | requestUTF8.size() |
代码示例
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestUTF8.data());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, requestUTF8.size());
# 2.多字节工程与Http要求UTF_8编码格式冲突
原因:VC工程为Use Multi-Byte Character Set多字节,而Http发送接受编码格式为UTF_8
解决方案
将ANSCII格式的string类型转成UTF_8格式的string。
std::string -->std::wstring(UNICODE)-->UTF_8
前置条件:Windows API函数需要引入Windows头文件 #include <Windows.h>
inline std::wstring AsciiToUnicode(const std::string& str)
{
// 预算-缓冲区中宽字节的长度
int unicodeLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
// 给指向缓冲区的指针变量分配内存
wchar_t *pUnicode = (wchar_t*)malloc(sizeof(wchar_t)*unicodeLen);
// 开始向缓冲区转换字节
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, pUnicode, unicodeLen);
std::wstring ret_str = pUnicode;
free(pUnicode);
return ret_str;
}
inline std::string UnicodeToUtf8(const std::wstring& wstr)
{
// 预算-缓冲区中多字节的长度
int ansiiLen = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
// 给指向缓冲区的指针变量分配内存
char *pAssii = (char*)malloc(sizeof(char)*ansiiLen);
// 开始向缓冲区转换字节
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, pAssii, ansiiLen, nullptr, nullptr);
std::string ret_str = pAssii;
free(pAssii);
return ret_str;
}
inline std::string AsciiToUtf8(const std::string& str)
{
return UnicodeToUtf8(AsciiToUnicode(str));
}
# 3.无法接受post返回数据
原因:1.未设置返回接受数据和返回写入回调函数
2.写入回调函数有问题
解决方案:
第一点问题可以设置curl_easy_setopt函数的CURLOPT_WRITEFUNCTION、CURLOPT_WRITEDATA字段的值。
已知std::ostringstream writedata;
字段名 | 意义 | 参数案例 |
CURLOPT_WRITEFUNCTION | 设置返回数据写入的回调函数 | write_data |
CURLOPT_WRITEDATA | 设置返回数据写入的变量 | writedata |
write_data为函数指针,可参照如下代码,建议最好是以字符流操作。
尝试过返回string*和char*指针存在崩溃或无返回情况。
ostringstream记得加头文件#include<sstream>
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
std::string html_data(reinterpret_cast<const char*>(buffer), size * nmemb);
*(reinterpret_cast<std::ostringstream*>(userp)) << html_data;
return size * nmemb;
}
调用案例:
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writedata);
# 4.如何发送接受json数据,接受到的数据转JSON失败的坑
原因:接受数据按照字节串流不满足JsonCPP库的parse函数调用
解决方案:需要将字节流转成UTF8字符数组
具体是ostringstream.str()得到字节流(byte类型)--》UNICODE--》UTF8格式字节流
字符编码笔记:ASCII,Unicode 和 UTF-8可通过如下地址学习
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
代码:已知std::string reponse;
reponse = AsciiToUtf8(writedata.str());
Json::Reader reader;
Json::Value root,body;
if (reader.parse(reponse.data(), body))
{
//your json data
}
完整示例代码如下:
bool doPost(int ack, const std::string &method, std::string &req, std::shared_ptr<std::string> &rep)
{
std::string reponse = "";
std::string request;
request.append("{");
request.append(req.data());
request.append("}");
//避免路径'\'理解成反转义
for (size_t i = 0; i<request.size(); i++) {
if (request[i] == '\\') {
request.insert(i, std::string("\\"));
i++;
}
}
std::string requestUTF8 = AsciiToUtf8(request);
std::string url = realUrl;
CURL *curl = curl_easy_init();
struct curl_slist *headers = NULL;
std::ostringstream writedata;
if (curl)
{
headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
headers = curl_slist_append(headers, "Accept: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置HTTP头
curl_easy_setopt(curl, CURLOPT_URL, url.data());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestUTF8.data());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, requestUTF8.size());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writedata);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "d:/curlposttest.cookie");
/*如果在60秒内低于30字节 / 秒,则中止*/
/*
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 10);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 2);//连接超时,这个数值如果设置太短可能导致数据请求不到就断开了
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 3000);//接收数据时超时设置,如果10秒内数据未接收完,直接退出
*/
curl_easy_perform(curl);
reponse = AsciiToUtf8(writedata.str());
reponse = reponse.substr(reponse.find_first_of('{'));
url_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = nullptr;
}
rep = std::shared_ptr<std::string>(new std::string(reponse));
return true;
}
附加
utf8转anscii方法源代码,使用到c++11宽窄字符转换,记得加头文件#include <codecvt>
std::string UnicodeToAscii(const std::wstring &str)
{
int iTextLen = WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
std::vector<char> vecText(iTextLen, '\0');
::WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, &(vecText[0]), iTextLen, NULL, NULL);
std::string strText = &(vecText[0]);
return strText;
}
std::string UTF8ToString(const std::string &utf8Data)
{
//先将UTF-8转换成Unicode
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
std::wstring wString = conv.from_bytes(utf8Data);
//在转换成string
return UnicodeToAscii(wString);
}
inline std::string Utf8ToAscii(const std::string& str)
{
return UTF8ToString(str);
}
ANSI码字节流 ---------(AsciiToUtf8方法)-------------------------》UTF8码字节流
UTF8码字节流 ---------(Utf8ToAscii方法)-------------------------》ANSI码字节流
GBK:0xB0 0xA1; Unicode-16 LE:0x4A 0x55,
Unicode-16 BE:0x55 0x4A; UTF-8:0xE5 0x95 0x8A
总结:
1.写Windows工程绕不过编码格式,对于计算机只有二进制,对于上层需要码制转换。感谢c++11宽窄字符转换,省了很大的力气。(Linux默认是UTF8不存在码制问题)
2.HTTP通信数据多采用UTF8编码,工程最好设置为UNICODE;
3.libcurl功能很强大,支持多种协议是作为客户端不错开源库。
4.c++11是c++灵魂的深造,解决长期困扰的多线程、函数指针、RAII、时间库等问题。
技术选型 c++11 + libcurl + jsoncpp
求助:望大神分享c++下httplib库编译、使用案例。万分感谢!!!