-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserverworker.cpp
More file actions
137 lines (113 loc) · 5.12 KB
/
serverworker.cpp
File metadata and controls
137 lines (113 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include "serverworker.h"
#include "Server.h"
#include "Logger.h"
#include <spdlog/spdlog.h>
#include <spdlog/details/null_mutex.h>
// ─────────────────────────────────────────────────────────
// LogBridge — 将 spdlog 日志实时桥接到 Qt 信号
//
// spdlog 支持自定义 sink(输出目标)。这里创建的 QtSignalSink
// 将每条日志通过 Qt 信号发射出去。由于 ServerWorker 运行在独立
// 线程,Qt 会自动通过 QueuedConnection 将信号排队到 GUI 线程
// 处理,彻底避免跨线程 GUI 访问问题。
// ─────────────────────────────────────────────────────────
template<typename Mutex>
class QtSignalSink : public spdlog::sinks::base_sink<Mutex> {
public:
QtSignalSink(std::function<void(const QString& msg, int level)> callback)
: callback_(std::move(callback)) {}
protected:
void sink_it_(const spdlog::details::log_msg& msg) override {
std::time_t t = std::chrono::system_clock::to_time_t(msg.time);
std::tm tm_info;
localtime_s(&tm_info, &t);
char time_buf[64];
std::strftime(time_buf, sizeof(time_buf), "%H:%M:%S", &tm_info);
char msec_buf[8];
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
msg.time.time_since_epoch()) % 1000;
std::snprintf(msec_buf, sizeof(msec_buf), ".%03d", (int)ms.count());
// msg.payload 是 string_view_t,用 append(迭代器) 构造 std::string
std::string payload = time_buf;
payload += msec_buf;
payload += ' ';
payload.append(msg.payload.data(), msg.payload.size());
int level = 2;
switch (msg.level) {
case spdlog::level::trace: level = 0; break;
case spdlog::level::debug: level = 1; break;
case spdlog::level::info: level = 2; break;
case spdlog::level::warn: level = 3; break;
case spdlog::level::err: level = 4; break;
case spdlog::level::critical: level = 5; break;
default: level = 2; break;
}
callback_(QString::fromStdString(payload), level);
}
void flush_() override {}
private:
std::function<void(const QString& msg, int level)> callback_;
};
using QtSignalSinkMt = QtSignalSink<std::mutex>;
// ─────────────────────────────────────────────────────────
// ServerWorker 成员函数实现
// ─────────────────────────────────────────────────────────
ServerWorker::ServerWorker(uint16_t port, QObject* parent)
: QObject(parent)
, port_(port)
{}
ServerWorker::~ServerWorker() {
stop();
}
void ServerWorker::start() {
// 1. 创建 QThread(由 Qt 管理其事件循环)
thread_ = std::make_unique<QThread>(this);
// 2. Server 本身运行在当前 start() 被调用的线程(即 worker_thread_)
// 所以无需 moveToThread —— Server 使用原生 select() 阻塞循环,
// 它不依赖 Qt 事件循环;将它移入 Qt 线程反而会被 Qt 事件循环
// 的 select()(Windows 消息泵)干扰。
// Server 在工作线程中直接执行,信号通过 QueuedConnection 跨线程。
server_ = std::make_unique<Server>(port_);
server_->setOnClientJoin([this](const std::string& name) {
Q_EMIT userJoined(QString::fromStdString(name));
});
server_->setOnClientLeave([this](const std::string& name) {
Q_EMIT userLeft(QString::fromStdString(name));
});
server_->setOnOnlineCount([this](int count) {
Q_EMIT onlineCountChanged(count);
});
auto qt_sink = std::make_shared<QtSignalSinkMt>(
[this](const QString& msg, int level) {
Q_EMIT logReady(msg, level);
});
qt_sink->set_level(spdlog::level::trace);
spdlog::default_logger()->sinks().push_back(qt_sink);
spdlog::info("========================================");
spdlog::info(" 局域网聊天室服务端");
spdlog::info(" 端口: {}", port_);
spdlog::info("========================================");
if (!server_->Start()) {
spdlog::error("[Main] 服务端启动失败");
Q_EMIT serverStopped();
return;
}
Q_EMIT serverStarted(static_cast<int>(port_));
// 3. Run() 在当前线程(worker_thread_)中阻塞执行
server_->Run();
spdlog::info("[Main] 服务端正常退出");
ShutdownLogger();
server_.reset();
Q_EMIT serverStopped();
}
void ServerWorker::stop() {
if (!server_) return;
server_->Stop();
}
int ServerWorker::currentOnlineCount() const {
if (server_) return server_->currentOnlineCount();
return 0;
}
void ServerWorker::kickUser(const QString& username) {
if (server_) server_->kickUser(username.toStdString());
}