使用mongoose来做http协议的基础,基本上的函数都在一个c文件里面,直接包含c文件就可以实现一个httpserve和httpclient,可以实现一个小型的httpserver,要求的并发数不是很高,如下所示,直接将mongoose.c包含在项目里面。
首先http server 是一个线程类, 定义一个线程基类,然后httpserver从基类继承下去,使用start函数以后,线程开启,调用run函数,无限循环。server不仅是可以是httpserver,也可以是websocketserver。
/*
Author:钱波
email: [email protected]
wei: 18091589062
func :线程类
time: 2018年5月30日
*/
#ifndef _TTHREAD_RUN_ABLE_H_
#define _TTHREAD_RUN_ABLE_H_
#include
#include
#include
#include
#include
#include
using namespace std;
class TThreadRunable
{
private:
//线程
thread _thread;
//等待信号
std::mutex _signal_mutex;
std::condition_variable _cond;
protected:
//char _running = false;
char _stop = true;
//锁定运行状态
std::mutex _mutex;
public:
TThreadRunable()
{}
virtual ~TThreadRunable()
{}
public:
char * status(){
return &_stop;
}
void Join()
{
if (_thread.joinable())
_thread.join();
}
bool IsStop()
{
return _stop == 1 ? true : false;
}
void WaitForSignal()
{
std::unique_lock<std::mutex> ul(_signal_mutex);
_cond.wait(ul);
}
void Notify()
{
_cond.notify_one();
}
virtual int Start()
{
if (_stop == 1)//非運行中
{
_stop = 0;
_thread = std::thread(std::bind(&TThreadRunable::Run, this));
return 0;
}
return -1;
}
virtual void Stop()
{
_stop = 1; // true;
}
virtual void Run() = 0;
};
#endif
实现httpserver,主要是实现httpserver的run函数,实现循环。
#include "mongoose.h"
#include "TThreadRunable.h"
#include
#include
#include
using namespace std;
//typedef struct re_param
//{
// void * pUser;
// string uri;
// string host;
// string uid;
// string message;
//}re_param;
//using handler = std::function ;
typedef struct request_param
{
//返回的
string retString;
map<string, string> params;
void insertParam(string key)
{
params[key] = "null";
}
void setParam(string key, string value)
{
params[key] = value;
}
const char* getParam(string key)
{
if (params.find(key) != params.end())
{
return params[key].c_str();
}
return NULL;
}
int getSize()
{
return (int)params.size();
}
map<string, string>::iterator iter;
const char * GetFirstParam()
{
iter = params.begin();
if (iter == params.end())
return NULL;
return iter->first.c_str();
}
void SetParamValue(const char *value)
{
if (iter != params.end())
iter->second = value;
}
const char * GetNextParam()
{
iter++;
if (iter == params.end())
{
return NULL;
}
else
{
return iter->first.c_str();
}
}
}request_param;
using handler2 = std::function<void(request_param &rp)>;
typedef struct route_request
{
request_param _param;
handler2 func;
}route_request;
class Service_HTTP :public TThreadRunable
{
protected:
using handler_map = std::map<std::string, route_request>;
handler_map _handlers;
public:
//handler handlestart = nullptr;
//handler handlestop = nullptr;
//handler hanlde_volume = nullptr;
//handler handle_microphone = nullptr;
handler_map &GetHandleMap()
{
return _handlers;
}
const char *s_http_port = "9091";
struct mg_serve_http_opts _s_http_server_opts;
struct mg_mgr _mgr;
struct mg_connection * _nc = NULL;
bool RegisterHandler(std::string uri, route_request f) {
auto it = _handlers.find(uri);
if (_handlers.end() != it)
return false;
return _handlers.emplace(uri, f).second;
}
void UnRegisterHandler(std::string uri) {
auto it = _handlers.find(uri);
if (_handlers.end() != it)
_handlers.erase(it);
}
public:
//static void handle_api(string uri, struct mg_connection *nc, struct http_message *hm);
static void handle_api2(string uri, struct mg_connection *nc, struct http_message *hm);
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data);
static int is_websocket(const struct mg_connection *nc) {
return nc->flags & MG_F_IS_WEBSOCKET;
}
//广播该视频数据
void WebSocket_Broadcast(void * s2);
//void WebSocket_Broadcast1(void * pUser, uint8_t * data, int len);
public:
~Service_HTTP()
{
}
void Stop();
void Run();
};
下面是实现文件
#include "restful_server.h"
//#include "../CorePhone/TPictureInfo.h"
static struct mg_serve_http_opts s_http_server_opts;
void Service_HTTP::WebSocket_Broadcast(void *s)
{
//return ;
//int jpglen = 0;
//videosend2 *s2 = (videosend2 *)s;
//char * jpg = NULL;
//plus.EncodeRGB2Jpeg((char*)s2->data, s2->w, s2->h, (s2->w)*3, &jpg, jpglen);
//struct mg_connection *c;
//if (_nc != NULL) {
// for (c = mg_next(_nc->mgr, NULL); c != NULL; c = mg_next(_nc->mgr, c)) {
// if (c == _nc) continue; /* Don't send to the sender. */
// mg_send_websocket_frame(c, WEBSOCKET_OP_BINARY, jpg, jpglen);
// }
//}
//if (jpg != NULL)
// delete[]jpg;
}
//void Service_HTTP::WebSocket_Broadcast1(void * pUser, uint8_t * data, int len)
//{
// struct mg_connection *c;
// if (pUser == NULL)
// return;
//
// if (_nc != NULL) {
// for (c = mg_next(_nc->mgr, NULL); c != NULL; c = mg_next(_nc->mgr, c)) {
// if (c == pUser)
// mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, data, len);
// }
// }
//
//}
void Service_HTTP::handle_api2(string uri, struct mg_connection *nc, struct http_message *hm)
{
Service_HTTP *sh = (Service_HTTP*)nc->user_data;
if (sh == NULL)
return;
handler_map& hmap = sh->GetHandleMap();
handler_map::iterator iter;
iter = hmap.find(uri);
if (iter == hmap.end())
{
mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
return;
}
route_request & rr = iter->second;
const char * param = rr._param.GetFirstParam();
while (param != NULL)
{
char buffer[255];
mg_get_http_var(&hm->query_string, param, buffer, sizeof(buffer));
rr._param.SetParamValue(&buffer[0]);
//rr._param.setParam(string(param), string(buffer));
param = rr._param.GetNextParam();
}
/* Send headers */
//mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n");
mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
//允许哪些url可以跨域请求到本域
//mg_printf(nc, "Access-Control-Allow-Origin:*\r\n");
//允许的请求方法,一般是GET,POST,PUT,DELETE,OPTIONS
mg_printf(nc, "Access-Control-Allow-Methods:POST,OPTIONS,GET\r\n");
//允许哪些请求头可以跨域
mg_printf(nc, "Access-Control-Allow-Headers:x-requested-with,content-type\r\n");
if (rr.func != nullptr)
{
rr.func(rr._param);
}
string reply = "{\"ret\":0}";
if(!rr._param.retString.empty())
reply = rr._param.retString;
mg_printf(nc, "Content-Type:application/json\r\n");
mg_printf(nc, "Content-Length:%u\r\n\r\n%s\r\n", (uint32_t)reply.size(), reply.c_str());
//mg_printf(nc, "HTTP/1.1 200 OK\r\niConnection: close\r\nContent-Type: text/html\r\nContent-Length: %u\r\n\r\n%s\r\n",
nc->flags |= MG_F_SEND_AND_CLOSE;
//mg_send(nc, "", 0);
}
#if 0
void Service_HTTP::handle_api(string uri, struct mg_connection *nc, struct http_message *hm)
{
Service_HTTP *sh = (Service_HTTP*)nc->user_data;
if (sh == NULL)
return;
//const char * param = sh->_param.GetFirstParam();
if (uri.compare("/start") == 0)
{
char host[64];
char uid[64];
char message[64];
mg_get_http_var(&hm->query_string, "serverhost", host, sizeof(host));
mg_get_http_var(&hm->query_string, "uid", uid, sizeof(uid));
mg_get_http_var(&hm->query_string, "message", message, sizeof(message));
/* Send headers */
//mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n");
mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
//允许哪些url可以跨域请求到本域
mg_printf(nc, "Access-Control-Allow-Origin:*\r\n");
//允许的请求方法,一般是GET,POST,PUT,DELETE,OPTIONS
mg_printf(nc, "Access-Control-Allow-Methods:POST\r\n");
//允许哪些请求头可以跨域
mg_printf(nc, "Access-Control-Allow-Headers:x-requested-with,content-type\r\n");
string reply = "{\"result\":0}";
mg_printf(nc, "Content-Length:%u\r\n\r\n%s\r\n", (uint32_t)reply.size(), reply.c_str());
//mg_printf(nc, "HTTP/1.1 200 OK\r\niConnection: close\r\nContent-Type: text/html\r\nContent-Length: %u\r\n\r\n%s\r\n",
nc->flags |= MG_F_SEND_AND_CLOSE;
mg_send(nc, "", 0);
//mg_printf_http_chunk(nc, "{ \"result\": %ld }", 0);
//mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
re_param request;
request.uri = uri;
request.pUser = NULL;
request.host = host;
request.uid = uid;
request.message = message;
if (sh->handlestart != nullptr)
sh->handlestart(request);
//it->second(request);
OutputDebugString(L"start video to");
//callback(__void, host, uid, message);
}
else if (uri.compare("/stop") == 0) //停止
{
char message[64];
mg_get_http_var(&hm->query_string, "message", message, sizeof(message));
/* Send headers */
//mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n");
mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
//允许哪些url可以跨域请求到本域
mg_printf(nc, "Access-Control-Allow-Origin:*\r\n");
//允许的请求方法,一般是GET,POST,PUT,DELETE,OPTIONS
mg_printf(nc, "Access-Control-Allow-Methods:POST\r\n");
//允许哪些请求头可以跨域
mg_printf(nc, "Access-Control-Allow-Headers:x-requested-with,content-type\r\n");
string reply = "{\"result\":0}";
mg_printf(nc, "Content-Length:%u\r\n\r\n%s\r\n", (uint32_t)reply.size(), reply.c_str());
//mg_printf(nc, "HTTP/1.1 200 OK\r\niConnection: close\r\nContent-Type: text/html\r\nContent-Length: %u\r\n\r\n%s\r\n",
nc->flags |= MG_F_SEND_AND_CLOSE;
mg_send(nc, "", 0);
//mg_printf(nc, "{ \"result\": %ld }", 0);
//mg_send(nc, "", 0);
//mg_printf_http_chunk(nc, "{ \"result\": %ld }", 0);
//mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
//auto it = sh->GetHandleMap().find(uri);
//if (sh->GetHandleMap().end() == it)
// return;
re_param request;
request.uri = uri;
request.pUser = NULL;
request.message = message;
if(sh->handlestop!=nullptr)
sh->handlestop(request);
OutputDebugString(L"stop video");
}
//else if(uri.compare("getmicrophone"))
//获取
}
#endif
void Service_HTTP::ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
//传送文件地址和转化的目的地址
// /api/t2pdf
switch (ev) {
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
printf("here connection\n");
break;
case MG_EV_WEBSOCKET_FRAME: {
struct websocket_message *wm = (struct websocket_message *) ev_data;
/* New websocket message. Tell everybody. */
struct mg_str d = { (char *)wm->data, wm->size };
//broadcast(nc, d);
break;
}
case MG_EV_HTTP_REQUEST:
{
string uri = string(hm->uri.p, hm->uri.len);
handle_api2(uri, nc, hm);
//if (mg_vcmp(&hm->uri, "/start") == 0) {
// handle_api2(uri, nc, hm);
//}
//else if (mg_vcmp(&hm->uri, "/stop") == 0) {
// handle_api2(uri, nc, hm);
//}
//else
//{
// mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
//}
break;
}
case MG_EV_CLOSE:
{
if (is_websocket(nc))
OutputDebugString(L"websocket out");
break;
}
default:
break;
}
}
void Service_HTTP::Stop()
{
TThreadRunable::Stop();
Join();
}
void Service_HTTP::Run()
{
mg_mgr_init(&_mgr, NULL);
_nc = mg_bind(&_mgr, s_http_port, ev_handler);
_nc->user_data = this;
mg_set_protocol_http_websocket(_nc);
s_http_server_opts.document_root = "./public"; // Serve current directory
s_http_server_opts.enable_directory_listing = "yes";
//printf("Started on port %s\n", s_http_port);
while (!IsStop()) {
mg_mgr_poll(&_mgr, 50);
}
mg_mgr_free(&_mgr);
}
run函数是一个无线while循环,注意mongoose谈不上有太高的效率,实际上就是使用mg_mgr_poll来实现的,每隔50毫秒刷新一次,这个50毫秒可以自行修改。
调用的时候也可以使用lamba函数,如下所示
handler hd1 = std::bind(&CMeetingDlg::StartHttpCallback, this, std::placeholders::_1);
_httpserver.RegisterHandler("/start", hd1);
下面实现client
#pragma once
#include
#include
#include "mongoose.h"
#include "../CorePhone/TThreadRunable.h"
typedef void(*callback_http)(std::string);
class HttpClient//:public TThreadRunable
{
private:
mg_connection *connection = NULL;
mg_mgr mgr;
std::string _posturl;
public:
void seturl(const char * ip)
{
//string temp = url;
_posturl = "http://";
_posturl += ip;
_posturl += "/streamclose";
}
public:
HttpClient();
~HttpClient();
void SendReq();
static void OnHttpEvent(mg_connection *connection, int event_type, void *event_data);
int s_exit_flag;
callback_http s_callback;
//void Run();
};
#include "HttpClient.h"
//ReqCallback HttpClient::s_req_callback;
// 客户端的网络请求响应
void HttpClient::OnHttpEvent(mg_connection *connection, int event_type, void *event_data)
{
HttpClient * hc =(HttpClient *)connection->user_data;
http_message *hm = (struct http_message *)event_data;
int connect_status;
switch (event_type)
{
case MG_EV_CONNECT:
connect_status = *(int *)event_data;
if (connect_status != 0)
{
printf("Error connecting to server, error code: %d\n", connect_status);
hc->s_exit_flag = 1;
}
break;
case MG_EV_HTTP_REPLY:
{
printf("Got reply:\n%.*s\n", (int)hm->body.len, hm->body.p);
std::string rsp = std::string(hm->body.p, hm->body.len);
connection->flags |= MG_F_SEND_AND_CLOSE;
hc->s_exit_flag = 1; // 每次收到请求后关闭本次连接,重置标记
// 回调处理
hc->s_callback(rsp);
}
break;
case MG_EV_CLOSE:
if (hc->s_exit_flag == 0)
{
printf("Server closed connection\n");
hc->s_exit_flag = 1;
};
break;
default:
break;
}
}
// 发送一次请求,并回调处理,然后关闭本次连接
void HttpClient::SendReq()
{
if (_posturl.empty())
return;
// 给回调函数赋值
//s_callback = req_callback;
connection = mg_connect_http(&mgr, OnHttpEvent, _posturl.c_str(), NULL, NULL);
connection->user_data = this;
mg_set_protocol_http_websocket(connection);
//printf("Send http request %s\n", _posturl.c_str());
// loop
//while (s_exit_flag == 0)
mg_mgr_poll(&mgr, 50);
mg_mgr_poll(&mgr, 50);
}
HttpClient::HttpClient()
{
mg_mgr_init(&mgr, NULL);
}
HttpClient::~HttpClient()
{
mg_mgr_free(&mgr);
}
//void HttpClient::Run()
//{
//
//}
简单的server和client 就可以这么简单的实现,不用花费太多精力。