#include "ConfigParser.h" #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif using namespace std; /** 清理字符串前后空格 */ void trim(std::string &s) { auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c); }); auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c); }).base(); s = (wsback <= wsfront) ? std::string() : std::string(wsfront, wsback); } CPStatus configFromIni(const std::string &path, std::unordered_map &config) { CPStatus result = CPSUCCESS; //打开文件 ifstream file(path); if (!file.is_open()) { result = CPFILE; return result; } // 一次性读取整个文件(减少I/O操作) file.seekg(0, std::ios::end); size_t size = file.tellg(); file.seekg(0, std::ios::beg); string m_buffer; m_buffer.resize(size); file.read(&m_buffer[0], static_cast(size)); //处理全部数据 config.clear(); std::string line; line.reserve(256); // 预分配行缓存 for (auto it = m_buffer.cbegin(); it != m_buffer.cend();) { // 快速跳过空白行 while (it != m_buffer.cend() && (*it == '\r' || *it == '\n')) ++it; if (it == m_buffer.cend()) break; // 获取行内容 line.clear(); while (it != m_buffer.cend() && *it != '\r' && *it != '\n' && *it != '\0') { line += *it++; } //处理行内容 // 跳过空行和注释 if (line.empty() || line[0] == ';' || line[0] == '#') { ++it; continue; } // 分割键值对 size_t eq_pos = line.find('='); if (eq_pos != std::string::npos) { std::string key = line.substr(0, eq_pos); std::string value = line.substr(eq_pos + 1); trim(key); trim(value); if (!key.empty()) { config[key] = value; // 移动语义自动优化 } } } return result; } void UDPSocketBase::create_socket() { while (true) { sockfd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); #if defined(_WIN32) if (sockfd_ != INVALID_SOCKET_VALUE){ break; } #else if (sockfd_ >= 0) { break; } #endif } } void UDPSocketBase::create_socket(socket_t socket) { sockfd_ = socket; } void UDPSocketBase::set_nonblock(bool enable) { u_long ctl = enable ? 1 : 0; #if defined(_WIN32) ioctlsocket(sockfd_, FIONBIO, &ctl) == 0; #else ioctl(sockfd_, FIONBIO, &ctl) == 0; #endif is_nonblock_ = enable; } void UDPSocketBase::bind_socket(const sockaddr_in &addr) const { if (bind(sockfd_, (struct sockaddr *) &addr, sizeof(addr)) != 0) { throw std::runtime_error("Bind failed"); } } UDPSocketBase::~UDPSocketBase() { #if defined(_WIN32) int ret = closesocket(sockfd_); #else int ret = close(sockfd_); #endif sockfd_ = (~0); } UDPSender::UDPSender(socket_t socket, bool nonblock) { if (socket != INVALID_SOCKET_VALUE) { create_socket(socket); } else { create_socket(); } set_nonblock(nonblock); } void UDPSender::set_destination(const std::string &ip, uint16_t port) { dstIp = ip; dstPort = port; dest_addr_.sin_family = AF_INET; dest_addr_.sin_port = htons(port); #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x600 dest_addr_.sin_addr.S_un.S_addr = inet_addr(ip.c_str()); #else inet_pton(AF_INET, ip.c_str(), &dest_addr_.sin_addr); #endif } void UDPSender::set_source(const std::string &ip, uint16_t port) { sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(port); #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x600 addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str()); #else inet_pton(AF_INET, ip.c_str(), &addr.sin_addr); #endif bind_socket(addr); } size_t UDPSender::send(const std::vector &data) { auto sent = sendto(sockfd_, reinterpret_cast(data.data()), static_cast(data.size()), 0, (struct sockaddr *) &dest_addr_, sizeof(dest_addr_)); if (sent == SOCKET_ERROR) { // throw std::runtime_error("Send failed"); printf("Send failed\n"); } return static_cast(sent); } size_t UDPSender::send(const std::string &data) { auto sent = sendto(sockfd_, reinterpret_cast(data.data()), static_cast(data.size()), 0, (struct sockaddr *) &dest_addr_, sizeof(dest_addr_)); if (sent == SOCKET_ERROR) { // throw std::runtime_error("Send failed"); printf("Send failed\n"); } return static_cast(sent); } size_t UDPSender::send(uint8_t *data, size_t size) { int nleft, nwritten; const char *ptr; ptr = (char *) data; /* can't do pointer arithmetic on void* */ nleft = size; while (nleft > 0) { #if defined(_WIN32) if ((nwritten = sendto(sockfd_, ptr, nleft, 0, (const struct sockaddr*)&dest_addr_, sizeof(dest_addr_))) <= 0) #else if ((nwritten = sendto(sockfd_, ptr, nleft, MSG_NOSIGNAL, (const struct sockaddr *) &dest_addr_, sizeof(struct sockaddr))) <= 0) #endif { if (!(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) return -1; /* error */ else continue; } nleft -= nwritten; ptr += nwritten; } return (size); } UDPReceiver::UDPReceiver(uint16_t port, bool nonblock, size_t buffer_size) : buffer_(buffer_size), running_(false), srcPort(port) { create_socket(); sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; bind_socket(addr); set_nonblock(nonblock); this->isOnline.store(false); } void UDPReceiver::start_receiving(const UDPReceiver::ReceiveCallback &callback) { running_ = true; receiver_thread_ = std::thread([this, callback]() { while (running_.load(std::memory_order_relaxed)) { try { auto result = receive(); ssize_t size = result.first; auto &from = result.second; if (size > 0) { lastTime_ = std::chrono::steady_clock::now(); std::vector data(buffer_.begin(), buffer_.begin() + size); callback(data, from.first, from.second); } } catch (const std::runtime_error &e) { if (is_nonblock_) continue; // cerr << e.what() << endl; } } }); online_thread_ = std::thread([this]() { while (running_.load(std::memory_order_relaxed)) { this_thread::sleep_for(std::chrono::seconds(1)); if (!isOnline.load()) { continue; } auto time = std::chrono::steady_clock::now() - lastTime_; if (std::chrono::duration_cast(time).count() > 5) { isOnline.store(false); } } }); } void UDPReceiver::stop_receiving() { running_.store(false); if (receiver_thread_.joinable()) { receiver_thread_.join(); } if (online_thread_.joinable()) { online_thread_.join(); } } std::pair> UDPReceiver::receive() { sockaddr_in from_addr{}; socklen_t from_len = sizeof(from_addr); #ifdef _WIN32 DWORD timeout = UDPTimeout*1000; // 3秒(单位:毫秒) setsockopt(sockfd_, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); #else struct timeval tv; tv.tv_sec = UDPTimeout; // 秒 tv.tv_usec = 0; // 微秒 setsockopt(sockfd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); #endif auto size = recvfrom(sockfd_, reinterpret_cast(buffer_.data()), static_cast(buffer_.size()), 0, (struct sockaddr *) &from_addr, &from_len); if (size == SOCKET_ERROR) { throw std::runtime_error("Receive Timeout"); } return {size, {from_addr, from_len}}; } UDPReceiver::~UDPReceiver() { stop_receiving(); } LogL::LogL(LogType logType, bool isSync, LogLevel level, std::string logPath) : m_isSync(isSync), m_logType(logType), m_logPath(std::move(logPath)), m_file(nullptr), m_thread(nullptr), m_sender(nullptr), m_logLevel(level) { switch (m_logType) { case LTStd: { break; } case LTTxt: { //创建文件 myDateTime now; std::string dirName; if (m_logPath.empty()) { dirName = "log"; } else { dirName = m_logPath; } myMkdir(dirName.c_str()); std::string fileName = dirName + "/" + now.toDate() + ".txt"; m_file = new std::ofstream(fileName, std::ios::app); m_today = now.day; break; } case LTUdp: { m_sender.reset(new UDPSender); std::vector parts; std::stringstream ss(m_logPath); std::string part; // 按逗号分割字符串 while (std::getline(ss, part, ':')) { parts.push_back(part); } if (parts.size() == 3) { m_sender->set_destination(parts[0], stoi(parts[1])); m_sender->set_source(parts[0], stoi(parts[2])); } else if (parts.size() == 2) { m_sender->set_destination(parts[0], stoi(parts[1])); } else { throw std::runtime_error("UDP Path Error"); } break; } case LTTcp: { break; } } if (!m_isSync) { //运行日志线程 m_thread_run = true; m_thread = new std::thread(&LogL::logThread, this); } } LogL::~LogL() { //停止线程 while (m_thread) { if (m_log_queue.empty()) { cout << "已清空日志队列" << endl; break; } cout << "正在写入剩余日志:" << m_log_queue.size() << endl; this_thread::sleep_for(chrono::milliseconds(300)); } if (m_thread) { //设置结束并等待线程结束 m_thread_run = false; m_cond_empty.notify_all(); if (m_thread->joinable()) { m_thread->join(); } delete m_thread; m_thread = nullptr; } //关闭文件 m_mtx.lock(); if (m_file) { m_file->flush(); m_file->close(); delete m_file; m_file = nullptr; } m_mtx.unlock(); cout << "日志析构函数完成" << endl; } void LogL::logThread() { std::string str; while (m_thread_run) { //如果队列为空,等待 if (m_log_queue.empty()) { std::unique_lock lock(m_mtx); m_cond_empty.wait(lock); continue; } //取出队列头并出栈 str = m_log_queue.front(); m_log_queue.pop(); m_cond_full.notify_one(); switch (m_logType) { case LTStd: cout << str << endl; break; case LTTxt: { if (m_file == nullptr) { std::cerr << "logThread():日志文件打开失败" << endl; break; } *m_file << str << endl; break; } case LTUdp: try { m_sender->send(str); } catch (const std::runtime_error &e) { std::cerr << e.what() << endl; } break; case LTTcp: break; } } } void LogL::debug(const std::string &str) { log(str, LLDebug); } void LogL::info(const std::string &str) { log(str, LLInfo); } void LogL::warn(const std::string &str) { log(str, LLWarn); } void LogL::error(const std::string &str) { log(str, LLError); } void LogL::log(const std::string &str, LogLevel level) { if (level < m_logLevel) { return; } string tmpLever; switch (level) { case LLDebug: tmpLever = " [debug]:"; break; case LLInfo: tmpLever = " [info]:"; break; case LLWarn: tmpLever = " [warn]:"; break; case LLError: tmpLever = " [error]:"; break; } myDateTime now; if (m_logType == LTTxt) { //逾期更新日志文件 m_mtx.lock(); if (now.day != m_today) { if (m_file) { m_file->flush(); m_file->close(); delete m_file; m_file = nullptr; } std::string dirName; if (m_logPath.empty()) { dirName = "log"; } else { dirName = m_logPath; } myMkdir(dirName.c_str()); std::string fileName = dirName + "/" + now.toDate() + ".txt"; m_file = new std::ofstream(fileName, std::ios::app); } m_mtx.unlock(); } std::string logInfo = now.toDateTime() + tmpLever + str; if (m_isSync) { switch (m_logType) { case LTStd: cout << logInfo << endl; break; case LTTxt: { if (m_file == nullptr) { std::cerr << "日志文件打开失败" << endl; break; } *m_file << logInfo << endl; break; } case LTUdp: try { m_sender->send(logInfo); } catch (const std::runtime_error &e) { std::cerr << e.what() << endl; } break; case LTTcp: break; } } else { if (m_log_queue.size() >= 4096) { std::unique_lock lock(m_mtx); m_cond_full.wait(lock); } m_mtx.lock(); m_log_queue.push(logInfo); m_mtx.unlock(); m_cond_empty.notify_all(); } } bool createPath(const char *path) { struct stat st = {0}; if (stat(path, &st) == -1) { #if WIN32 if (mkdir(path) == 0) { #else if (mkdir(path, 0777) == 0) { #endif return true; } else { std::cerr << "Failed to create directory: " << path << std::endl; return false; } } return true; // Directory already exists } bool myMkdir(const char *path) { char buffer[1024]; char *p = buffer; const char *sep = "/"; strcpy(buffer, path); while ((p = strchr(p, *sep)) != nullptr) { *p = '\0'; if (!createPath(buffer)) { return false; } *p = *sep; p++; } createPath(buffer); return true; } myDateTime::myDateTime() { auto now = std::chrono::system_clock::now(); std::time_t time1 = std::chrono::system_clock::to_time_t(now); std::tm *tm1 = std::localtime(&time1); year = tm1->tm_year + 1900; month = tm1->tm_mon + 1; day = tm1->tm_mday; hour = tm1->tm_hour; minute = tm1->tm_min; second = tm1->tm_sec; } std::string myDateTime::toDate() const { string date = to_string(year) + "-"; if (month < 10) { date += "0" + to_string(month) + "-"; } else { date += to_string(month) + "-"; } if (day < 10) { date += "0" + to_string(day); } else { date += to_string(day); } return date; } std::string myDateTime::toTime() const { string time; if (hour < 10) { time += "0" + to_string(hour) + ":"; } else { time += to_string(hour) + ":"; } if (minute < 10) { time += "0" + to_string(minute) + ":"; } else { time += to_string(minute) + ":"; } if (second < 10) { time += "0" + to_string(second); } else { time += to_string(second); } return time; } std::string myDateTime::toDateTime() const { string dateTime = toDate() + " " + toTime(); return dateTime; }