(一)基于Mongoose的HTTP通信库

基于Mongoose的HTTP通信库

Mongoose可以支持的网络服务类型

  • TCP服务端和客户端

  • UDP服务端和客户端

  • HTTP服务端和客户端

  • Websocket服务端和客户端

  • SNTP 客户端

  • DNS服务端和客户端(不常用)

  • MQTT服务端和客户端(不常用)

  • COAP服务端和客户端(不常用)

HTTP相关接口介绍

struct mg_http_endpoint {
  struct mg_http_endpoint *next;
  struct mg_str uri_pattern; /* owned */
  char *auth_domain;         /* owned */
  char *auth_file;           /* owned */

  mg_event_handler_t handler;
#if MG_ENABLE_CALLBACK_USERDATA
  void *user_data;
#endif
};
/* Looking for the longest suitable handler of nc and the given uri_path*/
struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, struct mg_str *uri_path);
/*Attaches a built-in HTTP event handler to the given connection*/
void mg_set_protocol_http_websocket(struct mg_connection *nc);
/*回调函数入口*/
void mg_http_handler(struct mg_connection *nc, int ev,void *ev_data MG_UD_ARG(void *user_data));
/*返回状态码对应的状态*/
const char *mg_status_message(int status_code);
/*HTTP数据发送相关接口*/
void mg_http_send_error(struct mg_connection *nc, int code,const char *reason);
void mg_http_send_redirect(struct mg_connection *nc, int status_code, const struct mg_str location,
                           const struct mg_str extra_headers);
void mg_send_head(struct mg_connection *c, int status_code,int64_t content_length, const char *extra_headers);

void mg_send_response_line(struct mg_connection *nc, int status_code,const char *extra_headers);
void mg_send_response_line_s(struct mg_connection *nc, int status_code,const struct mg_str extra_headers);

int mg_printf(struct mg_connection *, const char *fmt, ...);
int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
void mg_send(struct mg_connection *, const void *buf, int len);
mg_http_send_redirect
mg_send_response_line
/*
 * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
 * This function sends the buffer size as hex number + newline first, then
 * the buffer itself, then the newline. 
 *
 * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to
 * using this function.
 *
 * NOTE: do not forget to send an empty chunk at the end of the response,
 * to tell the client that everything was sent. Example:
 */
void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len);
void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...);
mg_printf_http_chunk
mg_send_http_chunk
/*Serves a specific file with a given MIME type and optional extra headers.*/
void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,const char *path, 
                        const struct mg_str mime_type,const struct mg_str extra_headers);
/*example:
 *static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
 *   switch (ev) {
 *     case MG_EV_HTTP_REQUEST: {
 *       struct http_message *hm = (struct http_message *) ev_data;
 *       mg_http_serve_file(nc, hm, "file.txt",mg_mk_str("text/plain"), mg_mk_str(""));
 *       break;
 *     }
 *     ...
 *   }
 * }
 */
/* Serves given HTTP request according to the options */
void mg_serve_http(struct mg_connection *nc, struct http_message *hm,struct mg_serve_http_opts opts);
/*example:
 *static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
 *   switch (ev) {
 *     case MG_EV_HTTP_REQUEST: {
 *       struct http_message *hm = (struct http_message *) ev_data;
 *		 struct mg_serve_http_opts opts = { .document_root = "/var/www" };  // C99
 *       mg_serve_http(nc, hm, opts);
 *       break;
 *     }
 *     ...
 *   }
 * }
 */
/*HTTP报文解析相关接口*/

/*Parses a HTTP message.
 *
 * `is_req` should be set to 1 if parsing a request, 0 if reply.
 *
 * Returns the number of bytes parsed. If HTTP message is
 * incomplete `0` is returned. On parse error, a negative number is returned.
 */
int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req);

struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);

int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,size_t buf_size);
int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,size_t buf_size);

/*
 * Gets and parses the Authorization: Basic header
 * Returns -1 if no Authorization header is found, or if
 * mg_parse_http_basic_auth fails parsing the resulting header.
 */
int mg_get_http_basic_auth(struct http_message *hm, char *user,
                           size_t user_len, char *pass, size_t pass_len);
int mg_parse_http_basic_auth(struct mg_str *hdr, char *user,
                             size_t user_len, char *pass, size_t pass_len);

int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,size_t dst_len);
mg_get_http_basic_auth
mg_get_http_header
mg_parse_http_basic_auth
mg_http_parse_header
mg_http_parse_header2
/* 客户端在请求头部中生成鉴权信息 */
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
                                      const char *method, const char *uri,
                                      const char *auth_domain, const char *user,
                                      const char *passwd, const char *nonce);

/*检查鉴权信息中的时间戳有没有过期,mongoose中实现为有效时间1小时。防止重放攻击。
 *未过期则返回1,否则返回0.
 * Assumption: nonce is a hexadecimal number of seconds since 1970.
 */
static int mg_check_nonce(const char *nonce);

/*
 * Authenticates a HTTP request against an opened password file.
 * Returns 1 if authenticated, 0 otherwise.
 */
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,FILE *fp);

