- 作者
- 赵曦
- 日期
- 2025/09/25
- 版本
- 1.0
上一篇教程:传输层设施 —— Socket
下一篇教程:工业自动化通信协议 —— OPC UA
相关类
1 HTTP 数据结构
相关类: rm::Request 及 rm::Response
两个类均提供了
- generate 成员函数,用于从请求/响应结构生成 HTTP 格式的字符串,在调用此方法后可直接通过 Socket 发送数据
- parse 静态成员函数,用于从 HTTP 格式的字符串解析出请求/响应结构,在接收到 Socket 原始数据后可调用此方法进行解析
rm::Response 还提供了多种简化生成响应的成员函数,如 send、json、redirect 等。
2 HTTP 请求工具
相关函数:
设计理念参考 Python 的 requests 库,提供了极其简洁易用的接口,这里给出一个简单的 GET 请求示例:
#include <rmvl/io/netapp.hpp>
int main() {
if (response) {
printf("Response: %s\n", response->body().c_str());
}
}
Response get(std::string_view url, const std::vector< std::string > &querys={}, const std::unordered_map< std::string, std::string > &heads={})
发出同步 GET 请求
定义 netapp.hpp:282
异步请求的使用方法类似,只需将 requests 替换为 async::requests,并在协程中使用 co_await 关键字等待结果,这里不再赘述。
3 Web 后端框架
RMVL 提供了后端应用框架 rm::async::Webapp ,以及负责传输层监听的 rm::async::HttpServer 和 rm::async::HttpsServer 。设计参考 Express JS,具体来说,Webapp 负责路由与中间件,服务器对象负责 HTTP 或 HTTPS 监听,同一个 Webapp 可以交给不同类型的服务器。
3.1 HTTP 服务器
rm::async::Webapp 和 rm::Router 提供了 GET、POST、DELETE 等相关方法用于定义路由,并且 rm::async::Webapp 可以使用 use 方法挂载中间件或挂载由 rm::Router 定义的子路由。
#include <rmvl/io/netapp.hpp>
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <port>\n", argv[0]);
return 1;
}
uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));
res.
send(
"<html><body><h1>Hello, World!</h1></body></html>");
});
});
{"key", "value"},
{"message", "This is a test API."},
});
});
auto name = req.
params.at(
"name");
{"greeting", "Hello, " + name + "!"},
});
});
app.use("/api", api_router);
server.listen(port, [&]() {
printf("Server is running on port %d\n", port);
});
}
HTTP 与 WebSocket 请求路由
定义 netapp.hpp:409
void get(std::string_view uri, RouteHandler callback)
Get 请求路由
定义 netapp.hpp:461
HTTP 服务器
定义 netapp.hpp:741
异步 I/O 执行上下文,负责管理 IO 事件循环和协程任务的调度
定义 async.hpp:205
Web 应用程序框架
定义 netapp.hpp:603
ResponseMiddleware cors()
【中间件】跨域资源共享 CORS,为响应添加 CORS 头部信息
void co_spawn(IOContext &ctx, Callable &&fn, Args &&...args)
在指定的执行上下文中生成并调度一个协程任务
定义 async.hpp:305
HTTP 请求结构
定义 netapp.hpp:75
std::unordered_map< std::string, std::string > params
路径参数
定义 netapp.hpp:94
HTTP 响应结构
定义 netapp.hpp:109
Response & json(const ::rm::json &j)
发送 JSON 格式的响应数据
Response & redirect(std::string_view url)
发送重定向响应
定义 netapp.hpp:169
Response & send(std::string_view str)
发送响应数据
可以简单使用以下命令行进行编译
g++ demo.cpp /usr/local/include/RMVL rmvl_io rmvl_core demo
- 注解
- 当然,也可以自行编写 CMakeLists.txt 文件,采用 CMake 构建系统进行构建,具体的 CMakeLists.txt 内容可参考如下内容:
cmake_minimum_required(VERSION 3.16)
project(Demo)
find_package(RMVL REQUIRED)
rmvl_add_exe(
demo
SOURCES demo.cpp
DEPENDS io
)
编译后,在终端输入
即可创建 Web 后端服务,并且监听 8080 端口,可以使用 curl 工具或浏览器访问 http://localhost:8080/ 来测试服务器。
curl "http://localhost:8080"
会得到如下输出:
htmlbodyh1Hello, World!/h1/body/html
- 注解
- rm::async::Webapp 虽然也提供了 listen 方法,但该方法仅用于快速测试,实际生产环境推荐使用 rm::async::HttpServer 或 rm::async::HttpsServer 进行监听。
WebSocket 支持
rm::async::Webapp 支持通过 ws 方法注册 WebSocket 路由。WebSocket 路由的回调函数是一个协程函数,在 HTTP Upgrade 握手成功后被调用,回调中可以通过 rm::async::WebSocket 的 recv 和 send 方法接收、发送文本消息。
下面的例子创建了一个简单的 Echo WebSocket 服务,客户端向 /ws/echo 发送文本消息后,服务端会原样返回。
#include <rmvl/io/netapp.hpp>
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <port>\n", argv[0]);
return 1;
}
uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));
res.
send(
"WebSocket echo server is running.");
});
std::string
msg =
co_await ws.
recv();
break;
}
});
server.listen(port, [&]() {
printf("WebSocket server is running on port %d\n", port);
});
}
保有 rm::async::Promise 的异步协程任务
定义 async.hpp:148
WebSocket 连接对象接口
定义 netapp.hpp:376
bool is_open() const noexcept
获取连接是否活跃
定义 netapp.hpp:392
Task< bool > send(std::string_view message)
异步发送文本消息
Task< std::string > recv()
异步接收消息,这是一个挂起点,直到收到客户端消息或连接关闭
LPSS 消息类型命名空间
定义 node.hpp:253
编译方式与前面的 HTTP 服务示例相同。运行后,可以使用 wscat 等 WebSocket 客户端进行测试:
./ws_demo 8080
wscat "ws://127.0.0.1:8080/ws/echo"
在客户端输入任意文本并回车,即可看到服务端返回相同的文本内容。
WebSocket 路由同样支持路径参数,参数会写入 Request::params,例如:
const auto room = req.
params.at(
"room");
std::string
msg =
co_await ws.
recv();
break;
co_await ws.
send(
"[" + room +
"] " +
msg);
}
});
- 注解
- 当前 WebSocket 接口面向文本消息,recv 在连接关闭或收到关闭帧时返回空字符串;若业务需要发送空字符串,请在应用层自行约定消息格式。
3.2 HTTPS 服务器
HTTPS 与 HTTP 使用相同的 Webapp,只需要将 rm::async::HttpServer 替换为 rm::async::HttpsServer ,并提供服务端的 rm::SSLContext 即可,示例代码如下:
res.
send(
"Hello, HTTPS!");
});
if (!ssl_context.
load_cert(
"server.crt",
"server.key")) {
printf(
"%s\n", ssl_context.
lasterr().c_str());
return 1;
}
server.listen(8443, [] {
printf("HTTPS server is listening on 8443\n");
});
std::string lasterr() const
获取最近一次错误信息
定义 ssl.hpp:73
bool load_cert(std::string_view cert_file, std::string_view key_file)
加载证书与私钥文件
static SSLContext server()
创建服务端 TLS 上下文
定义 ssl.hpp:64
HTTPS 服务器
定义 netapp.hpp:787
SSLContext 必须比 HttpsServer 和所有活动 TLS 连接存活得更久。证书自动续期后,需要重新加载 SSLContext 或重启/重载服务进程,才能让新连接使用更新后的证书。 使用命令行直接编译 HTTPS 程序时,还需要链接 OpenSSL 的 ssl 和 crypto 库;使用 CMake 链接 rmvl_io 目标时会自动传递该依赖。
测试自签名证书时可以使用:
curl "https://127.0.0.1:8443/"
对于 wss,即 TLS 加密的 WebSocket,若使用 HTTPS 服务器,WebSocket 路由的注册方式不变,只需要将服务端换成 rm::async::HttpsServer,客户端使用 wss:// 地址连接即可。