mongoose使用详细 -- 如何通过mongoose搭建服务器

news2024/10/6 1:37:49

前言 

        授人以鱼不如授人以渔,这篇文章详细介绍了,对于一个从来没有听说过mongoose的小菜鸟如何快速了解和上手mongoose

        其他一些开源库可以借助类似的方法进行学习

提前需要准备的工具

1.官网文档  Mongoose :: Documentation

   官网提供了很多例子讲解,本文主要针对HTTP server/client和webSocket Server/client进行讲解。

2.下载源码 GitHub - cesanta/mongoose: Embedded Web Server

    下载最新版源码,本文主要通过源码中例子进行快速了解mongoose如何使用,以官网提供的文档进行辅助工具

3.阅读源码的工具(这里推荐Source Insight)

4.开发工具(IDE,本文使用VS2017,请根据自己的开发环境进行选择)

 通过source insight查看源码

对于如何使用source insight,安装下面截图的步骤进行基本不会有太大的问题,这里把工具也分享给大家:source insight4.0破解版

 ​​​​​​

 

 打开的整体页码:

mongoose简介

根据官网文档给出的描述大致总结下:

1.mongoose是一个用于C/C++的网络库,它为TCP、UDP、HTTP、WebSocket、MQTT实现了事件驱动的非阻塞API。Mongoose使嵌入式编程快速、健壮、简单。

2.mongoose可在windows、Linux、Mac和许多嵌入式架构上运行。它可以在现有的操作系统和TCP/IP堆栈(如FreeRTOS和lwIP)上运行,也可以在裸机上运行,利用Mongoose内置的TCP/IP堆栈和网络驱动程序。

如何使用mongoose

这部分官网提供了说明,根据2-minute integration guide这部分的说明,我们可以猜测下:

为了将Mongoose集成到现有的C/C++应用程序或固件中,请使用以下步骤:

1.将mongoose.c和mongoose.h复制到源代码中

使用起来很简单,只需要把mongoose.c和mongoose.h加入到源代码中就可以顺畅的使用了,不需要使用cmake和vs编译,神仙体验。

分析http-server

再来说下,为什么建议去看源码中的例子,而不是别人博客中的案例。版本问题,不同的版本可能使用的方法会有一些不同,不同的版本支持功能源有会有一些差异。

如何看源码:主--宾,动词最重要,也就是说函数最重要(函数就是一个动作)

重要的函数先摘出来:

signal(SIGINT, signal_handler);
mg_log_set(s_debug_level);
mg_mgr_init(&mgr);
mg_http_listen(&mgr, s_listening_address, cb, &mgr)
mg_casecmp(s_enable_hexdump, "yes")
MG_INFO(("Mongoose version : v%s", MG_VERSION));
mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);

我们看下函数原型:

1===================

_ACRTIMP _crt_signal_t __cdecl signal(_In_ int _Signal, _In_opt_ _crt_signal_t _Function);

可以猜测应该是为某一个信号增加一个控制方式: signal(SIGINT, signal_handler);

为SIGINT这个信号做一个处理动作,SIGINT是ctrl+C时触发

我们看源码中signal_handler的信号捕捉函数:

typedef void (__CRTDECL* _crt_signal_t)(int);

2===================

mg_log_set(s_debug_level);

设置日志等级,如果源码中没有给出注释,可以查看官网中的文档

 这里应该是把日志设置成了INFO和ERROR等级

3=======================

mg_mgr_init(&mgr);

函数原型:

void mg_mgr_init(struct mg_mgr *mgr)

 根据文档描述可以知道,他是为事件管理器mg_mgr进行初始化的,初始化工作做了什么:

 1.将活动连接列表设置为NULL

 2.设置默认的DNS server为IPv4和IPv6

 3.设置默认的DNS查找超时

什么是mg_mgr呢?

事件管理结构,包含活动连接的列表以及一些内务管理信息

4=============================== 

mg_http_listen(&mgr, s_listening_address, cb, &mgr)

函数原型:

struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data);

创建一个HTTP服务

参数:

mgr:一个事件管理器

url:附加一个本机的IP和监听的端口,http://0.0.0.0:8000