int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
                         struct mg_str username, struct mg_str cnonce,
                         struct mg_str response, struct mg_str qop,
                         struct mg_str nc, struct mg_str nonce,
                         struct mg_str auth_domain, FILE *fp);

/* 发送连接鉴权失败的回复,状态码401.*/
void mg_http_send_digest_auth_request(struct mg_connection *c,const char *domain);
mg_http_check_digest_auth
mg_check_nonce
mg_check_digest_auth
/*HTTP连接相关接口*/
struct mg_connection *mg_connect_http(struct mg_mgr *mgr, 
                                      MG_CB(mg_event_handler_t ev_handler, void *user_data),
                                      const char *url, 
                                      const char *extra_headers, 
                                      const char *post_data);
struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr, 
                                          MG_CB(mg_event_handler_t ev_handler, void *user_data),
                                          struct mg_connect_opts opts, 
                                          const char *url, 
                                          const char *extra_headers,
                                          const char *post_data);
struct mg_connection *mg_connect_http_base(struct mg_mgr *mgr, 
                                           MG_CB(mg_event_handler_t ev_handler, void *user_data),
                                           struct mg_connect_opts opts, 
                                           const char *scheme1, 
                                           const char *scheme2,
                                           const char *scheme_ssl1, 
                                           const char *scheme_ssl2, 
                                           const char *url,
                                           struct mg_str *path, 
                                           struct mg_str *user_info, 
                                           struct mg_str *host);
mg_connect_http
mg_connect_http_opt

HTTP Websocket相关接口

/*向对端发送websocket帧*/
void mg_send_websocket_frame(struct mg_connection *nc, int op, const void *data,size_t len);

void mg_send_websocket_framev(struct mg_connection *nc, int op,const struct mg_str *strv, int strvcnt);

void mg_printf_websocket_frame(struct mg_connection *nc, int op,const char *fmt, ...);
mg_printf_websocket_frame
mg_send_websocket_frame
/*向服务端发送websocket握手报文:
* "Upgrade: websocket\r\n"
* "Connection: Upgrade\r\n"
* "Sec-WebSocket-Version: 13\r\n"
* "Sec-WebSocket-Key: xxx\r\n",
* "Sec-WebSocket-Protocol: xxx\r\n"
* 注意:nc必需已连接,且请求必需支持HTTP/1.1
*/
void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path,const char *host,
                                   const char *protocol,const char *extra_headers);

void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,const char *host,
                                  const char *protocol,const char *extra_headers, 
                                   const char *user,const char *pass);

void mg_send_websocket_handshake3v(struct mg_connection *nc,const struct mg_str path,
                                   const struct mg_str host, const struct mg_str protocol,
                                   const struct mg_str extra_headers,const struct mg_str user,
                                   const struct mg_str pass);

void mg_send_websocket_handshake(struct mg_connection *nc, const char *path,const char *extra_headers);
mg_send_websocket_handshake
mg_send_websocket_handshake3v
mg_send_websocket_handshake2
mg_send_websocket_handshake3
/*建立WebSocket连接*/
struct mg_connection *mg_connect_ws(
    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
    const char *url, const char *protocol, const char *extra_headers);

struct mg_connection *mg_connect_ws_opt(
    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
    struct mg_connect_opts opts, const char *url, const char *protocol,
    const char *extra_headers);
mg_connect_ws
mg_connect_ws_opt
#define MG_SOCK_STRINGIFY_IP 1
#define MG_SOCK_STRINGIFY_PORT 2
#define MG_SOCK_STRINGIFY_REMOTE 4
/*Converts a connection's local or remote address into string
 *
 * The `flags` parameter is a bit mask that controls the behaviour,
 * flag=MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT ,and output the local address
 * flag=MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT|MG_SOCK_STRINGIFY_REMOTE, and output the remote address
 *
 * Return length of the stringified address.
 */
int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,int flags);
/*Convert the socket's address into string.
 * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT.
 */
int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,int flags);
mg_conn_addr_to_str
mg_sock_addr_to_str

HTTP服务端、客户端代码封装

1、通用头文件

//http_message.h
#pragma once
#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>
#include "mongoose.h"

using namespace std;
const int CHAR_HTTP_VAR_MAX = 128;
const int CHAR_HTTP_COOKIE_MAX = 400;

//供客户端使用,用来处理服务端回复的响应报文
using ResponseHandler = std::function<void(http_message *)>;

//供服务端使用,用来处理客户端发送的请求报文
using RequestHandler = std::function<const std::string(http_message *)>;//返回值为回复报文

class HttpMessage
{
private:
    struct http_message *hm = NULL;
public:
    HttpMessage(struct http_message *hm){ this->hm = hm; }

    ~HttpMessage(){}

    const string getGetVar(const string & var_name) const
    {
        if (hm == NULL || hm->query_string.p == NULL)return "";
        try{
            char var[CHAR_HTTP_VAR_MAX] = { 0 };
            mg_get_http_var(&hm->query_string, var_name.c_str(), var, sizeof(var));
            string ret = var;
            return ret;
        }
        catch (...){ return ""; }
    }

