Files
CpCtrl/Udp/ConfigParser.cpp
2025-06-13 17:35:27 +08:00

633 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ConfigParser.h"
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <utility>
#include <sys/stat.h>
#include <ctime>
#include <sstream>
#include <memory>
#include <mutex>
#ifdef __linux__
#include <cstring>
#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<std::string, std::string> &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<long long>(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<uint8_t> &data) {
auto sent = sendto(sockfd_,
reinterpret_cast<const char *>(data.data()),
static_cast<int>(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<size_t>(sent);
}
size_t UDPSender::send(const std::string &data) {
auto sent = sendto(sockfd_,
reinterpret_cast<const char *>(data.data()),
static_cast<int>(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<size_t>(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<uint8_t> 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<std::chrono::seconds>(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<ssize_t, std::pair<sockaddr_in, socklen_t>> 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<char *>(buffer_.data()),
static_cast<int>(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<std::string> 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<std::mutex> 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<std::mutex> 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;
}