fn:回调函数,当有监听到有连接进来的时候,就执行回调函数

fn_data:传给回调函数的参数

返回值:指向已创建连接的指针,或出现错误时为NULL

5===============================

mg_casecmp(s_enable_hexdump, "yes")

函数原型:

int mg_casecmp(const char *s1, const char *s2);

不分大小写的比较两个以NULL结束的字符串

参数:s1,s2指向这两个字符串的指针

返回值:如果返回0则说明这两个字符串相等,如果大于0则s1>s2,如果小于0则s1<s2

6===============================

MG_INFO(("Mongoose version : v%s", MG_VERSION));

函数原型:

#define MG_INFO(args) MG_LOG(MG_LL_INFO, args)

写日志

7==============================

mg_mgr_poll(&mgr, 1000);

函数原型:

void mg_mgr_poll(struct mg_mgr *mgr, int ms);

执行轮询迭代,对于在监听链表中的每一个连接

1.查看是否有传入数据,如果有,将其读入到读缓冲区中,并发送 MG_EV_READ事件

2.查看是否有数据在写缓冲区,并且写入,发送MG_EV_WRITE事件

3.如果连接正在侦听,则接受传入连接(如果有),并向其发送MG_EV_accept事件

4.发送MG_EV_POLL事件

参数:

mgr:一个事件管理器

ms:超时时间(毫秒)

8===============================

mg_mgr_free(&mgr);

函数原型:

void mg_mgr_free(struct mg_mgr *mgr);

关闭所有的连接,释放所有的资源


结合上面的描述我们做个总结:

日志部分我们这里不做探究,直接跳过。

1.我们需要准备一个事件管理器,有两个步骤:

struct mg_mgr mgr;

mg_mgr_init(&mgr);

2.然后对这个事件管理器设置监听事件并且指定IP和PORT:

准备一个回调函数 ==> 回调函数内部我们之后探究

准备指定的IP和端口

mg_http_listen();

3.调用消息循环

mg_mgr_poll();       // 指定阻塞的时长

4.停止循环和释放资源

mg_mgr_free()

HTTP SERVER - V1.0

// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved

#include <signal.h>
#include "mongoose.h"


static const char *s_listening_address = "http://0.0.0.0:8000";


static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
	
	if (ev == MG_EV_HTTP_MSG) {
		printf("有数据到达\n");
	}

	(void)fn_data;
}


int main(int argc, char *argv[]) {

	struct mg_mgr mgr;
	struct mg_connection *c;
	
	mg_mgr_init(&mgr);
	if ((c = mg_http_listen(&mgr, s_listening_address, cb, &mgr)) == NULL) {
		exit(EXIT_FAILURE);
	}

	// Start infinite event loop
	while (1) mg_mgr_poll(&mgr, 1000);

	mg_mgr_free(&mgr);
	
	return 0;
}

客户端用postman进行测试就可以了,这里不自己写客户端代码了

HTTP SERVER - v2.0

现在我们对回调函数好好分析下:

函数原型,mongoose是通过函数指针来实现回调的,其原理跟信号捕捉函数一样。

typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, void *ev_data, void *fn_data);

 

官方文档给出了结构体中各个成员的解释我们经常会用到的:POST请求和GET请求

// 区别是POST请求还是GET请求
if (strstr(hm->method.ptr, "POST"))
{
	printf("这是POST请求\n");
	mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{%s:%s}", "status", "okk");
}
else if(strstr(hm->method.ptr, "GET"))
{
	printf("这是GET请求\n");
	mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{%s:%s}", "status", "okk");
}
else
{
	mg_http_reply(c, 500, NULL, "{%s:%s}", "status", "已经收到GET请求");
}

有时候会根据uri进行不同的处理: /hello

if (mg_http_match_uri(hm, "/hello"))
{
    if (strstr(hm->method.ptr, "POST"))
    {
	    printf("这是POST请求\n");
        mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{%s:%s}", "status", "okk");
    }
	else if (strstr(hm->method.ptr, "GET"))
    {
		printf("这是GET请求\n");
		mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{%s:%s}", "status", "okk");
	}
	else
	{
		mg_http_reply(c, 500, NULL, "{%s:%s}", "status", "已经收到GET请求");
	}
}
else if (mg_http_match_uri(hm, "/app"))
{
		printf("------------------\n");
}