    const string getPostVar(const string & var_name) const
    {
        if (hm == NULL || hm->body.p == NULL)return "";
        try{
            char var[CHAR_HTTP_VAR_MAX] = { 0 };
            mg_get_http_var(&hm->body, var_name.c_str(), var, sizeof(var));
            string ret = var;
            return ret;
        }
        catch (...){ return ""; }
    }

    const string getCookie(const string & cookie_name)const
    {
        return getHeadValue("cookie");
    }

    const string getHeadValue(const string & head_name)const
    {
        if (hm == NULL)return "";
        try{
            int i = 0;
            while (hm->header_names[i].len > 0)
            {
                if (hm->header_names[i].p != NULL
                    &&hm->header_names[i].len == head_name.size()
                    && mg_ncasecmp(hm->header_names[i].p, head_name.c_str(), head_name.size()))
                {
                    return string(hm->header_values[i].p, hm->header_values[i].len);
                }
                ++i;
            }
        }
        catch (...){ return ""; }
    }

    inline const string getBody()const
    {
        if (hm == NULL)return "";
        try{ return string(hm->body.p, hm->body.len); }
        catch (...){ return ""; }
    }

    inline const string getURI()const
    {
        if (hm == NULL)return "";
        try{ return string(hm->uri.p, hm->uri.len); }
        catch (...){ return ""; }
    }

    inline const string getAllMessage()const
    {
        if (hm == NULL)return "";
        try{ return string(hm->message.p, hm->message.len); }
        catch (...){ return ""; }
    }

    inline const string getProto()const
    {
        if (hm == NULL)return "";
        try{ return string(hm->proto.p, hm->proto.len); }
        catch (...){ return ""; }
    }

    inline const string getResponseStatusMsg()const
    {
        if (hm == NULL)return "";
        try{ return string(hm->resp_status_msg.p, hm->resp_status_msg.len); }
        catch (...){ return ""; }
    }

    inline int getResponseStatusCode()const
    {
        if (hm == NULL)return -1;
        try{ return hm->resp_code; }
        catch (...){ return -1; }
    }

    inline const string getMethod() const
    {
        if (hm == NULL) return "";
        try{ return string(hm->method.p, hm->method.len); }
        catch (...){ return ""; }
    }
};

static void hex_to_str(char *dst, unsigned char *src, int src_len)
{
    //函数功能: 十六进制转字符串
    for (int i = 0; i < src_len; i++)
    {
        sprintf(dst, "%02x", src[i]);
        dst += 2;
    }
}

#pragma region MG相关事件 
typedef std::unordered_map<int, std::string>     MG_EVENT;
const MG_EVENT rf_mg_event({
    //std::make_pair(MG_EV_POLL, "MG_EV_POLL"),               /* Sent to each connection on each mg_mgr_poll() call */
    std::make_pair(MG_EV_ACCEPT, "MG_EV_ACCEPT"),           /* New connection accepted. union socket_address * */
    std::make_pair(MG_EV_CONNECT, "MG_EV_CONNECT"),         /* connect() succeeded or failed. int *  */
    std::make_pair(MG_EV_RECV, "MG_EV_RECV"),               /* Data has been received. int *num_bytes */
    std::make_pair(MG_EV_SEND, "MG_EV_SEND"),               /* Data has been written to a socket. int *num_bytes */
    std::make_pair(MG_EV_CLOSE, "MG_EV_CLOSE"),             /* Connection is closed. NULL */
    std::make_pair(MG_EV_TIMER, "MG_EV_TIMER"),             /* now >= conn->ev_timer_time. double * */

    std::make_pair(MG_EV_HTTP_REQUEST, "MG_EV_HTTP_REQUEST"),   /* struct http_message * */
    std::make_pair(MG_EV_HTTP_REPLY, "MG_EV_HTTP_REPLY"),       /* struct http_message * */
    std::make_pair(MG_EV_HTTP_CHUNK, "MG_EV_HTTP_CHUNK"),       /* struct http_message * */
    std::make_pair(MG_EV_SSI_CALL, "MG_EV_SSI_CALL"),           /* char * */
    std::make_pair(MG_EV_SSI_CALL_CTX, "MG_EV_SSI_CALL_CTX"),   /* struct mg_ssi_call_ctx * */

    std::make_pair(MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, "MG_EV_WEBSOCKET_HANDSHAKE_REQUEST"),/* struct http_message * */
    std::make_pair(MG_EV_WEBSOCKET_HANDSHAKE_DONE, "MG_EV_WEBSOCKET_HANDSHAKE_DONE"),      /* struct http_message * */
    std::make_pair(MG_EV_WEBSOCKET_FRAME, "MG_EV_WEBSOCKET_FRAME"),                        /* struct websocket_message * */
    std::make_pair(MG_EV_WEBSOCKET_CONTROL_FRAME, "MG_EV_WEBSOCKET_CONTROL_FRAME"),        /* struct websocket_message * */

#if MG_ENABLE_HTTP_STREAMING_MULTIPART
    std::make_pair(MG_EV_HTTP_MULTIPART_REQUEST, "MG_EV_HTTP_MULTIPART_REQUEST"),              /* struct http_message */
    std::make_pair(MG_EV_HTTP_PART_BEGIN, "MG_EV_HTTP_PART_BEGIN"),                            /* struct mg_http_multipart_part */
    std::make_pair(MG_EV_HTTP_PART_DATA, "MG_EV_HTTP_PART_DATA"),                              /* struct mg_http_multipart_part */
    std::make_pair(MG_EV_HTTP_PART_END, "MG_EV_HTTP_PART_END"),                                /* struct mg_http_multipart_part */
    std::make_pair(MG_EV_HTTP_MULTIPART_REQUEST_END, "MG_EV_HTTP_MULTIPART_REQUEST_END"),      /* struct mg_http_multipart_part */
#endif

    std::make_pair(MG_EV_MQTT_CONNECT, "MG_EV_MQTT_CONNECT"),
    std::make_pair(MG_EV_MQTT_CONNACK, "MG_EV_MQTT_CONNACK"),
    std::make_pair(MG_EV_MQTT_PUBLISH, "MG_EV_MQTT_PUBLISH"),
    std::make_pair(MG_EV_MQTT_PUBACK, "MG_EV_MQTT_PUBACK"),
    std::make_pair(MG_EV_MQTT_PUBREC, "MG_EV_MQTT_PUBREC"),
    std::make_pair(MG_EV_MQTT_PUBREL, "MG_EV_MQTT_PUBREL"),
    std::make_pair(MG_EV_MQTT_PUBCOMP, "MG_EV_MQTT_PUBCOMP"),
    std::make_pair(MG_EV_MQTT_SUBSCRIBE, "MG_EV_MQTT_SUBSCRIBE"),
    std::make_pair(MG_EV_MQTT_SUBACK, "MG_EV_MQTT_SUBACK"),
    std::make_pair(MG_EV_MQTT_UNSUBSCRIBE, "MG_EV_MQTT_UNSUBSCRIBE"),
    std::make_pair(MG_EV_MQTT_UNSUBACK, "MG_EV_MQTT_UNSUBACK"),
    std::make_pair(MG_EV_MQTT_PINGREQ, "MG_EV_MQTT_PINGREQ"),
    std::make_pair(MG_EV_MQTT_PINGRESP, "MG_EV_MQTT_PINGRESP"),
    std::make_pair(MG_EV_MQTT_DISCONNECT, "MG_EV_MQTT_DISCONNECT"),

#if MG_ENABLE_COAP
    std::make_pair(MG_EV_COAP_CON, "MG_EV_COAP_CON"),
    std::make_pair(MG_EV_COAP_NOC, "MG_EV_COAP_NOC"),
    std::make_pair(MG_EV_COAP_ACK, "MG_EV_COAP_ACK"),
    std::make_pair(MG_EV_COAP_RST, "MG_EV_COAP_RST"),
#endif
});
#pragma endregion MG相关事件 

