mongoose implements httpserver, client

Use mongoose as the basis of the http protocol. Basically, the functions are all in a c file. A httpserve and httpclient can be implemented by directly including the c file, and a small httpserver can be implemented. The required concurrency is not very high, as shown below , directly include mongoose.c in the project.

insert image description here

1 server

First of all, http server is a thread class, which defines a thread base class, and then httpserver inherits from the base class. After using the start function, the thread starts, calls the run function, and infinitely loops. The server can be not only httpserver, but also websocketserver.

/*
Author:钱波
email: [email protected]
wei:   18091589062
func  :线程类
time:  2018年5月30日
*/

#ifndef _TTHREAD_RUN_ABLE_H_
#define _TTHREAD_RUN_ABLE_H_
#include <mutex>
#include <queue>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <functional>
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

The implementation of httpserver is mainly to implement the run function of httpserver to realize the loop.

#include "mongoose.h"
#include "TThreadRunable.h"
#include <string>
#include <functional>
#include <map>
using namespace std;
//typedef struct re_param
//{
    
    
//	void * pUser;
//	string uri;
//	string host;
//	string uid;
//	string message;
//}re_param;
//using handler = std::function <void(re_param &param)>;
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();
	

};

Below is the implementation file

#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);

}

The run function is a wireless while loop. Note that mongoose is not very efficient. In fact, it is implemented using mg_mgr_poll. It refreshes every 50 milliseconds, and this 50 milliseconds can be modified by itself.
You can also use the lamba function when calling, as shown below

handler hd1 = std::bind(&CMeetingDlg::StartHttpCallback, this, std::placeholders::_1);
_httpserver.RegisterHandler("/start", hd1);

The client is implemented below

#pragma once
#include <string>
#include <functional>


#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()
//{
    
    
//
//}

A simple server and client can be implemented so easily without spending too much effort.

Guess you like

Origin blog.csdn.net/qianbo042311/article/details/127128950