mongoose给出了几种不同的方式发送消息:

int mg_printf(struct mg_connection *, const char *fmt, ...);

void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...);

官网给出了很多函数,这些都是关于HTTP,官网有详细的介绍。

 这里就不每一个都给大家解释了

HTTP-CLIENT

在mongoose的HTTP客户端中内部自动帮我们发送连接,因为数据可能比较复杂。在建立连接的时候会发送一个MG_EV_CONNECT事件,我们在回调函数中对该事件进行处理。

客户端代码与服务端基本相同,唯一不同的是连接和监听连接不同,其他基本相同

#include <stdio.h>
#include <string.h>
#include "mongoose.h"

static const char *s_url = "http://127.0.0.1:8080/hello";

void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
{
	const char *post_data = "aaaa";

	// 连接事件
	if (MG_EV_CONNECT == ev)
	{
		struct mg_str host = mg_url_host(s_url);   // 解析出主机
		
		// 发送请求
		mg_printf(c, "%s %s HTTP/1.1\r\n"
			         "Host:%.*s\r\n"
			         "\r\n"
			         "%.*s", post_data == NULL ? "GET":"POST", 
			         mg_url_uri(s_url), 
			        (int)host.len, host.ptr, 
			        post_data == NULL ? 0:(int)strlen(post_data), 
			        post_data);
	}

	// 接受到回应
	if (MG_EV_HTTP_MSG == ev)
	{
		struct mg_str host = mg_url_host(s_url);   // 解析出主机

		struct mg_http_message *hm = (struct mg_http_message *)ev_data;
		printf("%s", hm->message);

		mg_printf(c, "%s %s HTTP/1.1\r\n"
			"Host:%.*s\r\n"
			"\r\n"
			"%.*s", post_data == NULL ? "GET" : "POST",
			mg_url_uri(s_url),
			(int)host.len, host.ptr,
			post_data == NULL ? 0 : (int)strlen(post_data),
			post_data);
	}
}

int main(void)
{

	// 1.创建一个
	struct mg_mgr mgr;
	mg_mgr_init(&mgr);
    
	// 2.连接HTTP服务器
	mg_http_connect(&mgr, "127.0.0.1:8080", cb, NULL);

	while (1)
	{
		// 消息循环
		mg_mgr_poll(&mgr, 1000);
	}

	// 释放资源
	mg_mgr_free(&mgr);

	return 0;
}

 WEBSOCKET-SERVER

可能很多朋友对webSocket协议不是很了解,这里简单说下:

webSocket协议

        本质上与HTTP协议类型,webSocket是一种全双工通信的协议,是基于HTTP协议的不足提出的一种新的通信协议。

        http是一种单向的应用层协议,它采用了请求响应模型,通信请求只能由客户端发起,服务端对请求做出应答处理。这样的弊端显然是很大的,只要服务器状态连续变化,客户端就必须实时响应,这样显然很麻烦,同时轮询的效率低,非常的浪费资源。

        webSocket是一种全面双工通讯的网络技术,任意一方都可以建立连接将数据推向另一方,webSocket只需要建立一次连接就可以一直保持。
mongoose对webSocket的处理

        mongoose的处理很简单,还是通过HTTP进行处理的,只不过在回调函数中会发送一个MG_EV_WS_MSG的事件,通过mg_ws_upgrade()将数据转换成ws数据进行处理,当调用mg_ws_upgrade会发送一个MG_EV_WS_MSG事件。

#include "mongoose.h"

static const char *s_listen_on = "ws://localhost:8080";

static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
	//if (ev == MG_EV_OPEN) {
	//	// c->is_hexdumping = 1;
	//}
	//else 
	if (ev == MG_EV_HTTP_MSG) {
		struct mg_http_message *hm = (struct mg_http_message *) ev_data;

		if (mg_http_match_uri(hm, "/websocket")) {
			// Upgrade to websocket. From now on, a connection is a full-duplex
			// Websocket connection, which will receive MG_EV_WS_MSG events.
			mg_ws_upgrade(c, hm, NULL);
		}
	}
	else if (ev == MG_EV_WS_MSG) {
		// Got websocket frame. Received data is wm->data. Echo it back!
		struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
		printf("%s", wm->data);
		mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
	}
	(void)fn_data;
}