2、HTTP服务端

//http_server.h
#pragma once
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <mutex>
#include <atomic>
#include <string>
#include "http_message.h"

class HttpServer
{
public:
	HttpServer(int port)
	{
        m_handler_map.clear();
        m_websocket_session_set.clear();
        m_port = port;
		m_server_option.enable_directory_listing = "yes";
		m_server_option.document_root = "./web";// 网页根目录 

		// 其他http设置
		// 开启 CORS,本项只针对主页加载有效
		//m_server_option.extra_headers = "Access-Control-Allow-Origin: *";
	}

	~HttpServer() 
    {
        m_handler_map.clear();
        m_websocket_session_set.clear();
    }

	// 启动HttpServer,此方法将阻塞当前线程
	void Start(); 

    // 启动HttpServer,此方法不会阻塞当前线程
    void AsyncStart();

	// 关闭HttpServer
	void Close();

	// 添加事件处理函数
    void AddRequestHandler(const std::string &uri, RequestHandler req_handler);

	// 移除事件处理函数
	void RemoveRequestHandler(const std::string &uri);

    //事件响应回调
    void OnServerEvent(mg_connection *connection, int event_type, void *event_data);

private:
    //查找事件对应的处理函数
    RequestHandler FindHttpHandler(const std::string &uri);
   
    //发送回复信息
    void SendReply(mg_connection *connection, const std::string &reply);

public:
	//发送消息到指定的websocket连接
	void SendWebsocketMsg(mg_connection *connection, const std::string & msg);

	//广播消息到给所有的websocket连接
	void BroadcastWebsocketMsg(const std::string & msg);

private:
    inline void AddWebsocketConnection( mg_connection *connection);

    inline void RemoveWebsocketConnection( mg_connection *connection);

private:
    std::mutex m_mutex_handler;
    std::unordered_map<std::string, RequestHandler> m_handler_map;// 回调函数映射表,key 为uri
    std::mutex m_mutex_websocket;
    std::unordered_set<mg_connection *> m_websocket_session_set; //缓存websocket连接

	mg_mgr m_mgr; // 连接管理器  
	mg_serve_http_opts m_server_option;// 服务端选项
	std::atomic<bool> m_stop;
    int m_port;   // 服务端监听端口
};

static void CB_OnServerEvent(mg_connection *connection, int event_type, void *event_data, void * user_data)
{
	if (user_data)
	{
        ((HttpServer*)user_data)->OnServerEvent(connection, event_type, event_data);
	}
}
//http_server.cpp
#include <thread>
#include "http_server.h"
#include "glog/init_glog.h"

