将代码保存为myWifiMouse.cpp, 在MSYS终端编译运行这个服务端,
$ cd /source_dir_path/
$ g++ -std=c++14 -o myWifiMouse myWifiMouse.cpp -lws2_32 && ./myWifiMouse.exe
/*
--------------------通信协议--------------------
鼠标数据帧格式:
mos+' '+len+func+' '+data
mos:为固定标识,注意:mos后是两个空格
len:func加data的字节长度,len与func之间是没有空格的
func:有3个分别为R(按键),m(移动),w(滚轮),c(左键点击)
data:有3种,
按键:l左键、r右键、m中键,按下为d,释放为u,
按键数据举例为:"l d"、"l u"、"m d"、"m u";
移动:分别为xy坐标,
移动数据举例为:"1 5"、"0 -8"、"-1 0"、"-5 -5";
滚轮:只不滚动值,要么正,要么负;
下面是鼠标数据帧举例:
左键点击一次:"mos 1c"
左键按下:"mos 5R l d"
左键释放:"mos 5R l u"
右键按下:"mos 5R r d"
右键释放:"mos 5R r u"
中键按下:"mos 5R m d"
中键释放:"mos 5R m u"
移动:"mos 5m 0 3"
滚轮:"mos 3w 0"
滚轮:"mos 3w 1"
键盘数据帧格式:
key+' '+len+data
key:为固定标识,注意:key后是2个空格
len:data的字节长度,len与data之间是没有空格的
data:为键盘操作输入数据
下面是键盘数据帧举例:
回车键:"key 3RTN"
退格键:"key 3BAS"
输入法数据帧格式:
utf8+' '+data
utf8:为固定标识,注意:utf8后是1个空格
data:为字符文字数据
下面是键盘数据帧举例:
"utf8 abdsd":接收到字符串"abdsd"
"utf8 好的,好东西":接收到字符串"好的,好东西"
--------------------通信协议--------------------
*/
#include <iostream>
#include <thread>
#include <mutex>
#include <unordered_map>
#include <string>
#include <winsock2.h>
#include <windows.h>
#include <ctime>
#include <list>
#pragma comment(lib, "ws2_32.lib")
// 配置日志(包含帧计数信息)
// 定义日志宏,用于输出不同级别的日志信息
// LOG_INFO 用于输出信息级别的日志
#define LOG_INFO(msg) std::cout << getCurrentTime()<<" "<< __LINE__ << " - INFO - " << msg << std::endl
// LOG_ERROR 用于输出错误级别的日志
#define LOG_ERROR(msg) std::cerr << getCurrentTime()<<" "<< __LINE__ << " - ERROR - " << msg << std::endl
// LOG_WARNING 用于输出警告级别的日志
#define LOG_WARNING(msg) std::cout << getCurrentTime()<<" "<< __LINE__ << " - WARNING - " << msg << std::endl
// LOG_DEBUG 用于输出调试级别的日志
#define LOG_DEBUG(msg) std::cout << getCurrentTime()<<" "<< __LINE__ << " - DEBUG - " << msg << std::endl
// 获取当前时间的函数,返回一个字符串表示当前时间
std::string getCurrentTime() {
time_t now = time(0);
char* dt = ctime(&now);
std::string timeStr(dt);
timeStr.pop_back(); // 去掉换行符
return timeStr;
}
// WiFiMouseServer 类,用于实现一个WiFi鼠标服务器
class WiFiMouseServer {
public:
// 构造函数,初始化服务器的主机地址和端口号
WiFiMouseServer(const std::string& host = "0.0.0.0", int port = 1978)
: host(host), port(port), running(false), frame_count(0) {
// 初始化Winsock库
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
LOG_ERROR("WSAStartup failed");
return;
}
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == INVALID_SOCKET) {
LOG_ERROR("Failed to create socket");
WSACleanup();
return;
}
// 配置服务器地址信息
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(host.c_str());
server_addr.sin_port = htons(port);
// 将套接字绑定到指定的地址和端口
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
LOG_ERROR("Failed to bind socket");
closesocket(server_socket);
WSACleanup();
return;
}
// 开始监听客户端连接
if (listen(server_socket, 5) == SOCKET_ERROR) {
LOG_ERROR("Failed to listen on socket");
closesocket(server_socket);
WSACleanup();
return;
}
}
// 析构函数,停止服务器并清理Winsock库
~WiFiMouseServer() {
stop();
WSACleanup();
}
// 启动服务器,开启一个新线程来接受客户端连接
void start() {
running = true;
LOG_INFO("Server started, listening on " + host + ":" + std::to_string(port) + " | Waiting for client connections...");
std::thread(&WiFiMouseServer::_accept_connections, this).detach();
}
// 停止服务器,关闭套接字并输出处理的总帧数
void stop() {
running = false;
if (server_socket != INVALID_SOCKET) {
closesocket(server_socket);
server_socket = INVALID_SOCKET;
}
LOG_INFO("Server stopped | Total frames processed: " + std::to_string(frame_count));
}
// 从列表中获取指定索引的元素,如果索引越界则抛出异常
static std::string getElement(const std::list<std::string>& myList, int index)
{
if (index < 0 || index >= myList.size()) {
throw std::out_of_range("Index out of range");
}
auto it = myList.begin();
std::advance(it, index); // 移动迭代器到目标位置
return *it; // 解引用获取元素
}
// 将字符串按指定分隔符分割成多个子字符串,并存储在列表中返回
static std::list<std::string> split(const std::string& _buffer, const std::string& _split )
{
int pos = 0;
int len;
std::list<std::string> temp;
for (size_t i = 0; i < _buffer.length(); )
{
pos = _buffer.find( _split, i );
if( pos < 0 )
{
temp.push_back( _buffer.substr( i ) );
break;
}
len = pos - i;
temp.push_back( _buffer.substr( i, len ) );
i += len+1;
}
return temp;
}
private:
std::string host; // 服务器主机地址
int port; // 服务器端口号
SOCKET server_socket; // 服务器套接字
bool running; // 服务器运行状态
std::unordered_map<SOCKET, std::string> client_buffers; // 客户端缓冲区,存储每个客户端接收到的数据
int frame_count; // 处理的总帧数
// 接受客户端连接的线程函数,不断循环接受新的客户端连接
void _accept_connections() {
while (running) {
sockaddr_in client_addr;
int client_addr_len = sizeof(client_addr);
SOCKET client_socket = accept(server_socket, (sockaddr*)&client_addr, &client_addr_len);
if (client_socket == INVALID_SOCKET) {
if (running) {
LOG_ERROR("Failed to accept connection");
}
continue;
}
client_buffers[client_socket] = "";
LOG_INFO("New client connected: " + std::string(inet_ntoa(client_addr.sin_addr)) + ":" + std::to_string(ntohs(client_addr.sin_port)) + " | Current connections: " + std::to_string(client_buffers.size()));
std::thread(&WiFiMouseServer::_handle_client, this, client_socket).detach();
}
}
// 处理客户端连接的线程函数,不断接收客户端发送的数据并处理
void _handle_client(SOCKET client_socket) {
// try {
while (true) {
char buffer[1024];
int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) {
LOG_INFO("Client disconnected | Total frames processed: " + std::to_string(frame_count));
break;
}
client_buffers[client_socket] += std::string(buffer, bytes_received);
LOG_DEBUG("Received data, current buffer length: " + std::to_string(client_buffers[client_socket].length()) + " characters");
_split_and_process_frames(client_socket);
}
// } catch (const std::exception& e) {
// LOG_ERROR("Error handling client: " + std::string(e.what()));
// }
if (client_buffers.find(client_socket) != client_buffers.end()) {
client_buffers.erase(client_socket);
}
closesocket(client_socket);
}
// 解析帧头信息,返回一个包含长度、功能和帧头长度的元组指针
std::unique_ptr<std::tuple<std::string, std::string, int>> _parse_frame_header(const std::string& buffer)
{
std::list<std::string> ds = split( buffer, " " );
if ( ds.size() < 2 )
{
return nullptr;
}
std::string mark;
std::string len_func_part ;
std::string func;
std::string len_str;
int i=0;
if ( ds.size()>i ) mark = getElement(ds,i);
i++;
if ( ds.size()>i ) ;//rev = getElement(ds,i);
i++;
if ( ds.size()>i ) len_func_part = getElement(ds,i);
i++;
if (len_func_part.find("1c") != std::string::npos) {
func = "c";
len_str = "1";
} else {
func = len_func_part.substr(len_func_part.length() - 1);
len_str = len_func_part.substr(0, len_func_part.length() - 1);
}
if (func != "R" && func != "m" && func != "w" && func != "c") {
LOG_WARNING("Invalid func field: " + func);
return nullptr;
}
int frame_header_len = mark.size() + len_str.size() + 2;
return std::make_unique<std::tuple<std::string, std::string, int>>(len_str, func, frame_header_len);
}
// 处理鼠标帧数据,解析并处理鼠标相关的操作
void _process_mouse_frame(SOCKET client_socket, std::string& buffer)
{
size_t mos_pos = buffer.find("mos ");
if (mos_pos == std::string::npos) {
return;
}
size_t _pos = mos_pos;
buffer = buffer.substr(_pos);
auto frame_parts = _parse_frame_header(buffer);
if (!frame_parts) {
return;
}
std::string len_str = std::get<0>(*frame_parts);
std::string func = std::get<1>(*frame_parts);
int frame_header_len = std::get<2>(*frame_parts);
int expected_func_data_len;
try {
expected_func_data_len = std::stoi(len_str);
} catch (const std::invalid_argument& e) {
LOG_WARNING("Invalid len field: " + len_str + " | Discarding current incomplete frame");
buffer = buffer.substr(1);
return;
}
int min_frame_len = frame_header_len + expected_func_data_len ;
if (buffer.length() < min_frame_len) {
buffer = buffer.substr(1);
return;
}
std::string func_data_str = buffer.substr(frame_header_len);
int actual_func_data_len = func_data_str.length();
if (actual_func_data_len < expected_func_data_len) {
return;
}
func_data_str = func_data_str.substr(0, expected_func_data_len);
int full_frame_len = frame_header_len + func_data_str.length();
if (full_frame_len > buffer.length()) {
return;
}
std::string full_frame = buffer.substr(0, full_frame_len);
_process_single_frame(full_frame);
buffer = buffer.substr(full_frame_len);
frame_count++;
LOG_DEBUG("Successfully processed 1 frame | Total frames: " + std::to_string(frame_count) + " | Remaining buffer length: " + std::to_string(buffer.length()));
}
// 处理键盘帧数据,解析并处理键盘相关的操作
void _process_keyboard_frame(SOCKET client_socket, std::string& buffer)
{
size_t key_pos = buffer.find("key ");
if (key_pos == std::string::npos) {
return;
}
buffer = buffer.substr(key_pos);
size_t space_pos = buffer.find(" ");
if (space_pos == std::string::npos) {
return;
}
std::string len_str = buffer.substr(space_pos + 2);
size_t data_start = len_str.find_first_not_of("0123456789");
if (data_start == std::string::npos) {
return;
}
std::string len_part = len_str.substr(0, data_start);
std::string data = len_str.substr(data_start);
int expected_len = std::stoi(len_part);
int actual_len = data.length();
if (actual_len < expected_len) {
LOG_WARNING("Length verification failed for key frame: expected " + std::to_string(expected_len) + " bytes, actual " + std::to_string(actual_len) + " bytes");
buffer = buffer.substr(1);
return;
}
std::string full_frame = buffer.substr(0, space_pos + 2 + len_part.length() + expected_len );
_process_single_frame(full_frame);
buffer = buffer.substr(full_frame.length());
frame_count++;
LOG_DEBUG("Successfully processed 1 key frame | Total frames: " + std::to_string(frame_count) + " | Remaining buffer length: " + std::to_string(buffer.length()));
}
// 处理输入法帧数据,解析并处理输入法相关的操作
void _process_input_method_frame(SOCKET client_socket, std::string& buffer) {
size_t utf8_pos = buffer.find("utf8 ");
if (utf8_pos == std::string::npos) {
return;
}
buffer = buffer.substr(utf8_pos);
size_t space_pos = buffer.find(' ');
if (space_pos == std::string::npos) {
return;
}
std::string func = "utf8";
std::string data = buffer.substr(space_pos + 1);
std::string full_frame = buffer.substr(0, space_pos + 0 + data.length());
_process_single_frame(full_frame);
buffer = buffer.substr(full_frame.length());
frame_count++;
LOG_DEBUG("Successfully processed 1 frame | Total frames: " + std::to_string(frame_count) + " | Remaining buffer length: " + std::to_string(buffer.length()));
}
// 分割并处理接收到的数据帧,根据帧的类型调用相应的处理函数
void _split_and_process_frames(SOCKET client_socket) {
std::string& buffer = client_buffers[client_socket];
while (true)
{
size_t mos_pos = buffer.find("mos ");
size_t utf8_pos = buffer.find("utf8 ");
size_t key_pos = buffer.find("key ");
// LOG_DEBUG("---> buffer: " + (buffer) );
if (mos_pos == std::string::npos && utf8_pos == std::string::npos && key_pos == std::string::npos) {
return;
}
if (mos_pos != std::string::npos && (utf8_pos == std::string::npos || mos_pos < utf8_pos) && (key_pos == std::string::npos || mos_pos < key_pos))
{
_process_mouse_frame( client_socket, buffer) ;
}
else if (utf8_pos != std::string::npos && (key_pos == std::string::npos || utf8_pos < key_pos))
{
_process_input_method_frame( client_socket, buffer) ;
}
else
{
_process_keyboard_frame( client_socket, buffer);
}
}
}
// 处理单个数据帧,根据帧的功能调用相应的处理函数
void _process_single_frame(const std::string& frame) {
try {
// std::string func;
std::string data;
std::string mark;
std::string len_func_part ;
std::string func;
std::string len_str;
std::list<std::string> ds = split( frame, " ");
if ( ds.size() < 2 )
{
return ;
}
int i=0;
if ( ds.size()>i ) mark = getElement(ds,i);
i++;
if ( ds.size()>i ) ;//rev = getElement(ds,i);
i++;
if ( ds.size()>i ) len_func_part = getElement(ds,i);
i++;
if ( mark == "utf8") {
func = mark;
data = frame.substr(5);
}
else if ( mark == "key")
{
func = mark;
size_t space_pos = frame.find(' ');
std::string len_str = frame.substr(space_pos + 2);
size_t data_start = len_str.find_first_not_of("0123456789");
data = len_str.substr(data_start);
}
else
{
if (len_func_part.find("1c") != std::string::npos) {
func = "c";
len_str = "1";
} else {
func = len_func_part.substr(len_func_part.length() - 1);
len_str = len_func_part.substr(0, len_func_part.length() - 1);
}
int _d_pos = frame.find( len_func_part );
if( _d_pos + len_func_part.size() + 1 < frame.size() )
data = frame.substr( _d_pos + len_func_part.size() + 1 );
int expected_len = std::stoi(len_str);
int actual_len = (func + " " + data).length();
if ( !data.empty() && actual_len != expected_len)
{
return ;//throw std::invalid_argument("Length verification failed: expected " + std::to_string(expected_len) + " bytes, actual " + std::to_string(actual_len) + " bytes");
}
}
if (func == "R") {
handle_mouse_key(data);
} else if (func == "m") {
handle_mouse_move(data);
} else if (func == "w") {
handle_mouse_scroll(data);
} else if (func == "c") {
handle_mouse_click(data);
} else if (func == "utf8") {
handle_keyboard(data);
} else if (func == "key") {
handle_keyboard_key(data);
} else {
return ;//throw std::invalid_argument("Unsupported func: " + func);
}
} catch (const std::exception& e) {
LOG_WARNING("Failed to process frame: " + std::string(e.what()) + " | Frame content: " + frame);
}
}
#if 1
// 处理鼠标按键事件,模拟鼠标按键的按下和释放操作
void handle_mouse_key(const std::string& data) {
size_t pos = data.find(' ');
if (pos == std::string::npos) {
throw std::invalid_argument("Invalid key data format: " + data);
}
std::string key_type = data.substr(0, pos);
std::string action = data.substr(pos + 1);
if ((key_type != "l" && key_type != "r" && key_type != "m") || (action != "d" && action != "u")) {
throw std::invalid_argument("Invalid key parameters: " + data);
}
INPUT input = {0};
input.type = INPUT_MOUSE;
// 设置按键类型
if (key_type == "l") {
input.mi.dwFlags = (action == "d") ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
} else if (key_type == "r") {
input.mi.dwFlags = (action == "d") ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
} else { // m
input.mi.dwFlags = (action == "d") ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
}
// 发送输入事件
SendInput(1, &input, sizeof(INPUT));
}
// 处理鼠标移动事件,模拟鼠标的移动操作
void handle_mouse_move(const std::string& data) {
size_t pos = data.find(' ');
if (pos == std::string::npos) {
throw std::invalid_argument("Invalid move data format: " + data);
}
int x = std::stoi(data.substr(0, pos));
int y = std::stoi(data.substr(pos + 1));
INPUT input = {0};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
input.mi.dx = x;
input.mi.dy = y;
SendInput(1, &input, sizeof(INPUT));
}
// 处理鼠标滚动事件,模拟鼠标的滚动操作
void handle_mouse_scroll(const std::string& data) {
try {
int value = std::stoi(data);
if (value == 0) return; // 无滚动,直接返回
INPUT input = {0};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
input.mi.mouseData = (value > 0) ? WHEEL_DELTA : -WHEEL_DELTA;
SendInput(1, &input, sizeof(INPUT));
} catch (const std::invalid_argument& e) {
throw std::invalid_argument("Invalid scroll value: " + data);
}
}
// 处理鼠标点击事件,模拟鼠标左键的点击操作
void handle_mouse_click(const std::string& data) {
INPUT inputs[2] = {0};
// 左键按下
inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
// 左键释放
inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
// 发送两个输入事件
SendInput(2, inputs, sizeof(INPUT));
}
#else
// 另一种处理鼠标按键事件的实现,使用mouse_event函数
void handle_mouse_key(const std::string& data) {
size_t pos = data.find(' ');
if (pos == std::string::npos) {
throw std::invalid_argument("Invalid key data format: " + data);
}
std::string key_type = data.substr(0, pos);
std::string action = data.substr(pos + 1);
if ((key_type != "l" && key_type != "r" && key_type != "m") || (action != "d" && action != "u")) {
throw std::invalid_argument("Invalid key parameters: " + data);
}
DWORD button;
if (action == "d") {
if (key_type == "l") {
button = MOUSEEVENTF_LEFTDOWN;
} else if (key_type == "r") {
button = MOUSEEVENTF_RIGHTDOWN;
} else {
button = MOUSEEVENTF_MIDDLEDOWN;
}
mouse_event( button, 0, 0, 0, 0);
}
else
{
if (key_type == "l") {
button = MOUSEEVENTF_LEFTUP;
} else if (key_type == "r") {
button = MOUSEEVENTF_RIGHTUP;
} else {
button = MOUSEEVENTF_MIDDLEUP;
}
mouse_event( button, 0, 0, 0, 0);
}
}
// 另一种处理鼠标移动事件的实现,使用mouse_event函数
void handle_mouse_move(const std::string& data) {
size_t pos = data.find(' ');
if (pos == std::string::npos) {
throw std::invalid_argument("Invalid move data format: " + data);
}
int x = std::stoi(data.substr(0, pos));
int y = std::stoi(data.substr(pos + 1));
mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);
}
// 另一种处理鼠标滚动事件的实现,使用mouse_event函数
void handle_mouse_scroll(const std::string& data) {
try {
int value = std::stoi(data);
if (value > 0) {
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, WHEEL_DELTA, 0);
} else if (value < 0) {
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, -WHEEL_DELTA, 0);
}
} catch (const std::invalid_argument& e) {
throw std::invalid_argument("Invalid scroll value: " + data);
}
}
// 另一种处理鼠标点击事件的实现,使用mouse_event函数
void handle_mouse_click(const std::string& data) {
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
#endif
// 辅助函数:将UTF-8字符串转换为宽字符字符串
wchar_t* utf8_to_wchar(const char* utf8_str) {
if (!utf8_str) return nullptr;
// 计算宽字符所需长度
int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, nullptr, 0);
if (wlen == 0) {
LOG_WARNING("UTF-8 to wide char conversion failed (length 0)");
return nullptr;
}
// 分配内存并转换
wchar_t* wstr = new wchar_t[wlen];
if (!wstr) {
LOG_ERROR("Memory allocation failed for wide char");
return nullptr;
}
if (MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, wstr, wlen) == 0) {
LOG_WARNING("UTF-8 to wide char conversion failed (MultiByteToWideChar)");
delete[] wstr;
return nullptr;
}
return wstr;
}
// 辅助函数:模拟单个宽字符的输入
void simulate_unicode_input(wchar_t c) {
INPUT input[2] = {0}; // 存储按键按下和释放两个事件
// 按键按下事件
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0; // 不使用虚拟键码(通过Unicode扫描码输入)
input[0].ki.wScan = c; // 宽字符扫描码(直接对应Unicode字符)
input[0].ki.dwFlags = KEYEVENTF_UNICODE; // 标记为Unicode输入
// 按键释放事件
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0;
input[1].ki.wScan = c;
input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; // 释放标记
// 发送输入事件(返回值为成功发送的事件数,此处简化处理)
SendInput(2, input, sizeof(INPUT));
}
// 处理键盘输入,将UTF-8字符串转换为宽字符并逐个字符模拟输入
void handle_keyboard(const std::string& data) {
if (data.empty()) {
LOG_DEBUG("No keyboard data to input");
return;
}
// 1. 将UTF-8字符串转换为宽字符(支持中文等)
wchar_t* wstr = utf8_to_wchar(data.c_str());
if (!wstr) {
LOG_WARNING("Failed to process keyboard data (invalid UTF-8)");
return;
}
// 2. 逐个字符模拟输入(间隔短时间避免输入混乱)
for (int i = 0; wstr[i] != L'\0'; ++i) {
simulate_unicode_input(wstr[i]);
Sleep(10); // 轻微延迟,确保系统能正确接收
}
// 释放内存
delete[] wstr;
LOG_DEBUG("Keyboard input completed: " + data);
}
// 处理键盘按键事件,模拟回车键和退格键的按下和释放操作
void handle_keyboard_key(const std::string& data) {
if (data == "RTN") {
// 处理回车键
keybd_event(VK_RETURN, 0, 0, 0);
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
} else if (data == "BAS") {
// 处理退格键
keybd_event(VK_BACK, 0, 0, 0);
keybd_event(VK_BACK, 0, KEYEVENTF_KEYUP, 0);
} else {
LOG_WARNING("Unsupported keyboard key: " + data);
}
}
};
int main() {
WiFiMouseServer server;
server.start();
try {
std::cout << "Server is running, press Ctrl+C to stop..." << std::endl;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
} catch (const std::exception& e) {
server.stop();
}
return 0;
}