目录
一.httplib
话不多说,我们直接安装httplib库
git clone https://github.com/yhirose/cpp-httplib
它的使用过程非常简单:只需在代码中包含httplib.h文件即可!也就是下面这个文件,我们把它单独拿出来放到我们这个项目专门存放第三方库的地方


接下来如果想要使用httplib库,那么只需要在文件里面包含这个头文件即可。
1.1.httplib库简介
httplib 是一个轻量级的 C++ 库,用于创建 HTTP 服务器和客户端。它的设计简单而灵活,非常容易设置。只需在代码中包含 httplib.h 文件即可!
使得开发者能够在自己的 C++ 项目中轻松地实现基本的 HTTP 通信功能。
| 特性 | 说明 |
|---|---|
| 简单易用的接口 | httplib 提供了简单易用的接口,使得创建 HTTP 服务器和客户端变得非常容易。通过少量的代码,你就能够建立起一个功能完善的 HTTP 服务器或客户端。 |
| 支持主要 HTTP 方法 | httplib 支持主要的 HTTP 方法,包括 GET、POST、PUT、DELETE 等,让你能够处理各种类型的 HTTP 请求。 |
| 灵活的路由处理 | 使用 httplib,你可以轻松地定义路由和处理函数,根据不同的 URL 请求调用相应的处理函数。这使得构建 RESTful API 或者 Web 应用程序变得非常方便。 |
| 支持静态文件服务 | httplib 还提供了静态文件服务的功能,你可以将指定目录下的文件直接暴露给客户端访问,而无需额外处理。 |
| 简洁的代码库 | httplib 的代码库非常简洁,没有过多的依赖,易于集成到你的项目中。这使得它成为了许多开发者选择的首选 HTTP 库之一。 |
| 活跃的开发和社区支持 | httplib 的开发仍在持续进行中,它的代码库得到了广大开发者社区的支持和贡献,因此可以期待它会持续改进和更新。 |
总的来说,httplib 是一个功能强大且易于使用的 C++ HTTP 库,适用于各种类型的项目,无论是小型工具还是大型 Web 应用程序。通过它,你可以快速地搭建起自己的 HTTP 服务器或客户端,实现各种网络通信需求。
1.2.httplib内部核心结构讲解
1.2.1.httplib请求类
还记得HTTP请求报头吗

httplib中封装了一个结构------Request,用来保存客户端/浏览器发送的HTTP请求的内容。当服务器获取到客户端的请求,httplib会将请求字符串解析成Request
cpp
struct Request {
//成员变量
std::string method;//请求方法
std::string path;//请求资源路径------URL
Headers headers;//头部字段
std::string body;//正文部分
std::string version;//协议版本
Params params;//查询字符串
MultipartFormDataMap files;//客户端上传文件的信息
Ranges ranges;//实现断点续传的请求区间
//提供的API
//查询头部字段key是否存在
bool has_header(const char *key) const;
//获取头部字段的value
std::string get_header_value(const char *key, size_t id = 0) const;
//设置头部字段
void set_header(const char *key, const char *val);
//查询客户端是否上传该文件
bool has_file(const char *key) const;
//获取文件信息
MultipartFormData get_file_value(const char *key) const;
};
其中有一个文件结构体和文件结构体数组
cpp
struct MultipartFormData {
std::string name;//字段名称
std::string content;//文件内容
std::string filename;//文件名
std::string content_type;//文件类型
};
//文件结构体数组
using MultipartFormDataItems = std::vector<MultipartFormData>;
1.2.3.httplib响应类
大家应该都记得http响应报头吧

httplib响应类是Responce,需要用户手动填补,httplib返回响应时,会将Responce组织成字符串返回给客户端
cpp
struct Response {
std::string version; //协议版本号,默认时http1.1
int status = -1; //响应状态码,
std::string reason;
Headers headers; //响应报头
std::string body; //响应正文
std::string location; // 重定向位置
//两个比较重要的API
//以key-val将相应的字段设定到响应报头中
void set_header(const char *key, const char *val);
//设置正文------可以设置类型
void set_content(const std::string &s, const char *content_type);
};
1.2.3.Server类
Server类就是httplib中用于搭建服务器的类
cpp
class Server {
//Handler一个函数指针,它的返回值为void,参数是Request,和Response
using Handler = std::function<void(const Request &, Response &)>;
//Handlers是一个映射表,将请求资源和处理函数映射在一起
using Handlers = std::vector<std::pair<std::regex, Handler>>;
//将Get方法的请求资源与处理函数加载到Handlers表中
Server &Get(const std::string &pattern, Handler handler);
Server &Post(const std::string &pattern, Handler handler);
Server &Put(const std::string &pattern, Handler handler);
Server &Patch(const std::string &pattern, Handler handler);
Server &Delete(const std::string &pattern, Handler handler);
Server &Options(const std::string &pattern, Handler handler);
//线程池
std::function<TaskQueue *(void)> new_task_queue;
//搭建并启动http
bool listen(const char *host, int port, int socket_flags = 0);
};
首先,Handler是一个回调的函数指针。Handlers是一个映射表。std::regex是正则表达式,用于匹配请求资源,即URL中的path。Handlers将请求的资源和处理函数进行映射。当服务器收到一个请求,解析Request,获取客户端请求的资源路径,在Handlers表中找是否存在映射关系,有则调用对应的Handler回调函数处理请求,无则返回404(请求资源不存在)。
上面的Get,Post,Put,Patch,Delete,Options都是往Handlers表中设置映射关系