void HttpServer::Start()
{
    m_stop = false;
	mg_mgr_init(&m_mgr, NULL);
    string addr = "tcp://:" + to_string(m_port);
    mg_connection *connection = mg_bind(&m_mgr, addr.c_str(), CB_OnServerEvent, this);
	if (connection == NULL)
	{
		LOG_INFO_GLOG<< "fail to start http server, listen at port="<<m_port ;
		return;
	}
	mg_set_protocol_http_websocket(connection);// for both http and websocket
	LOG_INFO_GLOG<< "success to start http server, listen at port=" << m_port ;
	while (!m_stop)
	{
		mg_mgr_poll(&m_mgr, 1000); // 1S轮询一次是否有连接上来
	}
	LOG_INFO_GLOG<< "http server stopped" ;
}


void HttpServer::AsyncStart()
{
    std::thread th(std::bind(&HttpServer::Start,this));
    th.detach();
}

void HttpServer::Close()
{
    if (m_stop==false)
    {
        m_stop = true;
        mg_mgr_free(&m_mgr);
        LOG_INFO_GLOG << "close http server";
    }
}

void HttpServer::OnServerEvent(mg_connection *connection, int event_type, void *event_data)
{
	switch (event_type)
	{
	case MG_EV_HTTP_REQUEST:
	{
        //user todo
		http_message *hm = (struct http_message *)event_data;
		if (hm->message.p == NULL)
		{
			break;
		}

        char addr[32] = {0};
        mg_sock_addr_to_str(&connection->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
		std::string request = std::string(hm->message.p, hm->message.len);
        LOG_INFO_GLOG << "Recv Request from ["<<addr<<"] : " << request;

		std::string uri = std::string(hm->uri.p, hm->uri.len);
		auto request_handler = FindHttpHandler(uri);
		if (request_handler)
		{
			std::string reply_msg=request_handler(hm);
            if (!reply_msg.empty())
            {
                SendReply(connection, reply_msg);
            }
		}
		else
		{
			LOG_INFO_GLOG<< "bad request:" << uri ;
            mg_printf(connection,"%s",
                "HTTP/1.1 501 Not Implemented\r\n",
                "Content-Length: 0\r\n\r\n");

            // 发送空白字符快,结束当前响应
            mg_send_http_chunk(connection, "", 0);
		}
		//mg_serve_http(connection, hm, m_server_option);
	    break;
	}
	case MG_EV_WEBSOCKET_FRAME:
	{
        //user todo
		websocket_message *websocket_msg = (struct websocket_message *)event_data;
		if (websocket_msg->data==NULL)
		{
			break;
		}
		mg_str msg = { (char *)websocket_msg->data, websocket_msg->size };
		std::string request(msg.p, msg.len);
        LOG_INFO_GLOG << "recv websocket frame:\n" << request;
		break;
	}
	case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
	{
		// 获取连接客户端的IP和端口
        char addr[32] = {0};
		mg_sock_addr_to_str(&connection->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
		LOG_INFO_GLOG<< "recv websocket connect from client, address=" << addr ;
        AddWebsocketConnection(connection);
		break;
	}
	case MG_EV_CLOSE:
	{
		LOG_INFO_GLOG<< "client close connection" ;
        RemoveWebsocketConnection(connection);
		break;
	}
	default:
		break;
	}
}


void HttpServer::AddRequestHandler(const std::string &uri, RequestHandler req_handler)
{
    std::unique_lock<std::mutex> lk(m_mutex_handler);
	if (m_handler_map.find(uri) != m_handler_map.end())
	{
		return;
	}
	m_handler_map.emplace(uri, req_handler);
}

void HttpServer::RemoveRequestHandler(const std::string &uri)
{
    std::unique_lock<std::mutex> lk(m_mutex_handler);
	auto it = m_handler_map.find(uri);
	if (it != m_handler_map.end())
	{
		m_handler_map.erase(it);
	}
}

RequestHandler HttpServer::FindHttpHandler(const std::string &uri)
{
    std::unique_lock<std::mutex> lk(m_mutex_handler);
	auto it = m_handler_map.find(uri);
	if (it != m_handler_map.end())
	{
		return it->second;
	}
    return RequestHandler();
}

void HttpServer::SendReply(mg_connection *connection, const std::string & reply)
{
	// --- 未开启CORS
	mg_send_head(connection, 200, -1, NULL);

	mg_printf_http_chunk(connection, "{ \"result\": %s }", reply.c_str());

	// 发送空白字符快,结束当前响应
	mg_send_http_chunk(connection, "", 0);
    LOG_INFO_GLOG << "Send Reply: " << reply;

	/*
    // --- 开启CORS
	mg_printf(connection, "HTTP/1.1 200 OK\r\n"
	"Content-Type: text/plain\n"
	"Cache-Control: no-cache\n"
	"Content-Length: %d\n"
	"Access-Control-Allow-Origin: *\n\n"
    "%s\n", reply.size(), reply.c_str());
    */
}


void HttpServer::SendWebsocketMsg(mg_connection *connection,const std::string & msg)
{
	if (connection)
	{
		mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, msg.c_str(), strlen(msg.c_str()));
	}
}

void HttpServer::BroadcastWebsocketMsg(const std::string & msg)
{
    std::unique_lock<std::mutex> lk(m_mutex_websocket);
	for (mg_connection *connection : m_websocket_session_set)
	{
		if (connection)
		{
			mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, msg.c_str(), strlen(msg.c_str()));
		}
	}
}

