项目开发中经常需要使用到私有协议和Qt,Android等GUI前端通信,比较常用的使用POST和GET方式带出MESSAGE
。实际使用中为了减少工作量和代码复用,经常使用到三方库,比较常用的服务有libevent或boost中的网络库、muduo, 也可自行写一套socket系统调用的二次封装, 当然这种方式不利于快速开发, 学习还是可以的。
这篇文章主要使用libevent库,因为是c写的, 所以掌握libevent非常重要。
针对以上,这里简单使用http的POST和GET方法解决以上问题。
关联:libevent库
,链接libevent 源码地址
jsoncpp
的编译和使用参考我的这篇文章: 链接C++库libjsoncpp使用
测试demo写的比较唐突,所以可能存在一些内存释放等BUG,因此如果向使用一下demo的程序开发,需要renew代码和多调试。
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编译后的库静态文件,如果要链接动态库,请自行编译。
#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 emitUTF8
std::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的请求API
if(!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-8
std::string strRespJsonBody("这里是协议内相关Json");
evbuffer_add_printf(pRespbuffer, "%s", strRespJsonBody.c_str()); //向evbuffer中增加message
evhttp_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;
}
#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
g++ *.cpp -I ./event ./libevent*.a -lpthread
因为没有将jsoncpp
移植到项目中,所以只是简单的测试响应的基本内容
该demo请求的是二进制流