请求方法,请求资源和处理函数都是强相关的,如上表:
- 只有http请求的请求方法是GET,且请求的资源是/Hello/a,服务器才会调用echoHelloA处理请求
- 只要请求方法和请求资源在Handlers表中找不到映射的处理函数,http服务器就会返回404的http响应,表示请求资源不存在
- Get("/Hello/a", echoHelloA):将请求方法为GET,请求资源为/Hello/a,与函数echoHelloA注册到Handlers表中
线程池的工作:
- 当服务器接收到一个http请求,会将该http请求放入线程池,线程池的线程会调用相应的函数解析http请求,形成一个Request对象
- 在Handlers映射表中查找有无对应请求方法和请求资源的处理函数,有则调用处理函数,并构建http响应
- 处理函数调用完后,将Responce构建成http响应,并将构建好的http响应返回给客户端
1.2.4.Client类
cpp
class Client {
//构造一个客户端对象,传入服务器Ip地址和端口
Client(const std::string &host, int port);
//向服务器发送GET请求
Result Get(const char *path, const Headers &headers);
//向服务器发送Post请求
//path是路径,body是request请求路径
//content_length是正文大小
//content_type是正文的类型
Result Post(const char *path, const char *body, size_t content_length,
const char *content_type);
//以Post方法上传文件
Result Post(const char *path, const MultipartFormDataItems &items);
}
1.3.httplib库搭建简单服务器
1.3.1.示例1
搭建服务器:
- 定义server对象
- 在Handlers表中注册相应请求方法和请求资源的处理函数
- listen绑定IP地址和端口号,启动服务器,监听连接和请求
cpp
// 包含httplib库头文件,用于搭建HTTP服务器
#include <iostream>
#include "httplib.h"
// 使用httplib命名空间,简化代码
using namespace httplib;
// 处理GET /hi 请求的函数
// 参数req: 客户端请求对象,包含请求信息
// 参数rsp: 服务器响应对象,用于设置响应内容
void Hello(const Request &req, Response &rsp)
{
// 设置响应内容为 "Hello World!",内容类型为纯文本
rsp.set_content("Hello World!", "text/plain");
// 设置HTTP状态码为200(成功)
rsp.status = 200;
}
// 处理GET /numbers/数字 请求的函数,使用正则表达式捕获路径中的数字
// 参数req: 请求对象,req.matches 保存正则捕获结果
// 参数rsp: 响应对象
void Numbers(const Request &req, Response &rsp)
{
// req.matches[0] 保存完整匹配的路径,req.matches[1] 保存第一个捕获组(即数字部分)
auto num = req.matches[1];
// 将捕获到的数字作为响应内容返回
rsp.set_content(num, "text/plain");
rsp.status = 200;
}
// 处理POST /multipart 请求的函数,用于处理文件上传(multipart/form-data)
void Multipart(const Request &req, Response &rsp)
{
// 检查请求中是否包含名为 "file" 的文件字段
auto ret = req.has_file("file");
if(ret == false)
{
// 如果没有文件,输出提示信息,并返回400错误(客户端请求错误)
std::cout << "not file upload\n";
rsp.status = 400;
return;
}
// 获取名为 "file" 的文件字段内容,返回一个 MultipartFile 对象
const auto& file = req.get_file_value("file");
// 清空响应正文
rsp.body.clear();
// 在响应正文中添加文件名和文件内容,用换行分隔
rsp.body = file.filename; // 文件名称
rsp.body += "\n";
rsp.body += file.content; // 文件内容
// 设置响应头 Content-Type 为纯文本
rsp.set_header("Content-Type", "text/plain");
rsp.status = 200;
return;
}
int main()
{
// 创建一个Server对象,用于搭建HTTP服务器
httplib::Server sever;
// 注册路由:GET /hi 映射到 Hello 函数
sever.Get("/hi", Hello);
// 注册路由:GET /numbers/数字 使用正则表达式匹配,映射到 Numbers 函数
// R"(/numbers/(\d+))" 表示一个原始字符串,匹配 /numbers/ 后跟一个或多个数字
sever.Get(R"(/numbers/(\d+))", Numbers);
// 注册路由:POST /multipart 映射到 Multipart 函数,用于处理文件上传
sever.Post("/multipart", Multipart);
// 启动服务器,监听所有网络接口(0.0.0.0)的9090端口
sever.listen("0.0.0.0", 9090);
return 0;
}
以下是该程序的执行过程详解:
创建HTTP服务器:
- 在main函数内部,首先创建了一个httplib::Server对象。这个对象代表了一个HTTP服务器,它将负责监听和处理来自客户端的请求。
设置路由和处理函数:程序通过调用server.Get和server.Post方法为HTTP服务器设置了路由和处理函数。
- 对于/hello路由,当收到GET请求时,服务器将返回"Hello World!"作为响应。
- 对于/number/(\d+)路由,当收到GET请求并匹配到一个数字时,服务器将返回该数字作为响应。这里使用了正则表达式\d+来匹配一个或多个数字。
启动服务器:
- 程序通过调用server.listen("0.0.0.0", 8080)来启动服务器。这里,"0.0.0.0"表示服务器将监听所有可用的网络接口,8080是服务器监听的端口号。
- listen函数将阻塞当前线程,等待客户端的连接和请求。这意味着在服务器运行期间,程序将停留在这一步,直到服务器被外部方式关闭(如通过发送终止信号或手动停止程序)。
处理客户端请求:
- 当有客户端向服务器发送请求时**(例如,通过浏览器访问http://<服务器IP>:8080/hello)**,服务器将根据请求的URL和方法(GET、POST等)找到相应的处理函数。
- 处理函数将被调用,并接收两个参数:一个表示请求信息的httplib::Request对象和一个用于构建响应的httplib::Response对象。
- 处理函数根据请求的内容生成响应,并通过修改httplib::Response对象来设置响应的内容、状态码和头部信息。
- 最后,服务器将响应发送回客户端。
编译
bash
g++ -o server main.cc -std=c++14 -lpthread
接下来就能开始我们的测试了
我们可以来试一试效果
这个程序是一个基于httplib库的简单HTTP服务器,它能够处理特定路由上的GET和POST请求。以下是它能处理的客户端请求类型:
GET请求:
- /hello:当客户端向服务器发送GET请求到/hello路由时,服务器将返回文本内容"Hello World!"。
- /number/(\d+):这是一个带有正则表达式的路由,用于匹配形如/number/123的URL,其中123可以是任意数字。当匹配成功时,服务器将返回该数字作为文本内容。
需要注意的是,这个程序只能处理上述特定路由上的请求。如果客户端发送请求到未定义的路由,服务器将不会返回任何响应(或者根据httplib的默认行为,可能会返回一个404错误)。





1.3.2.示例2
cpp
#include "../../third/include/httplib.h"
// 程序入口函数
int main()
{
// 1. 实例化服务器对象,创建一个HTTP服务器实例
httplib::Server server;
// 2. 注册回调函数,为GET请求的路径"/hi"设置处理逻辑
// 回调函数类型为 void(const httplib::Request &, httplib::Response &)
server.Get("/hi", [](const httplib::Request &req, httplib::Response &rsp){
// 打印请求的方法(例如 GET、POST等)
std::cout << req.method << std::endl;
// 打印请求的路径(例如 /hi)
std::cout << req.path << std::endl;
// 遍历并打印所有的HTTP请求头
for (auto it : req.headers) {
std::cout << it.first << ": " << it.second << std::endl;
}
// 构造一个简单的HTML响应体
std::string body = "<html><body><h1>Hello World</h1></body></html>";
// 设置响应的内容,同时指定内容类型为 text/html
rsp.set_content(body, "text/html");
// 显式设置HTTP状态码为200(成功),虽然set_content可能已默认设置,但此处明确指定
rsp.status = 200;
});
// 这行是注释,提示也可以注册POST请求的处理函数
// server.Post()
// 3. 启动服务器,监听所有网络接口(0.0.0.0)上的8070端口
// 服务器将在此端口上等待客户端连接
server.listen("0.0.0.0", 8070);
// 服务器启动后,listen会阻塞当前线程,直到服务器关闭,因此正常情况下不会执行到这里
return 0;
}

很完美
二.websocketpp
2.1.websocket协议
我们有了HTTP协议,为什么还需要websocket协议
HTTP协议和websocket协议的对比
HTTP 协议 ,就像寄信。
-
你(客户端) 想问问朋友(服务器)今晚看不看电影。你写了一封信(HTTP 请求),寄出去。
-
朋友(服务器) 收到信后,给你回一封信(HTTP 响应),说"看,7点见"。
-
在这个过程里,朋友不能主动给你说话。如果朋友突然想起来要改时间到8点,他没法主动告诉你,必须等你再写一封信去问。
-
如果你特别想知道有没有新消息,就只能不停地写信去问(这就是轮询):"改时间了吗?""改时间了吗?"。这样很累(浪费资源),而且信在路上有时间差,你得到的信息也未必是实时的。
而 WebSocket 则更像是打电话。
-
你先拨通朋友的电话,说"喂?"。
-
朋友接起来,说"喂,我在!"。
-
好了,电话打通了(连接建立)。接下来,你们俩谁都可以随时开口说话。
-
你可以说"今晚7点看电影?"
-
朋友可以马上回"好啊",过了几秒,朋友又主动说"哎,改8点吧,我加个班。"。
-
你听到后回复"没问题"。
你看,WebSocket 的核心就是:在同一个连接上,双方都可以随时、主动地向对方发送数据。这解决了 HTTP 协议"一来一回、服务器被动"的痛点。
2.2.安装websocketpp
websocketpp 是一个 C++ 的库,它的作用就是帮你用 C++ 语言快速实现 WebSocket 的功能。
我们刚才聊到 WebSocket 是一种"协议",就像是打电话的规则。而 websocketpp 就是一套已经写好的 C++ 工具,让你不用关心底层那些复杂的握手、数据帧解析、连接管理等细节,只需要调用它提供的简单接口,就能让你的 C++ 程序成为一个 WebSocket 服务器或者客户端。
用类比来理解
-
WebSocket 协议:就是"打电话"的规则(比如先拨号、接通后怎么说、怎么挂断)。
-
浏览器中的 WebSocket API :浏览器已经内置了打电话的工具,你只要写几行 JavaScript(
new WebSocket())就能用。 -
websocketpp:就是给 C++ 程序装上的"电话机"。你的 C++ 程序本来只能处理文件、计算数据,装上这个库之后,它就能和浏览器或者其他 WebSocket 客户端实时通话了。
websocketpp 的特点
-
为 C++ 而生:如果你用 C++ 写后端服务(比如游戏服务器、高性能推送服务),就可以用这个库来实现 WebSocket 通信。
-
跨平台:Windows、Linux、macOS 都能用。
-
仅头文件(Header-only):意思是大部分代码都写在头文件里,你只要在项目里包含它的头文件,就可以直接使用,不需要编译成单独的库(配置起来相对简单)。
-
支持 TLS/SSL :可以加密通信,就像 WebSocket 的安全版本
wss://。 -
灵活 :它提供了不同的传输方式(比如基于 ASIO 或者自定义的),你可以根据需要选择。ASIO 是一个很流行的网络编程库,websocketpp 可以配合它使用。
我们来安装一下
cpp
sudo apt-get install -y libboost-dev libboost-system-dev libwebsocketpp-dev
安装完毕后,若在 /usr/include 下有了 websocketpp目录就表示安装成功了。
cpp
ls /usr/include/websocketpp/

2.3.使用示例
2.3.1.示例1
server.cpp
cpp
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
// 定义一个 WebSocket 服务器类型,使用上述配置(无 TLS,基于 Asio)
typedef websocketpp::server<websocketpp::config::asio> server;
// 全局 server 指针,用于在回调函数中访问 server 对象
// 因为普通回调函数无法直接访问 main 中的局部变量,所以用全局指针传递
server* g_server = nullptr;
// 消息处理回调函数(普通函数)
// 当服务器收到客户端发来的消息时,该函数会被自动调用
// 参数 hdl 是连接句柄,用于标识具体的客户端连接
// 参数 msg 是指向消息对象的指针,包含消息内容、类型等信息
void on_message(websocketpp::connection_hdl hdl, server::message_ptr msg)
{
// 在控制台打印收到的消息内容(get_payload() 返回消息的文本内容)
std::cout << "服务端收到消息: " << msg->get_payload() << std::endl;
// 定义一个错误码变量,用于接收发送操作的结果状态
websocketpp::lib::error_code ec;
// 通过全局 server 指针调用 send 方法,将收到的消息原样发回给客户端
// 参数:连接句柄、消息内容、消息类型(文本/二进制)、错误码引用
g_server->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
// 检查发送是否出错
if (ec) {
// 如果出错,打印错误信息
std::cout << "发送消息失败: " << ec.message() << std::endl;
}
}
// 程序主函数
int main() {
// 创建一个 server 对象(名为 echo_server),代表 WebSocket 服务器实例
server echo_server;
// 将全局指针指向这个 server 对象,以便回调函数能使用它
g_server = &echo_server; // 设置全局指针
// 关闭日志输出(可选)
// set_access_channels 设置要输出的日志类型,alevel::none 表示不输出任何访问日志
echo_server.set_access_channels(websocketpp::log::alevel::none);
// clear_access_channels 清除之前设置的日志类型,alevel::all 表示所有类型,这里全部清除
echo_server.clear_access_channels(websocketpp::log::alevel::all);
// 初始化 Asio 网络库
// 定义一个错误码变量,用于接收初始化结果
websocketpp::lib::error_code ec;
// 调用 init_asio(ec) 进行初始化,错误信息会保存在 ec 中
echo_server.init_asio(ec);
// 检查初始化是否成功
if (ec) {
std::cout << "init_asio 失败: " << ec.message() << std::endl;
return 1; // 初始化失败,退出程序
}
// 设置消息处理函数,将上面定义的 on_message 函数注册为消息回调
// 当有客户端消息到达时,会自动调用 on_message
echo_server.set_message_handler(&on_message);
// 让服务器监听 8002 端口
// listen 第二个参数是错误码引用,用于返回监听操作的结果
echo_server.listen(8002, ec);
if (ec)
{
std::cout << "listen 失败: " << ec.message() << std::endl;
return 1; // 监听失败,退出程序
}
// 开始接受客户端连接请求
// start_accept 使服务器进入接受连接的状态,但不会阻塞,真正的等待在 run() 中进行
echo_server.start_accept(ec);
if (ec)
{
std::cout << "start_accept 失败: " << ec.message() << std::endl;
return 1; // 启动接受失败,退出程序
}
// 打印提示信息,表示服务器已启动
std::cout << "WebSocket 服务端启动,监听端口 8002" << std::endl;
// 启动事件循环(阻塞)
// run() 会一直运行,处理网络事件(连接、消息、关闭等),直到服务器被停止
echo_server.run();
// 程序正常退出(实际上 run() 通常不会返回,除非服务器主动停止)
return 0;
}
client.cpp
cpp
// 包含 websocketpp 库中基于 Asio 且不启用 TLS 的客户端配置文件
#include <websocketpp/config/asio_no_tls.hpp>
// 包含 websocketpp 的客户端核心头文件
#include <websocketpp/client.hpp>
// 包含标准输入输出流,用于打印信息
#include <iostream>
// 定义一个 WebSocket 客户端类型,使用上述配置(无 TLS,基于 Asio)
typedef websocketpp::client<websocketpp::config::asio> client;
// 全局 client 指针,用于在回调函数中访问 client 对象
// 因为普通回调函数无法直接访问 main 中的局部变量,所以用全局指针传递
client* g_client = nullptr;
// 连接打开回调函数
// 当客户端与服务器成功建立连接时,该函数会被自动调用
// 参数 hdl 是连接句柄,用于标识当前连接
void on_open(websocketpp::connection_hdl hdl)
{
// 在控制台打印提示信息
std::cout << "连接已打开,发送消息..." << std::endl;
// 定义一个错误码变量,用于接收发送操作的结果状态
websocketpp::lib::error_code ec;
// 通过全局 client 指针调用 send 方法,向服务器发送一条文本消息
// 参数:连接句柄、消息内容、消息类型(text 表示文本)、错误码引用
g_client->send(hdl, "Hello, server!", websocketpp::frame::opcode::text, ec);
// 检查发送是否出错
if (ec) {
// 如果出错,打印错误信息
std::cout << "发送失败: " << ec.message() << std::endl;
}
}
// 收到消息回调函数
// 当客户端收到服务器发来的消息时,该函数会被自动调用
// 参数 hdl 是连接句柄,参数 msg 是指向消息对象的指针,包含消息内容、类型等信息
void on_message(websocketpp::connection_hdl hdl, client::message_ptr msg)
{
// 在控制台打印收到的回复消息内容
std::cout << "客户端收到回复: " << msg->get_payload() << std::endl;
// 定义一个错误码变量,用于接收关闭操作的结果状态
websocketpp::lib::error_code ec;
// 通过全局 client 指针调用 close 方法,主动关闭与服务器的连接
// 参数:连接句柄、关闭状态码(normal 表示正常关闭)、关闭理由、错误码引用
g_client->close(hdl, websocketpp::close::status::normal, "Goodbye", ec);
// 检查关闭是否出错
if (ec)
{
std::cout << "关闭失败: " << ec.message() << std::endl;
}
}
// 连接关闭回调函数
// 当连接被关闭(无论主动还是被动)时,该函数会被自动调用
// 参数 hdl 是已关闭的连接句柄
void on_close(websocketpp::connection_hdl hdl)
{
// 在控制台打印连接已关闭的提示
std::cout << "连接已关闭" << std::endl;
// 调用客户端的 stop 方法,通知事件循环可以停止运行
// 这样 run() 函数就会返回,程序可以正常退出
g_client->stop(); // 停止事件循环
}
// 程序主函数
int main() {
// 创建一个 client 对象(名为 ws_client),代表 WebSocket 客户端实例
client ws_client;
// 将全局指针指向这个 client 对象,以便回调函数能使用它
g_client = &ws_client;
// 关闭日志输出(可选)
// set_access_channels 设置要输出的日志类型,alevel::none 表示不输出任何访问日志
ws_client.set_access_channels(websocketpp::log::alevel::none);
// clear_access_channels 清除之前设置的日志类型,alevel::all 表示所有类型,这里全部清除
ws_client.clear_access_channels(websocketpp::log::alevel::all);
// 初始化 Asio 网络库
// 定义一个错误码变量,用于接收初始化结果
websocketpp::lib::error_code ec;
// 调用 init_asio(ec) 进行初始化,错误信息会保存在 ec 中
ws_client.init_asio(ec);
// 检查初始化是否成功
if (ec) {
std::cout << "init_asio 失败: " << ec.message() << std::endl;
return 1; // 初始化失败,退出程序
}
// 设置回调函数,将上面定义的普通函数注册为相应事件的处理器
ws_client.set_open_handler(&on_open); // 连接打开时的回调
ws_client.set_message_handler(&on_message); // 收到消息时的回调
ws_client.set_close_handler(&on_close); // 连接关闭时的回调
// 创建一个连接到指定服务器的连接对象
// get_connection 的第一个参数是服务器 URI(ws:// 表示 WebSocket 协议,localhost:8002 是地址和端口)
// 第二个参数 ec 用于接收创建过程中的错误信息
client::connection_ptr con = ws_client.get_connection("ws://localhost:8002", ec);
// 检查连接对象创建是否成功
if (ec) {
std::cout << "连接创建失败: " << ec.message() << std::endl;
return 1; // 创建失败,退出程序
}
// 发起连接,将之前创建的连接对象加入客户端的连接管理
ws_client.connect(con);
// 启动客户端的事件循环(阻塞)
// run() 会一直运行,处理网络事件(连接、消息、关闭等),直到 stop() 被调用或所有连接关闭
ws_client.run();
// 当 run() 返回时(例如连接关闭后调用了 stop()),打印退出信息
std::cout << "客户端退出" << std::endl;
return 0;
}
makefile
cpp
all : server client
server : server.cpp
g++ -std=c++17 $^ -o $@ -lpthread -lboost_system
client : client.cpp
g++ -std=c++17 $^ -o $@ -lpthread -lboost_system
.PHONY : clean
clean :
rm -rf server client



2.3.2.示例2
使用Websocketpp实现一个简单的http和websocket服务器
server.cpp
cpp
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
// 使用标准命名空间,避免每次输入 std::
using namespace std;
// 定义 WebSocket 服务器类型,基于 Asio 且无 TLS,并起别名 websocketsvr
typedef websocketpp::server<websocketpp::config::asio> websocketsvr;
// 定义消息指针类型别名,便于使用
typedef websocketsvr::message_ptr message_ptr;
// 引入 websocketpp 库中的占位符,用于绑定回调函数
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
// 引入绑定函数,用于将成员函数或普通函数与对象绑定
using websocketpp::lib::bind;
// WebSocket 连接成功时的回调函数
// 参数 server: 指向服务器对象的指针,hdl: 连接句柄,用于标识当前连接
void OnOpen(websocketsvr *server, websocketpp::connection_hdl hdl) {
// 在控制台输出提示信息
cout << "连接成功" << endl;
}
// WebSocket 连接关闭时的回调函数
void OnClose(websocketsvr *server, websocketpp::connection_hdl hdl) {
cout << "连接关闭" << endl;
}
// WebSocket 收到消息时的回调函数
// 参数 server: 服务器指针,hdl: 连接句柄,msg: 收到的消息对象指针
void OnMessage(websocketsvr *server, websocketpp::connection_hdl hdl, message_ptr msg) {
// 输出收到的消息内容(get_payload() 返回消息的文本内容)
cout << "收到消息" << msg->get_payload() << endl;
// 将收到的消息原样发回给客户端(回声)
// 使用 send 方法,指定连接句柄、消息内容和消息类型(文本)
server->send(hdl, msg->get_payload(), websocketpp::frame::opcode::text);
}
// WebSocket 连接发生异常时的回调函数
void OnFail(websocketsvr *server, websocketpp::connection_hdl hdl) {
cout << "连接异常" << endl;
}
// 处理 HTTP 请求的回调函数,返回一个简单的 HTML 欢迎页面
void OnHttp(websocketsvr *server, websocketpp::connection_hdl hdl) {
cout << "处理 http 请求" << endl;
// 通过连接句柄获取连接对象的指针,以便操作 HTTP 响应
websocketsvr::connection_ptr con = server->get_con_from_hdl(hdl);
// 使用 stringstream 构建 HTML 页面内容
std::stringstream ss;
ss << "<!doctype html><html><head>"
<< "<title>hello websocket</title><body>"
<< "<h1>hello websocketpp</h1>"
<< "</body></head></html>";
// 设置 HTTP 响应的主体内容为上面构建的 HTML 字符串
con->set_body(ss.str());
// 设置 HTTP 响应状态码为 200 OK
con->set_status(websocketpp::http::status_code::ok);
}
int main() {
// 创建一个 WebSocket 服务器对象
websocketsvr server;
// 设置 websocketpp 的日志输出级别
// alevel::none 表示关闭所有访问日志,避免控制台被大量信息刷屏
server.set_access_channels(websocketpp::log::alevel::none);
// 初始化 Asio 网络库,为后续的网络操作做准备
server.init_asio();
// 注册 HTTP 请求处理函数,将 OnHttp 绑定到服务器对象上
// 使用 bind 将 server 指针作为第一个参数传递给 OnHttp,_1 是占位符,表示将来传入的 hdl
server.set_http_handler(bind(&OnHttp, &server, ::_1));
// 注册 WebSocket 相关事件的处理函数
// 连接打开事件
server.set_open_handler(bind(&OnOpen, &server, ::_1));
// 连接关闭事件
server.set_close_handler(bind(&OnClose, &server, _1));
// 收到消息事件(注意:_1 是 hdl,_2 是 message_ptr)
server.set_message_handler(bind(&OnMessage, &server, _1, _2));
// (可选)连接异常事件,但代码中未注册 OnFail,如果需要可以取消注释下面一行
// server.set_fail_handler(bind(&OnFail, &server, _1));
// 让服务器监听 8888 端口
server.listen(8888);
// 开始接受客户端连接请求(进入接受状态,但真正的处理在 run 中)
server.start_accept();
// 启动服务器事件循环,该函数阻塞,直到服务器停止
server.run();
// 服务器停止后返回 0
return 0;
}
这段代码确实实现了一个简单的 HTTP 和 WebSocket 服务器。
功能说明
HTTP 服务器功能
- 通过 set_http_handler 注册了 OnHttp 函数,处理所有进入的 HTTP 请求。
- 在 OnHttp 中,构建了一个简单的 HTML 页面,设置响应体并返回状态码 200 OK。
- 因此,当你在浏览器中访问 http://localhost:8888 时,会看到一个标题为 "hello websocket" 的欢迎页面。
WebSocket 服务器功能
- 注册了 OnOpen、OnClose、OnMessage 等回调,处理 WebSocket 连接的生命周期事件。
- OnMessage 实现了 echo 服务:收到任何文本消息后,原样发送回客户端。
- 同时支持 WebSocket 的握手(通过 HTTP 升级),因此 JavaScript 可以通过 new WebSocket("ws://localhost:8888") 连接并进行双向通信。

我们打开浏览器
用浏览器访问 http://你的主机IP:8888 可看到 HTML 页面。


然后我们需要去测试WebSocket部分
我们回到我们的Windows主机来,创建一个test.html,填充下面这段代码
cpp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Websocket</title>
</head>
<body>
<input type="text" id="message">
<button id="submit">提交</button>
<script>
// 创建 websocket 实例
// ws://你的主机IP:8888
// 类比http
// ws 表示websocket 协议
// 192.168.51.100 表示服务器地址
// 8888 表示服务器绑定的端口
let websocket = new WebSocket("ws://你的主机IP:8888");
// 处理连接打开的回调函数
websocket.onopen = function() {
console.log("连接建立");
}
// 处理收到消息的回调函数
// 控制台打印消息
websocket.onmessage = function(e) {
console.log("收到消息: " + e.data);
}
// 处理连接异常的回调函数
websocket.onerror = function() {
console.log("连接异常");
}
// 处理连接关闭的回调函数
websocket.onclose = function() {
console.log("连接关闭");
}
// 实现点击按钮后, 通过 websocket实例 向服务器发送请求
let input = document.querySelector('#message');
let button = document.querySelector('#submit');
button.onclick = function() {
console.log("发送消息: " + input.value);
websocket.send(input.value);
}
</script>
</body>
</html>

我们双击打开,进入浏览器

我们点击F12,打开浏览器的控制台

看看我们的服务器(注意从连接成功开始看)

输入你好

点击提交


怎么样?还是很不错的吧
2.3.3.示例3
test.cpp
cpp
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
// 0. 定义 server 类型:基于 Asio 且无 TLS 的 WebSocket 服务器类型,并起别名 server_t
typedef websocketpp::server<websocketpp::config::asio> server_t;
// 连接打开回调函数:当 WebSocket 长连接成功建立时调用
void onOpen(websocketpp::connection_hdl hdl) {
// 在控制台输出提示信息
std::cout << "websocket长连接建立成功!\n";
}
// 连接关闭回调函数:当 WebSocket 长连接断开时调用
void onClose(websocketpp::connection_hdl hdl) {
std::cout << "websocket长连接断开!\n";
}
// 收到消息回调函数:当收到客户端发送的消息时调用
// 参数 server: 指向服务器对象的指针,用于在回调中调用服务器方法
// 参数 hdl: 连接句柄,用于标识当前连接
// 参数 msg: 消息对象指针,包含消息内容等信息
void onMessage(server_t *server, websocketpp::connection_hdl hdl, server_t::message_ptr msg) {
// 1. 获取有效消息载荷数据,进行业务处理
// get_payload() 返回消息的文本内容
std::string body = msg->get_payload();
std::cout << "收到消息:" << body << std::endl;
// 2. 对客户端进行响应
// 获取通信连接对象(通过连接句柄)
auto conn = server->get_con_from_hdl(hdl);
// 发送数据给客户端:在原消息后加上 "-Hello!",消息类型为文本
conn->send(body + "-Hello!", websocketpp::frame::opcode::value::text);
}
int main()
{
// 1. 实例化服务器对象
server_t server;
// 2. 初始化日志输出 --- 关闭日志输出,避免控制台被大量信息刷屏
server.set_access_channels(websocketpp::log::alevel::none);
// 3. 初始化 Asio 框架,为后续网络操作做准备
server.init_asio();
// 4. 设置消息处理/连接握手成功/连接关闭回调函数
// 注册连接打开回调
server.set_open_handler(onOpen);
// 注册连接关闭回调
server.set_close_handler(onClose);
// 使用 std::bind 将服务器对象指针绑定到 onMessage 的第一个参数,
// std::placeholders::_1 和 _2 分别对应后续传入的 hdl 和 msg
auto msg_handler = std::bind(onMessage, &server, std::placeholders::_1, std::placeholders::_2);
// 注册消息接收回调
server.set_message_handler(msg_handler);
// 5. 启用地址重用,允许在端口被占用时重新绑定(例如快速重启服务器)
server.set_reuse_addr(true);
// 5. 设置监听端口为 8080(此处步骤编号重复,但功能正确)
server.listen(8080);
// 6. 开始接受客户端连接请求(进入接受状态,但真正的处理在 run 中进行)
server.start_accept();
// 7. 启动服务器事件循环,该函数阻塞运行,直到服务器被停止
server.run();
return 0;
}
makefile
cpp
test : test.cpp
g++ -std=c++17 $^ -o $@ -lpthread -lboost_system
.PHONY : clean
clean :
rm -rf test
我们编译运行一下

我们还是在我们的Windows主机创建一个test.html,然后存放下面这个内容
cpp
<!DOCTYPE html>
<html>
<body>
<input id="msg" placeholder="输入消息">
<button onclick="send()">发送</button>
<div id="log"></div>
<script>
const ws = new WebSocket('ws://你的主机IP:8080'); // 请根据实际服务器地址修改
const log = document.getElementById('log');
ws.onopen = () => log.innerHTML += '<div>✅ 连接成功</div>';
ws.onmessage = (e) => log.innerHTML += '<div>📩 收到: ' + e.data + '</div>';
ws.onclose = () => log.innerHTML += '<div>❌ 连接关闭</div>';
function send() {
const msg = document.getElementById('msg').value;
if (msg) {
ws.send(msg);
log.innerHTML += '<div>📤 发送: ' + msg + '</div>';
document.getElementById('msg').value = '';
}
}
</script>
</body>
</html>

双击打开

输入你好

我们点击发送

我们输入我很好


非常完美的一个程序。
我们的服务端也会出现下面这个