void HttpServer::AddWebsocketConnection(mg_connection *connection)
{
    if (connection->flags & MG_F_IS_WEBSOCKET)
    {
        std::unique_lock<std::mutex> lk(m_mutex_websocket);
        m_websocket_session_set.insert(connection);
    }
}

void HttpServer::RemoveWebsocketConnection(mg_connection *connection)
{
    if (connection->flags & MG_F_IS_WEBSOCKET)
    {
        std::unique_lock<std::mutex> lk(m_mutex_websocket);
        if (m_websocket_session_set.find(connection) != m_websocket_session_set.end())
        {
            m_websocket_session_set.erase(connection);
        }
    }
}

3、HTTP客户端-长连接

//http_client_long.h
#pragma once
#include <string>
#include <atomic>
#include <thread>
#include <mutex>
#include "http_message.h"

class HttpClientLong
{
public:
	HttpClientLong();
	~HttpClientLong();

    inline void Start();
    
    inline void Stop();

	inline void SetResponseHandler(ResponseHandler handler)
    {  m_handler = handler; }

    void OnClientEventLong(struct mg_connection *connection, 
        int event_type, 
        void *event_data);

    void SendRequest(std::string &uri, 
        std::string & extra_header,
        const std::string & post_data);

    void SendWebsocketMsg(const std::string &uri,
        const string & msg, 
        ResponseHandler handler);
        
private:
	std::mutex m_mutex_mgr;
	mg_mgr m_mgr;
	std::thread m_thread_run;
    std::atomic<bool> m_exit;
	ResponseHandler m_handler;
    std::string m_remote_host;
    struct mg_connection * m_connection;
};

static void CB_OnClientEventLong(struct mg_connection *connection, int event_type, void *event_data, void * user_data)
{
	if (user_data)
	{
        ((HttpClientLong *)user_data)->OnClientEventLong(connection, event_type, event_data);
	}
}


void already_connect_http(struct mg_connection * nc,
MG_CB(mg_event_handler_t ev_handler, void *user_data),
const char *extra_headers,const char *post_data,
struct mg_str * user,struct mg_str * password,
struct mg_str * host, struct mg_str * path);
//http_client_long.cpp
#include "http_client_long.h"
#include "glog/init_glog.h"

HttpClientLong::HttpClientLong()
{
	Start();
}

HttpClientLong::~HttpClientLong()
{
    Stop();
}

void HttpClientLong::Start()
{
    std::unique_lock<std::mutex> lk(m_mutex_mgr);
    mg_mgr_init(&m_mgr, NULL);
    m_exit = false;
    m_connection = nullptr;
    m_remote_host.clear();
    std::thread th([this](){while (!m_exit){
        mg_mgr_poll(&m_mgr, 1000);
    }});
    m_thread_run = std::move(th);
}

void HttpClientLong::Stop()
{
    std::unique_lock<std::mutex> lk(m_mutex_mgr);
    m_exit = true;
    m_thread_run.join();
    mg_mgr_free(&m_mgr);
    m_connection = nullptr;
    m_remote_host.clear();
}

// 客户端的网络请求响应
void HttpClientLong::OnClientEventLong(struct mg_connection *connection, int event_type, void *event_data)
{
	switch (event_type)
	{
	case MG_EV_CONNECT:
	{
		int connect_status = *(int *)event_data;
		if (connect_status != 0)
		{
            LOG_INFO_GLOG << "fail to connect http server,ec= " << connect_status;
            m_connection = nullptr;
            m_remote_host.clear();
            //连接失败,事件循环线程并不退出
		}
		break;
	}
	case MG_EV_HTTP_REPLY:
	{
		http_message *hm = (struct http_message *)event_data;
		std::string rsp = std::string(hm->body.p, hm->body.len);
        LOG_INFO_GLOG << "Recv Reply:\n" << rsp;
		if (m_handler)
		{
			m_handler(hm);
		}
		break;
	}
	case MG_EV_CLOSE:
	{
		if (!m_exit)
		{
            LOG_INFO_GLOG << "Server closed connection";
            m_connection = nullptr;
            m_remote_host.clear();
		}
		break;
	}
    case MG_EV_WEBSOCKET_FRAME:
    {
        websocket_message *websocket_msg = (struct websocket_message *)event_data;
		if (websocket_msg->data == NULL)
		{
			break;
		}
        mg_str msg = { (char *)websocket_msg->data, websocket_msg->size };
        std::string request(msg.p, msg.len);
        LOG_INFO_GLOG << "recv ws frame:\n" << request;
        //DO SOME THING
        break;
    }
	default:
		break;
	}
}