int main(void) {
	struct mg_mgr mgr;  // Event manager
	mg_mgr_init(&mgr);  // Initialise event manager
	
	mg_http_listen(&mgr, s_listen_on, fn, NULL);  // Create HTTP listener
	while (1)
	{
		mg_mgr_poll(&mgr, 1000);             // Infinite event loop
	}

	mg_mgr_free(&mgr);
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/483614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Pandas] 查看DataFrame的常用属性

导入数据 import pandas as pddf pd.DataFrame([[L123,A,0,123],[L456,A,1,456],[L437,C,0,789],[L112,B,1,741],[L211,A,0,852],[L985,B,1,963]],columns[Material,Level,Passing,LT]) df 1.dtypes: 查看DataFrame中各列的数据类型 df.dtypes会返回每个字段的数据类型及Da…

C++练级之初级:第六篇

类和对象入门级&#xff1a;第六篇 1.类的引入2.类的定义2.1类的访问限定符2.2类的封装2.3类的实例化 3.如何计算类或者对象的大小4.this指针 总结 我们知道&#xff0c;C在C语言的基础上引入了对象的概念&#xff0c;那么从本篇开始进入类和对象&#xff1b; 1.类的引入 &…

【JavaEE】_1.多线程(1)

目录 1.操作系统 2. 进程 3. CPU分配——进程调度 3.1 操作系统对进程的管理 3.2 PCB的属性 3.2.1 基础属性 3.2.2 实现进程调度的属性 4. 内存分配——内存管理 4.1 虚拟地址空间 4.2 进程间通信 5. 线程 5.1 线程的概念 5.2 创建与使用多线程 5.2.1 方式1&a…

【数据结构】八大排序(一)

&#x1f61b;作者&#xff1a;日出等日落 &#x1f4d8; 专栏&#xff1a;数据结构 珍惜自己的时间&#xff0c;利用好每一份每一秒。做事不放过没一个细节&#xff0c;小心谨慎&#xff0c;细致&#xff0c;能够做到这些&#xff0c;还有什么是不可能的呢? 目录 ​编辑 ✔…

【刷题之路Ⅱ】LeetCode 61. 旋转链表

【刷题之路Ⅱ】LeetCode 61. 旋转链表 一、题目描述二、解题1、方法1——移动部分链表1.1、思路分析1.2、代码实现 2、方法1——闭合为环2.1、思路分析2.2、代码实现 一、题目描述 原题连接&#xff1a; 61. 旋转链表 题目描述&#xff1a; 给你一个链表的头节点 head &#x…

【Python | matplotlib】matplotlib.cm的理解以及举例说明

文章目录 一、模块介绍二、颜色举例 一、模块介绍 matplotlib.cm是Matplotlib中的一个模块&#xff0c;它提供了一组用于处理颜色映射&#xff08;colormap&#xff09;的函数和类。颜色映射是一种将数值映射到颜色的方法&#xff0c;常用于制作热力图、等值线图、散点图等。 …

软件工程实验:原型设计

目录 前言实验目的实验要求实验过程系统原型绘制生成html代码 总结 前言 本次实验的主题是原型设计&#xff0c;即根据用户需求和系统功能&#xff0c;设计一个简单的软件原型&#xff0c;展示系统的界面和交互方式。原型设计是软件工程中的一种重要技术&#xff0c;它可以帮助…

深入探索PyTorch中的自动微分原理及梯度计算方法

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

如何完全卸载linux下通过rpm安装的mysql

卸载linux下通过rpm安装的mysql 1.关闭MySQL服务2.使用 rpm 命令的方式查看已安装的mysql3. 使用rpm -ev 命令移除安装4. 查询是否还存在遗漏文件5. 删除MySQL数据库内容 1.关闭MySQL服务 如果之前安装过并已经启动&#xff0c;则需要卸载前请先关闭MySQL服务 systemctl stop…

