2024-12-12 22:43:24 +08:00
|
|
|
#include "server.h"
|
2024-12-13 23:03:12 +08:00
|
|
|
#include <of_str.h>
|
2024-12-17 08:57:43 +08:00
|
|
|
#include <of_util.h>
|
2024-12-13 23:03:12 +08:00
|
|
|
|
|
|
|
using namespace ofen;
|
|
|
|
|
|
|
|
constexpr int g_ParseThreadNum = 1;
|
2024-12-12 23:11:55 +08:00
|
|
|
CTcpServer::CTcpServer(asio::io_context& io_context, const std::shared_ptr<spdlog::logger>& logger)
|
|
|
|
: io_context_(io_context), logger_(logger), acceptor_(io_context)
|
|
|
|
{
|
2024-12-13 12:35:08 +08:00
|
|
|
th_run_ = true;
|
2024-12-13 23:03:12 +08:00
|
|
|
handle_pool_ = std::make_shared<CThreadPool>(g_ParseThreadNum);
|
2024-12-13 12:35:08 +08:00
|
|
|
handle_pool_->init();
|
2024-12-13 23:03:12 +08:00
|
|
|
for (int i = 0; i < g_ParseThreadNum; ++i) {
|
|
|
|
handle_pool_->submit([&]() { handle_frame(); });
|
|
|
|
}
|
2024-12-12 23:11:55 +08:00
|
|
|
}
|
|
|
|
CTcpServer::~CTcpServer()
|
2024-12-12 22:43:24 +08:00
|
|
|
{
|
2024-12-13 12:35:08 +08:00
|
|
|
th_run_ = false;
|
|
|
|
handle_pool_->close_wait_all();
|
2024-12-12 22:43:24 +08:00
|
|
|
}
|
|
|
|
|
2024-12-13 12:35:08 +08:00
|
|
|
bool CTcpServer::start(unsigned short port)
|
2024-12-12 22:43:24 +08:00
|
|
|
{
|
2024-12-12 23:11:55 +08:00
|
|
|
asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
|
|
|
|
try {
|
|
|
|
acceptor_.open(endpoint.protocol());
|
|
|
|
acceptor_.set_option(asio::socket_base::reuse_address(true));
|
|
|
|
acceptor_.bind(endpoint);
|
|
|
|
acceptor_.listen();
|
|
|
|
} catch (const asio::system_error& e) {
|
|
|
|
logger_->error("Failed to bind to {}: {}", endpoint.address().to_string(), e.what());
|
|
|
|
return false;
|
|
|
|
}
|
2024-12-14 16:20:25 +08:00
|
|
|
auto bound_endpoint = acceptor_.local_endpoint();
|
|
|
|
server_ip_ = bound_endpoint.address().to_string() + ":" + std::to_string(bound_endpoint.port());
|
2024-12-13 12:35:08 +08:00
|
|
|
accept_client();
|
2024-12-12 23:11:55 +08:00
|
|
|
logger_->info("Server started on port {}", port);
|
|
|
|
return true;
|
2024-12-12 22:43:24 +08:00
|
|
|
}
|
|
|
|
|
2024-12-13 12:35:08 +08:00
|
|
|
void CTcpServer::stop()
|
2024-12-12 22:43:24 +08:00
|
|
|
{
|
2024-12-12 23:11:55 +08:00
|
|
|
acceptor_.close();
|
2024-12-13 12:35:08 +08:00
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
2024-12-12 23:11:55 +08:00
|
|
|
for (auto& [key, thread] : client_threads_) {
|
|
|
|
if (thread.joinable()) {
|
|
|
|
thread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
client_threads_.clear();
|
|
|
|
}
|
|
|
|
|
2024-12-13 23:03:12 +08:00
|
|
|
std::vector<TaskList> CTcpServer::get_clients()
|
2024-12-13 12:35:08 +08:00
|
|
|
{
|
2024-12-13 23:03:12 +08:00
|
|
|
std::vector<TaskList> result;
|
2024-12-13 12:35:08 +08:00
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
for (const auto& item : client_map_) {
|
2024-12-13 23:03:12 +08:00
|
|
|
TaskList t;
|
|
|
|
t.id_ = item.first;
|
|
|
|
t.task_ = item.second->task_;
|
|
|
|
t.time_ = item.second->time_;
|
|
|
|
result.push_back(t);
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-12-14 19:49:44 +08:00
|
|
|
void CTcpServer::get_client_list(CFrameBuffer** buf)
|
2024-12-13 12:35:08 +08:00
|
|
|
{
|
2024-12-14 19:49:44 +08:00
|
|
|
CFrameBuffer* tbuf = *buf;
|
2024-12-13 12:35:08 +08:00
|
|
|
auto vec = get_clients();
|
|
|
|
std::string msg;
|
2024-12-13 23:03:12 +08:00
|
|
|
int index = 1;
|
2024-12-13 12:35:08 +08:00
|
|
|
for (const auto& item : vec) {
|
2024-12-13 23:03:12 +08:00
|
|
|
msg.append(fmt::format("[{}][{}][{}]", index, item.id_, item.time_));
|
|
|
|
auto files = COfStr::split(item.task_, "|");
|
|
|
|
for (const auto& file : files) {
|
|
|
|
msg.append("\n" + file);
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
2024-12-14 16:20:25 +08:00
|
|
|
msg.append("\n");
|
|
|
|
++index;
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
2024-12-14 19:49:44 +08:00
|
|
|
tbuf->data_ = new char[msg.size() + 1];
|
|
|
|
std::memset(tbuf->data_, 0x0, msg.size() + 1);
|
|
|
|
tbuf->len_ = std::snprintf(tbuf->data_, msg.size() + 1, "%s", msg.data());
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTcpServer::handle_frame()
|
|
|
|
{
|
|
|
|
CFrameBuffer* buf = nullptr;
|
|
|
|
while (th_run_) {
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(buf_mut_);
|
2024-12-14 19:49:44 +08:00
|
|
|
if (!cache_.empty()) {
|
2024-12-13 12:35:08 +08:00
|
|
|
buf = cache_.front();
|
2024-12-14 23:59:13 +08:00
|
|
|
cache_.pop_front();
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
|
|
|
}
|
2024-12-13 12:39:34 +08:00
|
|
|
if (!buf) {
|
2024-12-16 12:19:08 +08:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
2024-12-13 12:39:34 +08:00
|
|
|
continue;
|
|
|
|
}
|
2024-12-17 08:57:43 +08:00
|
|
|
|
2024-12-15 13:14:04 +08:00
|
|
|
switch (buf->type_) {
|
2024-12-14 11:57:33 +08:00
|
|
|
case TYPE_GET_LIST: {
|
2024-12-14 16:20:25 +08:00
|
|
|
logger_->info("[{}] GetList.", buf->fid_);
|
2024-12-14 19:49:44 +08:00
|
|
|
get_client_list(&buf);
|
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
if (client_map_.count(buf->fid_)) {
|
|
|
|
auto& cli = client_map_[buf->fid_];
|
|
|
|
if (!send_frame(cli->socket_, buf)) {
|
|
|
|
logger_->error("GetList send failed.");
|
|
|
|
}
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
2024-12-14 11:57:33 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TYPE_UP_LIST: {
|
2024-12-17 08:57:43 +08:00
|
|
|
std::string files_path = std::string(buf->data_, buf->len_);
|
|
|
|
#ifdef _WIN32
|
|
|
|
std::string turn_files_path = CCodec::u8ToGBK(files_path);
|
|
|
|
#else
|
|
|
|
std::string turn_files_path(files_path);
|
|
|
|
#endif
|
|
|
|
logger_->info("[{}] UpList. {}", buf->fid_, turn_files_path);
|
2024-12-13 23:03:12 +08:00
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
2024-12-14 11:57:33 +08:00
|
|
|
if (client_map_.count(buf->fid_)) {
|
|
|
|
auto& cli = client_map_[buf->fid_];
|
2024-12-17 08:57:43 +08:00
|
|
|
cli->task_ = files_path;
|
2024-12-13 23:03:12 +08:00
|
|
|
cli->time_ = OfUtil::now_time();
|
|
|
|
}
|
2024-12-14 11:57:33 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TYPE_CANCEL_LIST: {
|
2024-12-14 16:20:25 +08:00
|
|
|
logger_->info("[{}] Cancle Task.", buf->fid_);
|
2024-12-13 23:03:12 +08:00
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
2024-12-14 11:57:33 +08:00
|
|
|
if (client_map_.count(buf->fid_)) {
|
|
|
|
auto& cli = client_map_[buf->fid_];
|
2024-12-13 23:03:12 +08:00
|
|
|
cli->task_.clear();
|
|
|
|
}
|
2024-12-14 11:57:33 +08:00
|
|
|
break;
|
|
|
|
}
|
2024-12-14 16:20:25 +08:00
|
|
|
// 两边发送OPEN
|
|
|
|
case TYPE_OPEN_FILE: {
|
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
if (client_map_.count(buf->tid_)) {
|
|
|
|
auto& cli = client_map_[buf->tid_];
|
|
|
|
if (!send_frame(cli->socket_, buf)) {
|
|
|
|
logger_->error("[{}] turn tid_ ailed to {}", buf->fid_, buf->tid_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (client_map_.count(buf->fid_)) {
|
|
|
|
auto& cli = client_map_[buf->fid_];
|
2024-12-14 18:20:58 +08:00
|
|
|
buf->mark_ = 1;
|
|
|
|
buf->fid_ = buf->tid_;
|
2024-12-14 16:20:25 +08:00
|
|
|
if (!send_frame(cli->socket_, buf)) {
|
|
|
|
logger_->error("[{}] turn fid_ failed to {}", buf->fid_, buf->tid_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
};
|
2024-12-14 18:20:58 +08:00
|
|
|
case TYPE_TRANS_DONE:
|
2024-12-14 11:57:33 +08:00
|
|
|
case TYPE_READY_TRANS:
|
|
|
|
case TYPE_TRANS_FILE: {
|
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
if (client_map_.count(buf->tid_)) {
|
|
|
|
auto& cli = client_map_[buf->tid_];
|
|
|
|
if (!send_frame(cli->socket_, buf)) {
|
2024-12-14 16:20:25 +08:00
|
|
|
logger_->error("[{}] turn failed to {}", buf->fid_, buf->tid_);
|
2024-12-14 11:57:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2024-12-15 23:19:56 +08:00
|
|
|
logger_->warn("No Mathched type.");
|
2024-12-14 11:57:33 +08:00
|
|
|
break;
|
2024-12-13 12:35:08 +08:00
|
|
|
}
|
|
|
|
delete buf;
|
2024-12-13 16:59:31 +08:00
|
|
|
buf = nullptr;
|
2024-12-12 22:43:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-13 12:35:08 +08:00
|
|
|
void CTcpServer::accept_client()
|
2024-12-12 22:43:24 +08:00
|
|
|
{
|
2024-12-12 23:11:55 +08:00
|
|
|
auto socket = std::make_shared<asio::ip::tcp::socket>(io_context_);
|
|
|
|
acceptor_.async_accept(*socket, [this, socket](const asio::error_code& error) {
|
|
|
|
if (!error) {
|
|
|
|
auto endpoint = socket->remote_endpoint();
|
|
|
|
std::string client_key = endpoint.address().to_string() + ":" + std::to_string(endpoint.port());
|
|
|
|
logger_->info("New connection from {}", client_key);
|
|
|
|
|
|
|
|
{
|
2024-12-13 12:35:08 +08:00
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
auto cache = std::make_shared<ClientCache>();
|
|
|
|
cache->socket_ = socket;
|
|
|
|
client_map_[client_key] = cache;
|
2024-12-12 23:11:55 +08:00
|
|
|
}
|
|
|
|
|
2024-12-13 12:35:08 +08:00
|
|
|
client_threads_[client_key] = std::thread(&CTcpServer::th_client, this, socket, client_key);
|
2024-12-12 23:11:55 +08:00
|
|
|
}
|
2024-12-13 12:35:08 +08:00
|
|
|
accept_client();
|
2024-12-12 23:11:55 +08:00
|
|
|
});
|
2024-12-12 22:43:24 +08:00
|
|
|
}
|
2024-12-12 23:11:55 +08:00
|
|
|
|
2024-12-13 12:35:08 +08:00
|
|
|
void CTcpServer::th_client(std::shared_ptr<asio::ip::tcp::socket> socket, const std::string& client_key)
|
2024-12-12 23:11:55 +08:00
|
|
|
{
|
2024-12-13 12:35:08 +08:00
|
|
|
std::shared_ptr<int> deleter(new int(0), [&](int* p) {
|
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
delete p;
|
|
|
|
client_map_.erase(client_key);
|
|
|
|
if (client_threads_.find(client_key) != client_threads_.end()) {
|
|
|
|
client_threads_.at(client_key).detach();
|
|
|
|
client_threads_.erase(client_key);
|
|
|
|
}
|
|
|
|
logger_->warn("{} client {} exit.", __FUNCTION__, client_key);
|
|
|
|
});
|
|
|
|
|
2024-12-12 23:11:55 +08:00
|
|
|
try {
|
2024-12-13 12:35:08 +08:00
|
|
|
std::shared_ptr<ClientCache> cache = nullptr;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(cli_mut_);
|
|
|
|
if (!client_map_.count(client_key)) {
|
|
|
|
logger_->error("Not Find Client{} in cache.", client_key);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cache = client_map_[client_key];
|
|
|
|
}
|
|
|
|
|
2024-12-12 23:11:55 +08:00
|
|
|
while (true) {
|
|
|
|
asio::error_code error;
|
2024-12-13 12:35:08 +08:00
|
|
|
size_t length = socket->read_some(asio::buffer(cache->tmp_buf_), error);
|
2024-12-12 23:11:55 +08:00
|
|
|
if (error == asio::error::eof) {
|
|
|
|
logger_->info("Connection closed by client: {}", client_key);
|
|
|
|
break;
|
|
|
|
} else if (error) {
|
|
|
|
throw asio::system_error(error);
|
|
|
|
}
|
|
|
|
|
2024-12-13 12:35:08 +08:00
|
|
|
cache->buffer_.push(cache->tmp_buf_.data(), length);
|
|
|
|
auto* frame = CTransProtocal::parse(cache->buffer_);
|
2024-12-12 23:11:55 +08:00
|
|
|
if (frame) {
|
2024-12-14 11:57:33 +08:00
|
|
|
frame->fid_ = client_key;
|
2024-12-14 23:59:13 +08:00
|
|
|
std::lock_guard<std::mutex> lock(buf_mut_);
|
|
|
|
cache_.push_back(frame);
|
2024-12-12 23:11:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
logger_->error("Error with client {}: {}", client_key, e.what());
|
|
|
|
}
|
2024-12-14 11:57:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CTcpServer::send_frame(std::shared_ptr<asio::ip::tcp::socket> socket, CFrameBuffer* buf)
|
|
|
|
{
|
|
|
|
char* out_buf{};
|
|
|
|
int out_len{};
|
2024-12-14 16:20:25 +08:00
|
|
|
if (buf->fid_.empty()) {
|
|
|
|
buf->fid_ = server_ip_;
|
|
|
|
}
|
|
|
|
|
2024-12-14 11:57:33 +08:00
|
|
|
if (!CTransProtocal::pack(buf, &out_buf, out_len)) {
|
|
|
|
logger_->error("{} pack failed.", __FUNCTION__);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!socket->send(asio::buffer(out_buf, out_len))) {
|
|
|
|
logger_->error("{} send failed.", __FUNCTION__);
|
|
|
|
delete[] out_buf;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
delete[] out_buf;
|
|
|
|
return true;
|
|
|
|
}
|