// 发送一次请求,并回调处理,然后关闭本次连接
void HttpClientLong::SendRequest(
	std::string &uri,
    std::string & extra_header,
    const std::string & post_data)
{
    if (extra_header.find("Connection")==std::string::npos)
    {
        extra_header += "Connection: keep-alive\r\n";
    }
    
	struct mg_str url = mg_mk_str(uri.c_str());
	struct mg_str scheme = MG_NULL_STR;
	struct mg_str user = MG_NULL_STR;
	struct mg_str host = MG_NULL_STR;
	unsigned int port = 0;
	struct mg_str path = MG_NULL_STR;
	struct mg_str query = MG_NULL_STR;
	struct mg_str fragment = MG_NULL_STR;
	struct mg_str password = MG_NULL_STR;

	if (mg_parse_uri(url, &scheme, &user, &host, &port, &path, &query, &fragment)!=0)
	{
        LOG_INFO_GLOG << "err:请求格式有误";
		return;
	}

	if (query.len > 0) path.len += query.len + 1;

	if (scheme.len == 0 || mg_vcmp(&scheme, "http") == 0 ) 
	{
		if (port == 0) port = 80;
	}
	else if (mg_vcmp(&scheme, "https") == 0) 
	{
		if (port == 0) port = 443;
	}
	else 
	{
        LOG_INFO_GLOG << "err:请求格式有误";
		return; 
	}

    std::string remote_host = string(scheme.p,scheme.len)+"://"+
		string(user.p, user.len) + "@" +
		string(host.p, host.len) + ":" +to_string(port);
    
    if (post_data.empty())
    {
        LOG_INFO_GLOG << "Send [GET] Request to " << remote_host << " : uri=" << uri;
    }
    else
    {
        LOG_INFO_GLOG << "Send [POST] Request to " << remote_host << " : uri=" << uri;
    }

    if (!m_connection)
    {
        //连接后请求
        std::unique_lock<std::mutex> lk(m_mutex_mgr);
        m_remote_host = remote_host;
		m_connection = mg_connect_http(&m_mgr, CB_OnClientEventLong, this,
            uri.c_str(), extra_header.c_str(), post_data.c_str());
    }
    else if (m_remote_host != remote_host)
    {
        LOG_INFO_GLOG << "err:当前连接的对端不是用户请求的对端";
        return;
    }
    else 
    {
        //直接请求
        std::unique_lock<std::mutex> lk(m_mutex_mgr);
        already_connect_http(m_connection, CB_OnClientEventLong, this,
             extra_header.c_str(), post_data.c_str(),
			 &user, &password,&host,&path);
    }
}


void HttpClientLong::SendWebsocketMsg(
	const std::string &uri,
	const string & msg, 
	ResponseHandler handler)
{
    if (handler)
    {
        m_handler = handler;
    }

    /
    struct mg_str url = mg_mk_str(uri.c_str());
    struct mg_str scheme = MG_NULL_STR;
    struct mg_str user = MG_NULL_STR;
    struct mg_str host = MG_NULL_STR;
    unsigned int port = 0;
    struct mg_str path = MG_NULL_STR;
    struct mg_str query = MG_NULL_STR;
    struct mg_str fragment = MG_NULL_STR;
    struct mg_str password = MG_NULL_STR;

    if (mg_parse_uri(url, &scheme, &user, &host, &port, &path, &query, &fragment) != 0)
    {
        LOG_INFO_GLOG << "websocket err:请求格式有误";
        return;
    }

    if (scheme.len == 0 || mg_vcmp(&scheme, "http") == 0)
    {
        if (port == 0) port = 80;
    }
    else if (mg_vcmp(&scheme, "https") == 0)
    {
        if (port == 0) port = 443;
    }
    else
    {
        LOG_INFO_GLOG << "websocket err:请求格式有误";
        return;
    }

    std::string remote_host = string(scheme.p, scheme.len) + "://" +
        string(user.p, user.len) + "@" +
        string(host.p, host.len) + ":" + to_string(port);
    
    if (!m_connection )
    {
        //连接后请求-HandShake
        std::unique_lock<std::mutex> lk(m_mutex_mgr);
        m_remote_host = remote_host;
        m_connection = mg_connect_ws(&m_mgr, CB_OnClientEventLong, 
            this, uri.c_str(), NULL, NULL);
    }
    else if (m_remote_host != remote_host)
    {
        LOG_INFO_GLOG << "websocket err:当前连接的对端不是用户请求的对端";
        return;
    }
    else
    {
        //直接请求
        std::unique_lock<std::mutex> lk(m_mutex_mgr);
		mg_send_websocket_frame(m_connection, WEBSOCKET_OP_TEXT, msg.c_str(), msg.size());
    }
}


void already_connect_http(
struct mg_connection * nc, MG_CB(mg_event_handler_t ev_handler, void *user_data),
	const char *extra_headers, const char *post_data,
struct mg_str * user, struct mg_str * password,
struct mg_str * host, struct mg_str * path) {
	struct mbuf auth;
	mbuf_init(&auth, 0);
	if (user->len > 0) {//生成连接鉴权HEADER: Authorization
		mg_basic_auth_header(*user, *password, &auth);
	}

	if (post_data == NULL) post_data = "";
	if (extra_headers == NULL) extra_headers = "";
	if (path->len == 0) { *path = mg_mk_str("/"); }
	if (host->len == 0) { *host = mg_mk_str(""); }

	mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
		"\r\n%.*s%s\r\n%s",
		(post_data[0] == '\0' ? "GET" : "POST"), (int)path->len, path->p,
		(int)(path->p - host->p), host->p, strlen(post_data), (int)auth.len,
		(auth.buf == NULL ? "" : auth.buf), extra_headers, post_data);

	mbuf_free(&auth);
}

4、HTTP客户端-短连接

//http_client.h
#pragma once
#include <atomic>
#include "http_message.h"
//线程安全
class HttpClient
{
public:
	HttpClient() {}
	~HttpClient() {}

	inline void SetResponseHandler(ResponseHandler handler)
    { m_handler = handler;  }

    void OnClientEvent(struct mg_connection *connection,
        int event_type, 
        void *event_data);

    void SendRequest(const std::string &uri, 
        std::string & extra_header, 
        std::string & post_data, 
        uint32_t timeout_ms,
        ResponseHandler handler = nullptr);

private:
	ResponseHandler m_handler;//默认handle
};

struct HTTP_CLIENT_USER_DATA
{
    HttpClient * obj;//HttpClient对象
    bool exit=false;
    ResponseHandler handler;//每个请求的专属handle
};

static void CB_OnClientEvent(struct mg_connection *connection, int event_type, void *event_data, void * user_data)
{
	if (user_data)
	{
        ((HTTP_CLIENT_USER_DATA *)user_data)->obj->OnClientEvent(connection, event_type, event_data);
	}
}
//http_client.cpp
#include "http_client.h"
#include "glog/init_glog.h"

void HttpClient::OnClientEvent(struct mg_connection *connection, int event_type, void *event_data)
{
    auto user_data=(HTTP_CLIENT_USER_DATA *)(connection->user_data);
	switch (event_type)
	{
	case MG_EV_CONNECT:
	{
		int connect_status = *(int *)event_data;
		if (connect_status != 0)
		{
            LOG_INFO_GLOG << "fail to connect http server,ec= " << connect_status;
            user_data->exit = true;
		}
		break;
	}
	case MG_EV_HTTP_REPLY:
	{
        http_message *hm = (struct http_message *)event_data;
        if (hm->resp_code == 200)
        {
            if (user_data->handler)
            {
                user_data->handler(hm);
            }
            else if (m_handler)
            {
                m_handler(hm);
            }
        }
        connection->flags |= MG_F_SEND_AND_CLOSE;
        user_data->exit = true; // 每次收到请求后关闭本次连接,重置标记
        break;
	}
	case MG_EV_CLOSE:
	{
        if (!user_data->exit)
		{
            LOG_INFO_GLOG << "Server closed connection";
            user_data->exit = true;
		};
		break;
	}
	default:
		break;
	}
}

void HttpClient::SendRequest(const std::string &uri, 
    std::string & extra_header,
    std::string & post_data, 
    uint32_t timeout_ms,
    ResponseHandler handler)
{
    LOG_INFO_GLOG << "Send Request:" << uri;
    extra_header += "Connection: close\r\n";

    HTTP_CLIENT_USER_DATA user_data;
    user_data.obj = this;
    user_data.exit = false;
    user_data.handler = std::move(handler);

    mg_mgr mgr;
    mg_mgr_init(&mgr, NULL);
    auto connection = mg_connect_http(&mgr, 
        CB_OnClientEvent,
        &user_data, uri.c_str(), 
        extra_header.c_str(), 
        post_data.c_str());

    //回调和事件轮询在同一个线程
    while (user_data.exit==false && timeout_ms>0)
    {
        timeout_ms -= 500;
        mg_mgr_poll(&mgr, 500);
    }
    mg_mgr_free(&mgr);
}

5、代码说明

  • 以上代码用到了glog来记录日志。若你的开发环境中没有这个库,可以将其中相关的代码去除或采用std::cout来替代。

  • 使用时需要在mongoose中使能:MG_ENABLE_CALLBACK_USERDATA。

  • HTTP客户端-长连接HTTP服务端的实现是支持websocket的。

  • HTTP客户端-长连接HTTP客户端-短连接均支持线程安全。

  • mongoose中mg_connection *connection的实现方式类似链表,也就是说通过对一个连接进行遍历,得到其他连接。

    但本文中将HTTP客户端-长连接实现为:一个HttpClientLong对象对应一个连接m_connection,同时对应一个m_remote_host。

    若企图通过同一个HttpClientLong对象与不同的对端服务通信,将提示错误:"err:当前连接的对端不是用户请求的对端"

  • 下图为HTTP客户端-短连接访问HTTP服务端的抓包过程分析,客户端IP:10.33.38.71;服务端:10.33.38.128

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKwrM2Eu-1592141308987)(C:\Users\36262\AppData\Roaming\Typora\typora-user-images\image-20200601135819047.png)]

  • 下图为HTTP客户端-长连接访问HTTP服务端的抓包过程分析,客户端IP:10.33.38.71;服务端:10.33.38.128

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7mF6heKb-1592141308990)(C:\Users\36262\AppData\Roaming\Typora\typora-user-images\image-20200601141420421.png)]

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值