Tomcat整体架构解析

一、Tomcat整体架构介绍 Tomcat是一个开源的轻量级web应用服务器。整体架构如下&#xff1a; Tomcat中最顶层的容器是Server&#xff0c;即代表一个Tomcat服务器&#xff0c;一个Server中可以有多个Service&#xff0c;对外提供不同的web服务。Service是对Connector和Contain…

电话号码的字母组合

题目&#xff1a;17. 电话号码的字母组合 - 力扣&#xff08;Leetcode&#xff09; 思路&#xff1a; 给定一个电话号码字符串 digits&#xff0c;须输出它所能表示的所有字母组合。我们可以先定义一个数字字符到字母表的映射表 numToStr&#xff0c;然后再用 Combine 函数递归…

【Linux专区】 环境搭建 | 带你白嫖七个月阿里云服务器

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《Linux专区》&#x1f448; 前言&#xff1a; 工欲善其事必先利其器&#xff0c;没个Linux环境怎么愉快地学Linux&#xff1f;这期就先带大家把环境搞好&#xf…

物联网系统中常见的通信协议分析

物联网&#xff08;Internet of Things, 简称IoT&#xff09;是指将各种传感器、设备等通过互联网连接起来&#xff0c;形成一个庞大的网络&#xff0c;实现物与物之间的互联互通。在实现这个过程中&#xff0c;各种不同的通信协议被广泛应用。本文将为大家介绍物联网中常见的通…

[架构之路-185]-《软考-系统分析师》-3-操作系统基本原理 - 文件索引表

目录 一、文件的索引块。 二、索引分配表 三、索引表的链接方案 四、多层索引 五、混合索引分配 一、文件的索引块。 存放在目录中的文件&#xff0c;并非是文件的真实内容。 目录中记录了文件的索引块是几号磁盘块。 文件对应的索引表是存放在指定的磁盘块中的&#x…

CSI指纹预处理(中值、均值、Hampel、小波滤波)

目录 1、前言 2、中值滤波器 3、均值滤波器 4、Hampel滤波器 5、小波变换滤波器 1、前言 因为设备、温度和实验室物品摆设等因素的影响&#xff0c;未经处理的CSI数据不能直接使用&#xff0c;需要对数据进行异常值处理以保证数据的稳定性&#xff0c;同时减少环境中人的…

云原生Istio架构和组件介绍

目录 1 Istio 架构2 Istio组件介绍2.1 Pilot2.2 Mixer2.3 Citadel2.4 Galley2.5 Sidecar-injector2.6 Proxy(Envoy)2.7 Ingressgateway2.8 其他组件 1 Istio 架构 Istio的架构&#xff0c;分为控制平面和数据面平两部分。 - 数据平面&#xff1a;由一组智能代理&#xff08;[En…

Eclipse改SSH项目,修改java代码无效

遇到了一个大坑&#xff0c;记录一下… 坑1&#xff1a;修改后台代码总是没用… 1.背景&#xff1a; Eclipse运行SSH项目&#xff08;StrutsSpringHibernate&#xff09;&#xff0c;修改SQL语句&#xff0c;但是前端查询的结果没变化…(例如&#xff0c;在sql里加上 where …

LeetCode279之完全平方数(相关话题:动态规划,四平方和定理)

题目描述 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。 示例 1: 输入:n = 12 输出:3 解释:12 = 4 + 4 +…

【Android构建篇】MakeFile语法

前言 对于一个看不懂Makefile构建文件规则的人来说&#xff0c;这个Makefile语法和shell语法是真不一样&#xff0c;但是又引用了部分shell语法&#xff0c;可以说是shell语法的子类&#xff0c;Makefile语法继承了它。 和shell语法不一样&#xff0c;这个更难一点&#xff0…

Vue3基本知识点

为什么要学vue3 1、Vue是国内 最火的前端框架 2、Vue3是2020年09月18日正式发布的 目前以支持Vue3的UI组件库 库名称简介ant-design-vuePC 端组件库&#xff1a;Ant Design 的 Vue 实现&#xff0c;开发和服务于企业级后台产品arco-design-vuePC 端组件库&#xff1a;字节跳…