我们手游要接入uc九游进行测试,要用http向uc那边的sdk 服务器post json数据。
虽然他们提供了php,java还有c#的服务端demo,但是我们服务器是C++写的,我实在不想中间再转一层。
于是查了一些资料,发现libcurl可以用在c++里用来构造http包,然后json可以用jsoncpp这个开源api来整合,于是就直接开干!
1.下载libcurl http://curl.haxx.se/download/curl-7.40.0.zip
2.下载jsoncpp https://github.com/open-source-parsers/jsoncpp
3.实现一个http线程,接收主线程推送过来的消息,并转化为json数据,并post到目标地址,在回调里获取到返回数据。
具体实现代码
HttpParse.h
#ifndef HttpParse_h__ #define HttpParse_h__ #include <curl/curl.h> #include <string.h> #include <string> #include "ConfigHelper.hpp" #include "CodeQueue.hpp" #include "Thread.hpp" #include "TypesDefine.hpp" #include "NowTime.hpp" #include "StrStream.hpp" using namespace std; using namespace KylinLib; struct stHttpData { enum actiontype { uc_login = 1, // 登陆 uc_recharge = 2, // 充值 uc_extend = 3, // 扩展数据 }; actiontype action; //动作id,登录?注册? unsigned int uiUin; //QQ号 int iWorldid; //区id int iLoginWay; //登录来源 int iChangeValue; //变化值 stHttpData() { memset(this,0,sizeof(*this) ); } }; class CHTTPParse { CCodeQueue* m_pDataQueue; CURL* m_pCurlHandlder; public: //enum {appID = 90010006,}; enum {appID = 100705924,}; CHTTPParse(); ~CHTTPParse(); // 增加http数据 void AddOneHttpData(stHttpData::actiontype actType, unsigned int uiUin, int worldID, int changeValue = 0, int loginWay = 0); // 发送数据 int ProcessMsgSend(); static size_t OnWriteData(void* buffer, size_t size, size_t nmemb, void* lpVoid); private: // 初始化 void Init(); //Curl 初始化 void CurlInit(); //UC登录 int UCLogin(const char* szSid, const char* szAccount); //UC充值 int UCRecharge(unsigned int Roleid, int iWorldid, const char* szAccount, int iRechargeNum); //UC扩展数据 int UCSendExtend(); //发送数据 int SendHttpPost(const char* url,const char* pData); }; //处理http交互的线程 class CHTTPParseThread : public CThread { int m_iIsToBeBlocked; public: CHTTPParseThread(); public: virtual int PrepareToRun() { return 0; } virtual int Run() ; virtual int IsToBeBlocked() { return m_iIsToBeBlocked; } void SetBlockedStatus(int iStatus) { m_iIsToBeBlocked = iStatus; } }; #define CHTTPParseMgr CSingleton<CHTTPParse>::Instance() #endif // HttpParse_h__
// HttpParse.cpp
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <json/config.h> #include <json/json.h> #include "HttpParse.hpp" #include "LogAdapter.hpp" #define LUOPANGAPPID 100658092 #define DoSend(host, stat, data) CStrStream1024 strHttp; strHttp << "http://" << host << "/" << stat; return SendHttpPost(strHttp.GetStr(), data); #define InitNet() CurlInit() CHTTPParseThread::CHTTPParseThread() { m_iIsToBeBlocked =1; } // 注册 登录 支付 int CHTTPParseThread::Run() { while(true) { if (m_iRunStatus == rt_stopped) { break; } int iNewMsgCount = 0; while(true) { int iRet = CHTTPParseMgr->ProcessMsgSend(); if (iRet ==0) { iNewMsgCount++; } else { break; } } if (iNewMsgCount ==0) { usleep(1000*10); //1毫秒 } } return 0; } CHTTPParse::CHTTPParse() { m_pDataQueue =NULL; m_pCurlHandlder = NULL; Init(); } CHTTPParse::~CHTTPParse() { if (m_pCurlHandlder) { curl_easy_cleanup(m_pCurlHandlder); } curl_global_cleanup(); } // 增加上报数据 void CHTTPParse::AddOneHttpData(stHttpData::actiontype actType, unsigned int uiUin, int worldID, int changeValue /* = 0 */, int loginWay /* = 0 */) { if (NULL == m_pDataQueue) { return; } stHttpData lplData; lplData.action = actType; lplData.uiUin = uiUin; lplData.iWorldid = worldID; lplData.iChangeValue = changeValue; lplData.iLoginWay = loginWay; m_pDataQueue->PushOneCode( (const unsigned char*)&lplData,sizeof(lplData) ); } int CHTTPParse::ProcessMsgSend() { if (m_pDataQueue) { stHttpData stData; int iCodelen; m_pDataQueue->PopOneCode((unsigned char*)&stData,sizeof(stData),iCodelen); if ( iCodelen == sizeof(stData) ) { switch (stData.action) { case stHttpData::uc_login: //登录 return UCLogin("test", "account1234"); break; case stHttpData::uc_recharge: //注册 return UCRecharge(1, 2, "account1234", 4); break; case stHttpData::uc_extend: //扩展 return UCSendExtend(); break; } } else { return -2; } } return -2; } size_t CHTTPParse::OnWriteData( void* buffer, size_t size, size_t nmemb, void* lpVoid ) { std::string* str = dynamic_cast<std::string*>((std::string *)lpVoid); if( NULL == str || NULL == buffer ) { return -1; } char* pData = (char*)buffer; str->append(pData, size * nmemb); return nmemb; } void CHTTPParse::Init() { //创建一个环形缓存队列来进行线程同步 m_pDataQueue = new CCodeQueue(1024*100); //100K足够了吧 if (m_pDataQueue) { m_pDataQueue->Initialize(); } InitNet(); } void CHTTPParse::CurlInit() { curl_global_init(CURL_GLOBAL_ALL); m_pCurlHandlder = curl_easy_init(); if (m_pCurlHandlder == NULL) { TRACESVR( "Failed to create CURL connection "); return ; } return ; } int CHTTPParse::SendHttpPost(const char* url,const char* pData) { CURLcode code; TRACESVR_DEBUG("Http Data[%s] ",pData); if (NULL == m_pCurlHandlder) { return -1; } code =curl_easy_setopt(m_pCurlHandlder, CURLOPT_NOSIGNAL, 1L); if (code !=CURLE_OK) { TRACESVR("Failed to set URL [%d] ", code); return -2 ; } //设置超时 curl_easy_setopt(m_pCurlHandlder, CURLOPT_TIMEOUT, 1); //curl_easy_setopt(m_pCurlHandlder, CURLOPT_CONNECTTIMEOUT, 3); //设置method为post //curl_easy_setopt(m_pCurlHandlder, CURLOPT_POST, 1); curl_easy_setopt(m_pCurlHandlder, CURLOPT_VERBOSE, 1L); curl_slist *http_headers = NULL; http_headers = curl_slist_append(http_headers, "Accept: application/json"); http_headers = curl_slist_append(http_headers, "Content-Type: application/json"); http_headers = curl_slist_append(http_headers, "charsets: utf-8"); curl_easy_setopt(m_pCurlHandlder, CURLOPT_CUSTOMREQUEST, "POST"); code = curl_easy_setopt(m_pCurlHandlder, CURLOPT_HTTPHEADER, http_headers); code = curl_easy_setopt(m_pCurlHandlder, CURLOPT_URL, "http://192.168.1.166/sdkdemo/restful/dispatch.php?do=cp/account.verifySession"); if (code != CURLE_OK) { TRACESVR("Failed to set URL [%d] ", code); return -3 ; } code =curl_easy_setopt(m_pCurlHandlder, CURLOPT_POSTFIELDS, pData); if (code != CURLE_OK) { TRACESVR("Failed to PostFields=%s, [code=%d] ",pData, code); return -4; } code =curl_easy_setopt(m_pCurlHandlder, CURLOPT_WRITEFUNCTION, CHTTPParse::OnWriteData); string strResp; code =curl_easy_setopt(m_pCurlHandlder, CURLOPT_WRITEDATA, (void *)&strResp); curl_easy_setopt(m_pCurlHandlder, CURLOPT_SSL_VERIFYPEER, false); curl_easy_setopt(m_pCurlHandlder, CURLOPT_SSL_VERIFYHOST, false); curl_easy_setopt(m_pCurlHandlder,CURLOPT_FOLLOWLOCATION,1); code = curl_easy_perform(m_pCurlHandlder); if(CURLE_OK == code) { char *ct =NULL; code = curl_easy_getinfo(m_pCurlHandlder, CURLINFO_CONTENT_TYPE, &ct); if((CURLE_OK == code) && ct) { TRACESVR("We received Content-Type: %s ", ct); } } else { TRACESVR("curl_easy_perform Error "); } curl_easy_cleanup(m_pCurlHandlder); return 0; } int CHTTPParse::UCLogin(const char* szSid, const char* szAccount) { Json::Value root; // id root["id"] = GETNOWTIMEUS; // service root["service"] = "account.verifySession"; Json::Value gameid; gameid["gameId"] = 123456; root["game"] = gameid; Json::Value sid; sid["sid"] = "eeeeettttttt555444"; root["data"] = sid; // encrypt root["encrypt"] = "md5"; // sign root["sign"] = "cce4cef6ea157f411932f9651555836e"; Json::FastWriter writer; std::string strWrite = writer.write(root); DoSend("192.168.1.166", "sdkdemo/restful/dispatch.php?do=cp/account.verifySession", strWrite.c_str()); }
// 其他函数还没调试,就不贴出来了
注意事项
1.设置http post数据格式和accept格式
http_headers = curl_slist_append(http_headers, "Accept: application/json"); http_headers = curl_slist_append(http_headers, "Content-Type: application/json");
2.json数据如果有问题,会直接报 500 internal server erro 的错误,其实就是json的数据格式不对。最好填充完,用工具检测一下json字符串是否合法
3.jsoncpp中要用 Json::FastWriter将json Value类型格式化,如下代码中的strWrite 就是json格式的字符串(需要post的数据),而不是调用asString()进行转化,后者会导致程序直接崩溃。
Json::FastWriter writer; std::string strWrite = writer.write(root);