mongoose学习(一)Https通信

mongoose之Https通信

  • 前言
  • 配置opessl库
  • 开启SSL功能
  • code
    • 服务端
    • 客户端

前言

上一篇文章记录了下使用mongoose进行http通信。本打算进一步进行https通信,奈何最终仅成功搭建了https服务器,可以通过浏览器https请求访问;但自己编写的https客户端却无法与服务器通信成功。
(小编水平有限,还请谅解,先记录搭建https服务器的一些心得ヾ( ̄□ ̄;)ノ)

配置opessl库

配置HTTPS支持,需要使用OpenSSL库,

当然也可以使用gmssl(因为gmssl可替代openssl)。但这并不意味着可以使用gmssl中的密码套件,mongoose仅支持openssl中的密码套件,不支持gmssl特有的密码套件。原因如下
mongoose学习(一)Https通信_第1张图片
从mongoose源码可看出其构建上下文时使用的是SSLv23_client_method/ SSLv23_server_method,而对于gmssl应该使用GMTLS_client_method/GMTLS_server_method…所以也就不能支持gmtls。
----------------------------------OK,以上是题外话-------------------------------------------

回归正题,我们需要编译openssl/gmssl库,小编之前已编译过gmssl库(libcrypto.lib、libssl.lib),并写过相关的如何编译的文章,这里就直接“拿来主义”了,不再进行过多赘述,童鞋们可以查找资料自行编译…(σ°∀°)σ…
…在项目中引入gmssl库(链接头文件以及.lib文件等),这样openssl库就配置好了。
(注:小编是在windows平台下进行的编码测试)

开启SSL功能

mongoose默认ssl功能是关闭的,所以我们需要先开启ssl功能。
开启方法如下:(注.小编使用的是mongoose 6.13版本)
mongoose定义了MG_ENABLE_SSL宏来控制ssl功能的开启与关闭。修改mongoose.h头文件中MG_ENABLE_SSL的值为1,以开启SSL功能。
mongoose学习(一)Https通信_第2张图片

code

服务端

https服务端搭建流程与http类似,同样遵循服务器基本逻辑。区别在于需要使用mg_bind_opt函数来创建监听连接,通过mg_bind_opts函数中struct mg_bind_opts opts参数来指定ssl通信时所需的证书、私钥等参数。
mongoose学习(一)Https通信_第3张图片
服务端完整代码如下:

#include "stdafx.h"
#include "stdafx.h"
#include "mongoose.h"

#include 

static const char *s_http_port = "8443";
static const char *s_ssl_cert = "server.crt";
static const char *s_ssl_key = "server.key";
static const char *s_ssl_ca = "ca.crt";
static struct mg_serve_http_opts s_http_server_opts;

static void ev_handler(struct mg_connection *conn, int ev, void *ev_data)
{
     
	// 区分http和websocket
	if (ev == MG_EV_HTTP_REQUEST)
	{
     
		http_message *hm = (http_message *)ev_data;

		//(int)hm->message.len, hm->message.p 中存放客户端发过来的信息,包括post,Host(http地址),Content-Length(信息的长度),以及信息本身。
		//通过 std::string url = std::string(hm->uri.p, hm->uri.len); 可以得到url
		//通过 std::string body = std::string(hm->body.p, hm->body.len);可以得到body中 存储的从客户端发送过来的信息
		std::string req_str = std::string(hm->message.p, hm->message.len);
		printf("got request:\n%s\n", req_str.c_str());

		//TODO.  请求处理
		// eg.  We have received an HTTP request. Parsed request is contained in `hm`.
		//      Send HTTP reply to the client which shows full original request.
		mg_send_head(conn, 200, hm->message.len, "Content-Type: text/plain");
		mg_printf(conn, "%.*s", (int)hm->message.len, hm->message.p);

		//API函数mg_serve_http()可以轻松地从文件系统提供文件。 例如,为了创建一个从当前目录提供静态文件的Web服务器,实现如下处理:
		//mg_serve_http(conn, hm, s_http_server_opts);

	}
	else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE ||
		ev == MG_EV_WEBSOCKET_FRAME ||
		ev == MG_EV_CLOSE)
	{
     
		websocket_message *ws_message = (struct websocket_message *)ev_data;
		//TODO.  请求处理
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
     
	//声明和初始化事件管理器,  mg_mgr是拥有所有活动连接的事件管理器
	struct mg_mgr mgr;
	//mg_connection描述连接
	struct mg_connection *nc;

	struct mg_bind_opts bind_opts;
	const char *err;

	mg_mgr_init(&mgr, NULL);

	/* Set HTTP server options */
	memset(&bind_opts, 0, sizeof(bind_opts));
	bind_opts.error_string = &err;      //
	bind_opts.ssl_cert = s_ssl_cert;    //指定服务端证书
	bind_opts.ssl_key = s_ssl_key;      //指定服务端私钥
	//bind_opts.ssl_ca_cert = s_ssl_ca;  //提供了CA证书,表示需要验证客户端的证书

	printf("Starting SSL server on port %s, cert from %s, key from %s\n", s_http_port, bind_opts.ssl_cert, bind_opts.ssl_key);
	nc = mg_bind_opt(&mgr, s_http_port, ev_handler, bind_opts);
	if (nc == NULL)
	{
     
		printf("Failed to create listener: %s\n", err);
		getchar();
		return 1;
	}
	// Set up HTTP server parameters,  for both http and websocket
	mg_set_protocol_http_websocket(nc);
	s_http_server_opts.document_root = ".";  // Serve current directory
	s_http_server_opts.enable_directory_listing = "yes";

	//通过调用循环创建一个事件mg_mgr_poll()循环:
	for (;;) {
     
		mg_mgr_poll(&mgr, 1000);
	}
	mg_mgr_free(&mgr);

	return 0;
}

运行结果: (代码中我们是直接将接收到的request直接返回给客户端)
mongoose学习(一)Https通信_第4张图片

若通过bind_opts.ssl_ca_cert指定了CA证书,即表明服务端需要验证客户端的证书,此时客户端访问时需要提供其证书,否则无法与服务端建立连接。
由于这里我们使用的都是自己生成的证书,因此直接从浏览器中无法访问了。
mongoose学习(一)Https通信_第5张图片

客户端

理论上,客户端实现流程应该也与http类似,同样遵循服务器基本逻辑。区别在于若服务端需要认证客户端身份时,需通过mg_connect_http_opt/mg_connect_opt等函数进行参数配置。

理想很丰满,现实很残酷╮(︶﹏︶")╭鬼知道我经历了什么
小编实际编码时客户端无法连接到服务端。调试跟踪发现在ev_handler中触发了MG_EV_CONNECT信号,但是其得到的connect_status状态值为-3.
… (•ิ_•ิ)? 目前尚未找到原因,有关https客户端连接的内容待后续再更新了。
有知道的朋友,欢迎留言评论告知小编。

你可能感兴趣的:(mongoose学习,https,http,服务器)