目录
- 1、前言
- 2、测试demo
- 2.1、目录结构
- 2.2、 测试源码
- 2.2.1、http_server.cpp
- 2.2.2、 http_server.h
- 2.3、 编译
- 2.4、 运行结果
- 2.4.1、测试POST
- 2.4.2 、测试GET请求
1、前言
项目开发中经常需要使用到私有协议和Qt,Android等GUI前端通信,比较常用的使用POST和GET方式带出MESSAGE
。实际使用中为了减少工作量和代码复用,经常使用到三方库,比较常用的服务有libevent或boost中的网络库、muduo, 也可自行写一套socket系统调用的二次封装, 当然这种方式不利于快速开发, 学习还是可以的。
这篇文章主要使用libevent库,因为是c写的, 所以掌握libevent非常重要。
- POST请求比较常用, 特别是针对一些数据比较小的场景,比如控制相关, 业务相关的。当然传图片也可以,传输效果过低不推荐。
- 针对传输二进制比较大的数据, 可以使用GET方式。
针对以上,这里简单使用http的POST和GET方法解决以上问题。
关联:libevent库
,链接libevent 源码地址
jsoncpp
的编译和使用参考我的这篇文章: 链接C++库libjsoncpp使用
2、测试demo
测试demo写的比较唐突,所以可能存在一些内存释放等BUG,因此如果向使用一下demo的程序开发,需要renew代码和多调试。
2.1、目录结构
event目录|-- libevent头文件
http_server.cpp|-- CHttpServer 功能类,里面带main的测试程序
http_server.h|-- CHttpServer 接口
libevent.a|-- libevent库
libevent_core.a|-- libevent库
libevent_pthreads.a|-- libevent库其中event目录是libevent编译后的头文件, *.a是libevent编译后的库静态文件,如果要链接动态库,请自行编译。
2.2、 测试源码
2.2.1、http_server.cpp
#include <unistd.h>#include <iostream>
#include <string>
#include <memory> //shared_ptr,unique_ptr .etc#include "http_server.h"CHttpServer::CHttpServer():base_(nullptr), http_(nullptr), serverloopThread_(nullptr),isExit_(true), sock_(nullptr)
{if(!serverloopThread_){serverloopThread_ = new std::thread(&CHttpServer::serverDispatch, this);if(!serverloopThread_){std::cout << "创建线程失败!" << std::endl;}}
}CHttpServer::~CHttpServer()
{if(serverloopThread_){serverloopThread_->join();delete serverloopThread_; serverloopThread_ = nullptr;}
}int CHttpServer::pic_video_test(struct evhttp_request *_req, const std::string &strPath, const char *strParms/*参数query*/)
{/*1. 拿到文件数据*/FILE *fp = fopen("/tmp/test.pic", "rb");if(!fp){return -1; }fseek(fp, 0, SEEK_END);size_t stream_size = ftell(fp);fseek(fp, 0, 0);printf("size:%d\n", stream_size);char *pStream = (char*)calloc(1, stream_size);if(!pStream){return -2;}fread(pStream, 1, stream_size, fp);fclose(fp); fp = (FILE*)0;/*添加一些headers*/evhttp_add_header(_req->output_headers, "Server", "帘下有白绿的服务");evhttp_add_header(_req->output_headers, "Connection", "close");struct evbuffer *buf = evbuffer_new();if(!buf){evhttp_send_error(_req, HTTP_INTERNAL, "Internal Error");return -255;}if(pStream && stream_size > 0){evhttp_add_header(_req->output_headers, "Content-Type", "img/jpg");int ret = evbuffer_add(buf, pStream, stream_size);printf("ret:%d\n", ret);free(pStream); pStream = 0;}else {#if 0 //增加异常信息响应evhttp_add_header(_req->output_headers, "Content-Type", "application/json;charset=UTF-8");Json::Value root;try {root["code"] = 300;root["msg"] = "打开文件异常,可能文件不存在或系统错误";}catch(std::exception &e){return -3;}Json::Value def;Json::StreamWriterBuilder::setDefaults(&def);def["emitUTF8"] = true;def["precisionType"] = "decimal";def["precision"] = 6;def["indentation"] = ""; // 压缩格式,没有换行和不必要的空白字符std::ostringstream stream;Json::StreamWriterBuilder stream_builder;stream_builder.settings_ = def;//Config emitUTF8std::unique_ptr<Json::StreamWriter> writer(stream_builder.newStreamWriter());writer->write(root, &stream);std::string strJson = stream.str();evbuffer_add(buf, strJson.c_str(), strJson.length());
#endif}evhttp_send_reply(_req, HTTP_OK, "done",buf);evbuffer_free(buf);printf("request,response Done.\n");return 0;
}//TODO : GET相关的开发工作int CHttpServer::method_GET_io_process(struct evhttp_request *req)
{if( !req ){return -1;}const char *uri = evhttp_request_get_uri(req); //获取URI信息struct evhttp_uri *decoded = nullptr;const char *path;char *decoded_path;decoded = evhttp_uri_parse(uri); //解析URI请求信息if(!decoded){evhttp_send_error(req, HTTP_BADREQUEST, 0);return -2;}path = evhttp_uri_get_path(decoded); //获取http get请求路径if(!path) path="/";printf("path:%s\n", path);/*We need to decode it, to see what path the user really wanted.*/decoded_path = evhttp_uridecode(path, 0, NULL); //查询路径相关{char *}, get的请求APIif(!decoded_path){return -3;evhttp_send_error(req, HTTP_NOTFOUND, NULL); //响应http错误信息}printf("decoded_path:%s\n", decoded_path);//获取uri中的参数部分const char * query = evhttp_uri_get_query(decoded); //query 参数, {char *}printf("query:%s\n", query);pic_video_test(req, decoded_path, query);if(decoded)evhttp_uri_free(decoded);if(decoded_path){free(decoded_path);decoded_path = 0;}}//TODO: POST私有协议相关的开发工作, 业务层
int CHttpServer::method_POST_io_process( struct evhttp_request *req )
{
// Json::Value root;struct evbuffer *pEvbuffer(nullptr);pEvbuffer = evhttp_request_get_input_buffer(req);if(nullptr == pEvbuffer){//需要增加异常的响应, 这里暂忽略return -1;}int nJsonbodySize = 1024 * 10;char *pJsonbody = (char*)calloc(nJsonbodySize, sizeof(char));if(!pJsonbody){return -2;}int nread = 0;while(evbuffer_get_length(pEvbuffer)){nread += evbuffer_remove(pEvbuffer, pJsonbody, nJsonbodySize-1);}try {//解包{反序列化}//Json::Reader reader;//reader.parse(pJsonbody, root);}catch(std::exception &e){}/*请求数据的输出*/// 1. 反序列化的逻辑处理,涉及到jsoncpp的库操作// 2. 根据项目业务做数据的转发处理{event}以及配置文件的读写操作// 3. 封Json包响应请求 //4. 发送struct evbuffer *pRespbuffer = evbuffer_new();if(!pRespbuffer){evhttp_send_error(req, HTTP_INTERNAL, "internal error");return -1;}evhttp_add_header(req->output_headers, "Connection", "close");evhttp_add_header(req->output_headers, "Content-Type", "application/json;charset=UTF-8"); //和客户端约定的编码方式,这里用的UTF-8std::string strRespJsonBody("这里是协议内相关Json");evbuffer_add_printf(pRespbuffer, "%s", strRespJsonBody.c_str()); //向evbuffer中增加messageevhttp_send_reply(req, 200, "ok", pRespbuffer); //向socker发送操作evbuffer_free(pRespbuffer); //释放操作if(pJsonbody){free(pJsonbody); pJsonbody = (char*)0;}return 0;
}void CHttpServer::serverIoExec(struct evhttp_request *req, void *arg)
{CHttpServer *_this = (CHttpServer*)arg;if(!req){return ;} evhttp_cmd_type eMethod = evhttp_request_get_command(req);switch(eMethod){case EVHTTP_REQ_GET:{_this->method_GET_io_process(req);}break;case EVHTTP_REQ_POST:{_this->method_POST_io_process(req);}break;default:std::cout << "未知http方法" << std::endl;}return;
}void CHttpServer::serverDispatch()
{pthread_setname_np(pthread_self(), "ServerLoop");evthread_use_pthreads();base_ = event_base_new();if(nullptr == base_){std::cout << "create event_base failure!" << std::endl;goto FREE_BASE;}if(!(http_ = evhttp_new(base_))){std::cout << " Create a new HTTP server failure!" << std::endl;goto FREE_BASE;}evhttp_set_gencb(http_, CHttpServer::serverIoExec, (void*)this);sock_ = evhttp_bind_socket_with_handle(http_, "0.0.0.0", DEFAULT_LISTEN_PORT);if(nullptr == sock_){std::cout << "" << std::endl;goto FREE_HTTP;}event_base_dispatch(base_); FREE_SOCK:if(http_ && sock_){evhttp_del_accept_socket(http_, sock_); sock_ = nullptr;}FREE_HTTP:if(http_){evhttp_free(http_); http_ = nullptr;}
FREE_BASE:if(base_){event_base_free(base_); base_ = nullptr;}} void CHttpServer::loop()
{while(!isExit_){sleep(2); //这里使用select精准时钟比较合理,待修改}
}int main(int argc, char *argv[])
{std::shared_ptr<CHttpServer> impl = std::make_shared<CHttpServer>();if(impl){impl->loop();}return 0;
}
2.2.2、 http_server.h
#ifndef HTTP_SERVER_H__
#define HTTP_SERVER_H__#include <evhttp.h>
#include <event2/thread.h>#include <thread>#define DEFAULT_LISTEN_PORT ( 12385 )
class CHttpServer {
public:CHttpServer();~CHttpServer();void loop();static void serverIoExec(struct evhttp_request *req, void *arg);private:int method_POST_io_process( struct evhttp_request *req );int method_GET_io_process(struct evhttp_request *req);int pic_video_test(struct evhttp_request *_req, const std::string &strPath, const char *strParms/*参数query*/);void serverDispatch(); /*loop pthread process*/struct event_base *base_;struct evhttp *http_;struct evhttp_bound_socket * sock_;bool isExit_;std::thread *serverloopThread_;
};
#endif
2.3、 编译
g++ *.cpp -I ./event ./libevent*.a -lpthread
2.4、 运行结果
2.4.1、测试POST
因为没有将jsoncpp
移植到项目中,所以只是简单的测试响应的基本内容
2.4.2 、测试GET请求
该demo请求的是二进制流