finnal
This commit is contained in:
635
ApCreate/NetraLib/src/NetRequest.cpp
Normal file
635
ApCreate/NetraLib/src/NetRequest.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
#include "NetRequest.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
|
||||
namespace ntq
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static std::string joinPath(const std::string &base, const std::string &path)
|
||||
{
|
||||
if (base.empty()) return path.empty() || path[0] == '/' ? path : std::string("/") + path;
|
||||
if (path.empty()) return base[0] == '/' ? base : std::string("/") + base;
|
||||
bool base_has = base.front() == '/';
|
||||
bool base_end = base.back() == '/';
|
||||
bool path_has = path.front() == '/';
|
||||
std::string b = base_has ? base : std::string("/") + base;
|
||||
if (base_end && path_has) return b + path.substr(1);
|
||||
if (!base_end && !path_has) return b + "/" + path;
|
||||
return b + path;
|
||||
}
|
||||
|
||||
static std::string paramsToQuery(const httplib::Params ¶ms)
|
||||
{
|
||||
if (params.empty()) return {};
|
||||
std::string s;
|
||||
bool first = true;
|
||||
for (auto &kv : params)
|
||||
{
|
||||
if (!first) s += '&';
|
||||
first = false;
|
||||
s += kv.first;
|
||||
s += '=';
|
||||
s += kv.second;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static httplib::Headers mergeHeaders(const httplib::Headers &a, const httplib::Headers &b)
|
||||
{
|
||||
httplib::Headers h = a;
|
||||
for (auto &kv : b)
|
||||
{
|
||||
// 覆盖同名 header:先删再插
|
||||
h.erase(kv.first);
|
||||
h.emplace(kv.first, kv.second);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
class ConcurrencyGate
|
||||
{
|
||||
public:
|
||||
explicit ConcurrencyGate(size_t limit) : limit_(limit), active_(0) {}
|
||||
|
||||
void set_limit(size_t limit)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mtx_);
|
||||
limit_ = limit > 0 ? limit : 1;
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
struct Guard
|
||||
{
|
||||
ConcurrencyGate &g;
|
||||
explicit Guard(ConcurrencyGate &gate) : g(gate) { g.enter(); }
|
||||
~Guard() { g.leave(); }
|
||||
};
|
||||
|
||||
private:
|
||||
friend struct Guard;
|
||||
void enter()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx_);
|
||||
cv_.wait(lk, [&]{ return active_ < limit_; });
|
||||
++active_;
|
||||
}
|
||||
void leave()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mtx_);
|
||||
if (active_ > 0) --active_;
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
size_t limit_;
|
||||
size_t active_;
|
||||
std::mutex mtx_;
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
|
||||
struct NetRequest::Impl
|
||||
{
|
||||
RequestOptions opts;
|
||||
LogCallback logger;
|
||||
Stats stats;
|
||||
|
||||
// 并发控制
|
||||
ConcurrencyGate gate{4};
|
||||
|
||||
// 缓存
|
||||
struct CacheEntry
|
||||
{
|
||||
HttpResponse resp;
|
||||
std::chrono::steady_clock::time_point expiry;
|
||||
};
|
||||
bool cache_enabled = false;
|
||||
std::chrono::milliseconds cache_ttl{0};
|
||||
std::unordered_map<std::string, CacheEntry> cache;
|
||||
std::mutex cache_mtx;
|
||||
|
||||
void log(const std::string &msg)
|
||||
{
|
||||
if (logger) logger(msg);
|
||||
}
|
||||
|
||||
template <typename ClientT>
|
||||
void apply_client_options(ClientT &cli)
|
||||
{
|
||||
const time_t c_sec = static_cast<time_t>(opts.connect_timeout_ms / 1000);
|
||||
const time_t c_usec = static_cast<time_t>((opts.connect_timeout_ms % 1000) * 1000);
|
||||
const time_t r_sec = static_cast<time_t>(opts.read_timeout_ms / 1000);
|
||||
const time_t r_usec = static_cast<time_t>((opts.read_timeout_ms % 1000) * 1000);
|
||||
const time_t w_sec = static_cast<time_t>(opts.write_timeout_ms / 1000);
|
||||
const time_t w_usec = static_cast<time_t>((opts.write_timeout_ms % 1000) * 1000);
|
||||
cli.set_connection_timeout(c_sec, c_usec);
|
||||
cli.set_read_timeout(r_sec, r_usec);
|
||||
cli.set_write_timeout(w_sec, w_usec);
|
||||
cli.set_keep_alive(opts.keep_alive);
|
||||
}
|
||||
|
||||
std::string build_full_path(const std::string &path) const
|
||||
{
|
||||
return joinPath(opts.base_path, path);
|
||||
}
|
||||
|
||||
std::string cache_key(const std::string &path, const httplib::Params ¶ms, const httplib::Headers &headers)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << opts.scheme << "://" << opts.host << ':' << opts.port << build_full_path(path);
|
||||
if (!params.empty()) oss << '?' << paramsToQuery(params);
|
||||
for (auto &kv : headers) oss << '|' << kv.first << '=' << kv.second;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void record_latency(double ms)
|
||||
{
|
||||
stats.last_latency_ms = ms;
|
||||
const double alpha = 0.2;
|
||||
if (stats.avg_latency_ms <= 0.0) stats.avg_latency_ms = ms;
|
||||
else stats.avg_latency_ms = alpha * ms + (1.0 - alpha) * stats.avg_latency_ms;
|
||||
}
|
||||
|
||||
static ErrorCode map_error()
|
||||
{
|
||||
// 简化:无法区分具体错误码,统一归为 Network
|
||||
return ErrorCode::Network;
|
||||
}
|
||||
};
|
||||
|
||||
NetRequest::NetRequest(const RequestOptions &options)
|
||||
: impl_(new Impl)
|
||||
{
|
||||
impl_->opts = options;
|
||||
if (impl_->opts.scheme == "https" && impl_->opts.port == 80) impl_->opts.port = 443;
|
||||
if (impl_->opts.scheme == "http" && impl_->opts.port == 0) impl_->opts.port = 80;
|
||||
}
|
||||
|
||||
NetRequest::~NetRequest()
|
||||
{
|
||||
delete impl_;
|
||||
}
|
||||
|
||||
void NetRequest::setLogger(LogCallback logger)
|
||||
{
|
||||
impl_->logger = std::move(logger);
|
||||
}
|
||||
|
||||
void NetRequest::setMaxConcurrentRequests(size_t n)
|
||||
{
|
||||
impl_->gate.set_limit(n > 0 ? n : 1);
|
||||
}
|
||||
|
||||
void NetRequest::enableCache(std::chrono::milliseconds ttl)
|
||||
{
|
||||
impl_->cache_enabled = true;
|
||||
impl_->cache_ttl = ttl.count() > 0 ? ttl : std::chrono::milliseconds(1000);
|
||||
}
|
||||
|
||||
void NetRequest::disableCache()
|
||||
{
|
||||
impl_->cache_enabled = false;
|
||||
std::lock_guard<std::mutex> lk(impl_->cache_mtx);
|
||||
impl_->cache.clear();
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> NetRequest::Get(const std::string &path,
|
||||
const httplib::Params &query,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
ConcurrencyGate::Guard guard(impl_->gate);
|
||||
impl_->stats.total_requests++;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (impl_->cache_enabled)
|
||||
{
|
||||
std::string key = impl_->cache_key(path, query, mergeHeaders(impl_->opts.default_headers, headers));
|
||||
std::lock_guard<std::mutex> lk(impl_->cache_mtx);
|
||||
auto it = impl_->cache.find(key);
|
||||
if (it != impl_->cache.end() && std::chrono::steady_clock::now() < it->second.expiry)
|
||||
{
|
||||
if (err) *err = ErrorCode::None;
|
||||
auto resp = it->second.resp;
|
||||
resp.from_cache = true;
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> result;
|
||||
ErrorCode local_err = ErrorCode::None;
|
||||
|
||||
const auto full_path = impl_->build_full_path(path);
|
||||
auto merged_headers = mergeHeaders(impl_->opts.default_headers, headers);
|
||||
|
||||
if (impl_->opts.scheme == "https")
|
||||
{
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = query.empty() ? cli.Get(full_path.c_str(), merged_headers)
|
||||
: cli.Get(full_path.c_str(), query, merged_headers);
|
||||
if (res)
|
||||
{
|
||||
HttpResponse r;
|
||||
r.status = res->status;
|
||||
r.body = res->body;
|
||||
r.headers = res->headers;
|
||||
r.from_cache = false;
|
||||
result = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
#else
|
||||
impl_->log("HTTPS requested but OpenSSL is not enabled; falling back to error.");
|
||||
local_err = ErrorCode::SSL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
httplib::Client cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = query.empty() ? cli.Get(full_path.c_str(), merged_headers)
|
||||
: cli.Get(full_path.c_str(), query, merged_headers);
|
||||
if (res)
|
||||
{
|
||||
HttpResponse r;
|
||||
r.status = res->status;
|
||||
r.body = res->body;
|
||||
r.headers = res->headers;
|
||||
r.from_cache = false;
|
||||
result = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
impl_->record_latency(std::chrono::duration<double, std::milli>(end - start).count());
|
||||
|
||||
if (!result.has_value())
|
||||
{
|
||||
impl_->stats.total_errors++;
|
||||
if (err) *err = local_err;
|
||||
return ntq::nullopt;
|
||||
}
|
||||
|
||||
if (impl_->cache_enabled)
|
||||
{
|
||||
std::string key = impl_->cache_key(path, query, merged_headers);
|
||||
std::lock_guard<std::mutex> lk(impl_->cache_mtx);
|
||||
impl_->cache[key] = Impl::CacheEntry{*result, std::chrono::steady_clock::now() + impl_->cache_ttl};
|
||||
}
|
||||
if (err) *err = ErrorCode::None;
|
||||
return result;
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> NetRequest::PostJson(const std::string &path,
|
||||
const std::string &json,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
ConcurrencyGate::Guard guard(impl_->gate);
|
||||
impl_->stats.total_requests++;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
ntq::optional<HttpResponse> result;
|
||||
ErrorCode local_err = ErrorCode::None;
|
||||
|
||||
const auto full_path = impl_->build_full_path(path);
|
||||
auto merged_headers = mergeHeaders(impl_->opts.default_headers, headers);
|
||||
|
||||
if (impl_->opts.scheme == "https")
|
||||
{
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = cli.Post(full_path.c_str(), merged_headers, json, "application/json");
|
||||
if (res)
|
||||
{
|
||||
HttpResponse r{res->status, res->body, res->headers, false};
|
||||
result = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
#else
|
||||
local_err = ErrorCode::SSL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
httplib::Client cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = cli.Post(full_path.c_str(), merged_headers, json, "application/json");
|
||||
if (res)
|
||||
{
|
||||
HttpResponse r{res->status, res->body, res->headers, false};
|
||||
result = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
impl_->record_latency(std::chrono::duration<double, std::milli>(end - start).count());
|
||||
if (!result)
|
||||
{
|
||||
impl_->stats.total_errors++;
|
||||
if (err) *err = local_err;
|
||||
return ntq::nullopt;
|
||||
}
|
||||
if (err) *err = ErrorCode::None;
|
||||
return result;
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> NetRequest::PostForm(const std::string &path,
|
||||
const httplib::Params &form,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
ConcurrencyGate::Guard guard(impl_->gate);
|
||||
impl_->stats.total_requests++;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
ntq::optional<HttpResponse> result;
|
||||
ErrorCode local_err = ErrorCode::None;
|
||||
|
||||
const auto full_path = impl_->build_full_path(path);
|
||||
auto merged_headers = mergeHeaders(impl_->opts.default_headers, headers);
|
||||
|
||||
if (impl_->opts.scheme == "https")
|
||||
{
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = cli.Post(full_path.c_str(), merged_headers, form);
|
||||
if (res)
|
||||
{
|
||||
HttpResponse r{res->status, res->body, res->headers, false};
|
||||
result = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
#else
|
||||
local_err = ErrorCode::SSL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
httplib::Client cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = cli.Post(full_path.c_str(), merged_headers, form);
|
||||
if (res)
|
||||
{
|
||||
HttpResponse r{res->status, res->body, res->headers, false};
|
||||
result = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
impl_->record_latency(std::chrono::duration<double, std::milli>(end - start).count());
|
||||
if (!result)
|
||||
{
|
||||
impl_->stats.total_errors++;
|
||||
if (err) *err = local_err;
|
||||
return ntq::nullopt;
|
||||
}
|
||||
if (err) *err = ErrorCode::None;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::future<ntq::optional<HttpResponse>> NetRequest::GetAsync(const std::string &path,
|
||||
const httplib::Params &query,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
return std::async(std::launch::async, [this, path, query, headers, err]() mutable {
|
||||
ErrorCode local;
|
||||
auto r = Get(path, query, headers, &local);
|
||||
if (err) *err = local;
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
std::future<ntq::optional<HttpResponse>> NetRequest::PostJsonAsync(const std::string &path,
|
||||
const std::string &json,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
return std::async(std::launch::async, [this, path, json, headers, err]() mutable {
|
||||
ErrorCode local;
|
||||
auto r = PostJson(path, json, headers, &local);
|
||||
if (err) *err = local;
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
std::future<ntq::optional<HttpResponse>> NetRequest::PostFormAsync(const std::string &path,
|
||||
const httplib::Params &form,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
return std::async(std::launch::async, [this, path, form, headers, err]() mutable {
|
||||
ErrorCode local;
|
||||
auto r = PostForm(path, form, headers, &local);
|
||||
if (err) *err = local;
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
bool NetRequest::DownloadToFile(const std::string &path,
|
||||
const std::string &local_file,
|
||||
const httplib::Headers &headers,
|
||||
bool resume,
|
||||
size_t /*chunk_size*/,
|
||||
ErrorCode *err)
|
||||
{
|
||||
ConcurrencyGate::Guard guard(impl_->gate);
|
||||
impl_->stats.total_requests++;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
std::ios_base::openmode mode = std::ios::binary | std::ios::out;
|
||||
size_t offset = 0;
|
||||
if (resume)
|
||||
{
|
||||
std::ifstream in(local_file, std::ios::binary | std::ios::ate);
|
||||
if (in)
|
||||
{
|
||||
offset = static_cast<size_t>(in.tellg());
|
||||
}
|
||||
mode |= std::ios::app;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode |= std::ios::trunc;
|
||||
}
|
||||
|
||||
std::ofstream out(local_file, mode);
|
||||
if (!out)
|
||||
{
|
||||
if (err) *err = ErrorCode::IOError;
|
||||
impl_->stats.total_errors++;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto merged_headers = mergeHeaders(impl_->opts.default_headers, headers);
|
||||
if (resume && offset > 0)
|
||||
{
|
||||
merged_headers.emplace("Range", "bytes=" + std::to_string(offset) + "-");
|
||||
}
|
||||
|
||||
const auto full_path = impl_->build_full_path(path);
|
||||
int status_code = 0;
|
||||
ErrorCode local_err = ErrorCode::None;
|
||||
|
||||
auto content_receiver = [&](const char *data, size_t data_length) {
|
||||
out.write(data, static_cast<std::streamsize>(data_length));
|
||||
return static_cast<bool>(out);
|
||||
};
|
||||
|
||||
bool ok = false;
|
||||
if (impl_->opts.scheme == "https")
|
||||
{
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = cli.Get(full_path.c_str(), merged_headers, content_receiver);
|
||||
if (res)
|
||||
{
|
||||
status_code = res->status;
|
||||
ok = (status_code == 200 || status_code == 206);
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
#else
|
||||
local_err = ErrorCode::SSL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
httplib::Client cli(impl_->opts.host.c_str(), impl_->opts.port);
|
||||
impl_->apply_client_options(cli);
|
||||
auto res = cli.Get(full_path.c_str(), merged_headers, content_receiver);
|
||||
if (res)
|
||||
{
|
||||
status_code = res->status;
|
||||
ok = (status_code == 200 || status_code == 206);
|
||||
}
|
||||
else
|
||||
{
|
||||
local_err = Impl::map_error();
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
impl_->record_latency(std::chrono::duration<double, std::milli>(end - start).count());
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
impl_->stats.total_errors++;
|
||||
if (err) *err = local_err;
|
||||
return false;
|
||||
}
|
||||
if (err) *err = ErrorCode::None;
|
||||
return true;
|
||||
}
|
||||
|
||||
NetRequest::Stats NetRequest::getStats() const
|
||||
{
|
||||
return impl_->stats;
|
||||
}
|
||||
|
||||
// ------------------------- Quick helpers -------------------------
|
||||
namespace {
|
||||
struct ParsedURL {
|
||||
std::string scheme;
|
||||
std::string host;
|
||||
int port = 0;
|
||||
std::string path_and_query;
|
||||
bool ok = false;
|
||||
};
|
||||
|
||||
static ParsedURL parse_url(const std::string &url)
|
||||
{
|
||||
ParsedURL p; p.ok = false;
|
||||
// very small parser: scheme://host[:port]/path[?query]
|
||||
auto pos_scheme = url.find("://");
|
||||
if (pos_scheme == std::string::npos) return p;
|
||||
p.scheme = url.substr(0, pos_scheme);
|
||||
size_t pos_host = pos_scheme + 3;
|
||||
|
||||
size_t pos_path = url.find('/', pos_host);
|
||||
std::string hostport = pos_path == std::string::npos ? url.substr(pos_host)
|
||||
: url.substr(pos_host, pos_path - pos_host);
|
||||
auto pos_colon = hostport.find(':');
|
||||
if (pos_colon == std::string::npos) {
|
||||
p.host = hostport;
|
||||
p.port = (p.scheme == "https") ? 443 : 80;
|
||||
} else {
|
||||
p.host = hostport.substr(0, pos_colon);
|
||||
std::string port_str = hostport.substr(pos_colon + 1);
|
||||
p.port = port_str.empty() ? ((p.scheme == "https") ? 443 : 80) : std::atoi(port_str.c_str());
|
||||
}
|
||||
p.path_and_query = (pos_path == std::string::npos) ? "/" : url.substr(pos_path);
|
||||
p.ok = !p.host.empty();
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> NetRequest::QuickGet(const std::string &url,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
auto p = parse_url(url);
|
||||
if (!p.ok) { if (err) *err = ErrorCode::InvalidURL; return std::nullopt; }
|
||||
RequestOptions opt; opt.scheme = p.scheme; opt.host = p.host; opt.port = p.port;
|
||||
NetRequest req(opt);
|
||||
return req.Get(p.path_and_query, {}, headers, err);
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> NetRequest::QuickPostJson(const std::string &url,
|
||||
const std::string &json,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
auto p = parse_url(url);
|
||||
if (!p.ok) { if (err) *err = ErrorCode::InvalidURL; return std::nullopt; }
|
||||
RequestOptions opt; opt.scheme = p.scheme; opt.host = p.host; opt.port = p.port;
|
||||
NetRequest req(opt);
|
||||
return req.PostJson(p.path_and_query, json, headers, err);
|
||||
}
|
||||
|
||||
ntq::optional<HttpResponse> NetRequest::QuickPostForm(const std::string &url,
|
||||
const httplib::Params &form,
|
||||
const httplib::Headers &headers,
|
||||
ErrorCode *err)
|
||||
{
|
||||
auto p = parse_url(url);
|
||||
if (!p.ok) { if (err) *err = ErrorCode::InvalidURL; return std::nullopt; }
|
||||
RequestOptions opt; opt.scheme = p.scheme; opt.host = p.host; opt.port = p.port;
|
||||
NetRequest req(opt);
|
||||
return req.PostForm(p.path_and_query, form, headers, err);
|
||||
}
|
||||
}
|
664
ApCreate/NetraLib/src/Netra.cpp
Normal file
664
ApCreate/NetraLib/src/Netra.cpp
Normal file
@@ -0,0 +1,664 @@
|
||||
#include "Netra.hpp"
|
||||
|
||||
namespace QCL
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
TcpServer::TcpServer(int port)
|
||||
: port_(port), running_(false), serverSock_(-1) {}
|
||||
|
||||
/**
|
||||
* @brief 析构函数中调用stop()确保服务器资源被释放
|
||||
*/
|
||||
TcpServer::~TcpServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动服务器:
|
||||
* 1. 创建监听socket(TCP)
|
||||
* 2. 绑定端口
|
||||
* 3. 监听端口
|
||||
* 4. 启动监听线程acceptThread_
|
||||
*
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool TcpServer::start()
|
||||
{
|
||||
// 创建socket
|
||||
serverSock_ = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (serverSock_ < 0)
|
||||
{
|
||||
std::cerr << "Socket 创建失败\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置socket地址结构
|
||||
sockaddr_in serverAddr;
|
||||
std::memset(&serverAddr, 0, sizeof(serverAddr));
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(port_); // 端口转网络字节序
|
||||
serverAddr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡IP
|
||||
|
||||
// 允许端口重用,防止服务器异常关闭后端口被占用
|
||||
int opt = 1;
|
||||
setsockopt(serverSock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
|
||||
// 绑定端口
|
||||
if (bind(serverSock_, (sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
|
||||
{
|
||||
std::cerr << "绑定失败\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 开始监听,最大等待连接数为5
|
||||
if (listen(serverSock_, 5) < 0)
|
||||
{
|
||||
std::cerr << "监听失败\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置运行标志为true
|
||||
running_ = true;
|
||||
|
||||
// 启动专门接受客户端连接的线程
|
||||
acceptThread_ = std::thread(&TcpServer::acceptClients, this);
|
||||
|
||||
std::cout << "服务器启动,监听端口:" << port_ << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止服务器:
|
||||
* 1. 设置运行标志为false,通知线程退出
|
||||
* 2. 关闭监听socket
|
||||
* 3. 关闭所有客户端socket,清理客户端列表
|
||||
* 4. 等待所有线程退出
|
||||
*/
|
||||
void TcpServer::stop()
|
||||
{
|
||||
running_ = false;
|
||||
|
||||
if (serverSock_ >= 0)
|
||||
{
|
||||
close(serverSock_);
|
||||
serverSock_ = -1;
|
||||
}
|
||||
|
||||
{
|
||||
// 线程安全关闭所有客户端socket
|
||||
std::lock_guard<std::mutex> lock(clientsMutex_);
|
||||
for (int sock : clientSockets_)
|
||||
{
|
||||
close(sock);
|
||||
}
|
||||
clientSockets_.clear();
|
||||
}
|
||||
|
||||
// 等待监听线程退出
|
||||
if (acceptThread_.joinable())
|
||||
acceptThread_.join();
|
||||
|
||||
// 等待所有客户端处理线程退出
|
||||
for (auto &t : clientThreads_)
|
||||
{
|
||||
if (t.joinable())
|
||||
t.join();
|
||||
}
|
||||
|
||||
std::cout << "服务器已停止\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief acceptClients函数循环监听客户端连接请求
|
||||
* 每当accept成功:
|
||||
* 1. 打印客户端IP和Socket信息
|
||||
* 2. 线程安全地将客户端Socket加入clientSockets_列表
|
||||
* 3. 创建新线程调用handleClient处理该客户端收发
|
||||
*/
|
||||
void TcpServer::acceptClients()
|
||||
{
|
||||
while (running_)
|
||||
{
|
||||
sockaddr_in clientAddr;
|
||||
socklen_t clientLen = sizeof(clientAddr);
|
||||
int clientSock = accept(serverSock_, (sockaddr *)&clientAddr, &clientLen);
|
||||
if (clientSock < 0)
|
||||
{
|
||||
if (running_)
|
||||
std::cerr << "接受连接失败\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
// 将客户端IP转换成字符串格式打印
|
||||
char clientIP[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN);
|
||||
std::cout << "客户端连接,IP: " << clientIP << ", Socket: " << clientSock << std::endl;
|
||||
|
||||
{
|
||||
// 加锁保护共享的clientSockets_容器
|
||||
std::lock_guard<std::mutex> lock(clientsMutex_);
|
||||
clientSockets_.push_back(clientSock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送消息给指定客户端
|
||||
* @param clientSock 客户端socket
|
||||
* @param message 发送消息内容
|
||||
*/
|
||||
void TcpServer::sendToClient(int clientSock, const std::string &message)
|
||||
{
|
||||
send(clientSock, message.c_str(), message.size(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 单次接收指定客户端数据
|
||||
* @param clientSock 客户端socket
|
||||
*/
|
||||
std::string TcpServer::receiveFromClient(int clientSock, bool flag)
|
||||
{
|
||||
char buffer[1024];
|
||||
std::memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
int flags = flag ? 0 : MSG_DONTWAIT;
|
||||
ssize_t bytesReceived = recv(clientSock, buffer, sizeof(buffer) - 1, flags);
|
||||
|
||||
if (bytesReceived <= 0)
|
||||
return {};
|
||||
|
||||
return std::string(buffer, bytesReceived);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前所有客户端Socket副本(线程安全)
|
||||
* @return 包含所有客户端socket的vector副本
|
||||
*/
|
||||
std::vector<int> TcpServer::getClientSockets()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(clientsMutex_);
|
||||
return clientSockets_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取连接客户端的IP和端口
|
||||
* @param clientSock 客户端Socket描述符
|
||||
*/
|
||||
char *TcpServer::getClientIPAndPort(int clientSock)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
// 获取客户端地址信息
|
||||
if (getpeername(clientSock, (struct sockaddr *)&addr, &addr_size) == -1)
|
||||
{
|
||||
perror("getpeername failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 分配内存存储结果(格式: "IP:PORT")
|
||||
char *result = (char *)malloc(INET_ADDRSTRLEN + 10);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
// 转换IP和端口
|
||||
char *ip = inet_ntoa(addr.sin_addr);
|
||||
unsigned short port = ntohs(addr.sin_port);
|
||||
|
||||
snprintf(result, INET_ADDRSTRLEN + 10, "%s:%d", ip, port);
|
||||
return result;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
WriteFile::WriteFile(const std::string &filePath)
|
||||
: filePath_(filePath) {}
|
||||
|
||||
/**
|
||||
* @brief 覆盖写文本(线程安全)
|
||||
*/
|
||||
bool WriteFile::overwriteText(const std::string &content)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_); // 加锁
|
||||
return writeToFile(content, std::ios::out | std::ios::trunc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 追加写文本(线程安全)
|
||||
*/
|
||||
bool WriteFile::appendText(const std::string &content)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
return writeToFile(content, std::ios::out | std::ios::app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 覆盖写二进制(线程安全)
|
||||
*/
|
||||
bool WriteFile::overwriteBinary(const std::vector<char> &data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
return writeBinary(data, std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 追加写二进制(线程安全)
|
||||
*/
|
||||
bool WriteFile::appendBinary(const std::vector<char> &data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
return writeBinary(data, std::ios::out | std::ios::app | std::ios::binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通用文本写入(私有)
|
||||
*/
|
||||
bool WriteFile::writeToFile(const std::string &content, std::ios::openmode mode)
|
||||
{
|
||||
std::ofstream file(filePath_, mode);
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
file << content;
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通用二进制写入(私有)
|
||||
*/
|
||||
bool WriteFile::writeBinary(const std::vector<char> &data, std::ios::openmode mode)
|
||||
{
|
||||
std::ofstream file(filePath_, mode);
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
file.write(data.data(), data.size());
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t WriteFile::countBytesPattern(const std::string &pattern, bool includePattern)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
|
||||
if (pattern.empty())
|
||||
return 0;
|
||||
|
||||
std::ifstream file(filePath_, std::ios::binary);
|
||||
if (!file.is_open())
|
||||
return 0;
|
||||
|
||||
const size_t chunkSize = 4096;
|
||||
std::string buffer;
|
||||
buffer.reserve(chunkSize * 2);
|
||||
|
||||
size_t totalRead = 0;
|
||||
char chunk[chunkSize];
|
||||
|
||||
while (file.read(chunk, chunkSize) || file.gcount() > 0)
|
||||
{
|
||||
size_t bytesRead = file.gcount();
|
||||
buffer.append(chunk, bytesRead);
|
||||
|
||||
size_t pos = buffer.find(pattern);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
size_t absolutePos = totalRead + pos; // 关键:加上 totalRead
|
||||
return includePattern ? (absolutePos + pattern.size()) : absolutePos;
|
||||
}
|
||||
|
||||
if (buffer.size() > pattern.size())
|
||||
buffer.erase(0, buffer.size() - pattern.size());
|
||||
|
||||
totalRead += bytesRead; // 读完后再累计
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WriteFile::writeAfterPatternOrAppend(const std::string &pattern, const std::string &content)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
|
||||
// 读取整个文件
|
||||
std::ifstream in(filePath_, std::ios::binary);
|
||||
if (!in.is_open())
|
||||
return false;
|
||||
|
||||
std::string fileData((std::istreambuf_iterator<char>(in)), {});
|
||||
in.close();
|
||||
|
||||
size_t pos = fileData.find(pattern);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
// 模式存在,插入位置在模式结尾
|
||||
pos += pattern.size();
|
||||
|
||||
// 删除模式后所有内容
|
||||
if (pos < fileData.size())
|
||||
fileData.erase(pos);
|
||||
|
||||
// 插入新内容
|
||||
fileData.insert(pos, content);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 模式不存在,直接追加到文件末尾
|
||||
if (!fileData.empty() && fileData.back() != '\n')
|
||||
fileData += '\n'; // 保证换行
|
||||
fileData += content;
|
||||
}
|
||||
|
||||
// 写回文件
|
||||
std::ofstream out(filePath_, std::ios::binary | std::ios::trunc);
|
||||
if (!out.is_open())
|
||||
return false;
|
||||
|
||||
out.write(fileData.data(), fileData.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFile::overwriteAtPos(const std::string &content, size_t pos, size_t length)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
|
||||
// 打开文件读取
|
||||
std::ifstream in(filePath_, std::ios::binary);
|
||||
if (!in.is_open())
|
||||
return false;
|
||||
|
||||
std::string fileData((std::istreambuf_iterator<char>(in)), {});
|
||||
in.close();
|
||||
|
||||
// 边界检查
|
||||
if (pos >= fileData.size())
|
||||
return false; // pos 超过文件范围,无法覆盖
|
||||
|
||||
// 生成要覆盖的实际数据块
|
||||
std::string overwriteBlock;
|
||||
if (content.size() >= length)
|
||||
{
|
||||
overwriteBlock = content.substr(0, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
overwriteBlock = content;
|
||||
overwriteBlock.append(length - content.size(), '\0'); // 补齐
|
||||
}
|
||||
|
||||
// 计算实际可写范围
|
||||
size_t maxWritable = std::min(length, fileData.size() - pos);
|
||||
|
||||
// 覆盖
|
||||
fileData.replace(pos, maxWritable, overwriteBlock.substr(0, maxWritable));
|
||||
|
||||
// 写回文件
|
||||
std::ofstream out(filePath_, std::ios::binary | std::ios::trunc);
|
||||
if (!out.is_open())
|
||||
return false;
|
||||
|
||||
out.write(fileData.data(), fileData.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFile::insertAfterPos(const std::string &content, size_t pos, size_t length)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
|
||||
// 打开文件读取
|
||||
std::ifstream in(filePath_, std::ios::binary);
|
||||
if (!in.is_open())
|
||||
return false;
|
||||
|
||||
std::string fileData((std::istreambuf_iterator<char>(in)), {});
|
||||
in.close();
|
||||
|
||||
// 边界检查
|
||||
if (pos > fileData.size())
|
||||
pos = fileData.size(); // 如果 pos 超出范围,就视为文件末尾
|
||||
|
||||
// 生成要插入的实际数据块
|
||||
std::string insertBlock;
|
||||
if (content.size() >= length)
|
||||
{
|
||||
insertBlock = content.substr(0, length); // 只取前 length 个字节
|
||||
}
|
||||
else
|
||||
{
|
||||
insertBlock = content; // 全部内容
|
||||
insertBlock.append(length - content.size(), '\0'); // 补足空字节
|
||||
}
|
||||
|
||||
// 插入到 pos 后面
|
||||
fileData.insert(pos + 1, insertBlock);
|
||||
|
||||
// 写回文件
|
||||
std::ofstream out(filePath_, std::ios::binary | std::ios::trunc);
|
||||
if (!out.is_open())
|
||||
return false;
|
||||
|
||||
out.write(fileData.data(), fileData.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
ReadFile::ReadFile(const std::string &filename) : filename_(filename) {}
|
||||
|
||||
ReadFile::~ReadFile()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
Close();
|
||||
}
|
||||
|
||||
bool ReadFile::Open()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (file_.is_open())
|
||||
file_.close();
|
||||
file_.open(filename_, std::ios::in | std::ios::binary);
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
void ReadFile::Close()
|
||||
{
|
||||
if (file_.is_open())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
file_.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadFile::IsOpen() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
std::string ReadFile::ReadAllText()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (!file_.is_open() && !Open())
|
||||
return "";
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << file_.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::vector<char> ReadFile::ReadAllBinary()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (!file_.is_open() && !Open())
|
||||
return {};
|
||||
|
||||
return ReadBytes(GetFileSize());
|
||||
}
|
||||
|
||||
std::vector<std::string> ReadFile::ReadLines()
|
||||
{
|
||||
// std::lock_guard<std::mutex> lock(mtx_);
|
||||
// if (!file_.is_open() && !Open())
|
||||
// return {};
|
||||
|
||||
// std::vector<std::string> lines;
|
||||
// std::string line;
|
||||
// while (std::getline(file_, line))
|
||||
// {
|
||||
// lines.push_back(line);
|
||||
// }
|
||||
// return lines;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (!file_.is_open()) {
|
||||
file_.open(filename_, std::ios::in | std::ios::binary);
|
||||
if (!file_.is_open()) return {};
|
||||
}
|
||||
file_.clear();
|
||||
file_.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
while (std::getline(file_, line)) lines.push_back(line);
|
||||
return lines;
|
||||
}
|
||||
|
||||
std::vector<char> ReadFile::ReadBytes(size_t count)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (!file_.is_open() && !Open())
|
||||
return {};
|
||||
|
||||
std::vector<char> buffer(count);
|
||||
file_.read(buffer.data(), count);
|
||||
buffer.resize(file_.gcount());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
size_t ReadFile::GetBytesBefore(const std::string &marker, bool includeMarker)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
|
||||
if (!file_.is_open() && !Open())
|
||||
return 0;
|
||||
|
||||
file_.clear(); // 清除EOF和错误状态
|
||||
file_.seekg(0, std::ios::beg); // 回到文件开头
|
||||
|
||||
const size_t chunkSize = 4096;
|
||||
std::string buffer;
|
||||
buffer.reserve(chunkSize * 2);
|
||||
|
||||
size_t totalRead = 0;
|
||||
char chunk[chunkSize];
|
||||
|
||||
while (file_.read(chunk, chunkSize) || file_.gcount() > 0)
|
||||
{
|
||||
buffer.append(chunk, file_.gcount());
|
||||
size_t pos = buffer.find(marker);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
// 如果 includeMarker 为 true,返回包含 marker 的长度
|
||||
if (includeMarker)
|
||||
return pos + marker.size();
|
||||
else
|
||||
return pos;
|
||||
}
|
||||
|
||||
// 保留末尾部分,避免 buffer 无限增长
|
||||
if (buffer.size() > marker.size())
|
||||
buffer.erase(0, buffer.size() - marker.size());
|
||||
|
||||
totalRead += file_.gcount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<char> ReadFile::ReadBytesFrom(size_t pos, size_t count)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
|
||||
if (!file_.is_open() && !Open())
|
||||
return {};
|
||||
|
||||
size_t filesize = GetFileSize();
|
||||
if (pos >= filesize)
|
||||
return {}; // 起始位置超出文件大小
|
||||
|
||||
file_.clear(); // 清除 EOF 和错误状态
|
||||
file_.seekg(pos, std::ios::beg);
|
||||
if (!file_)
|
||||
return {};
|
||||
|
||||
size_t bytes_to_read = count;
|
||||
if (count == 0 || pos + count > filesize)
|
||||
bytes_to_read = filesize - pos; // 读取到文件末尾
|
||||
|
||||
std::vector<char> buffer(bytes_to_read);
|
||||
file_.read(buffer.data(), bytes_to_read);
|
||||
|
||||
// 实际读取的字节数可能少于请求的数量
|
||||
buffer.resize(file_.gcount());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ReadFile::FileExists() const
|
||||
{
|
||||
return std::filesystem::exists(filename_);
|
||||
}
|
||||
|
||||
size_t ReadFile::GetFileSize() const
|
||||
{
|
||||
if (!FileExists())
|
||||
return 0;
|
||||
return std::filesystem::file_size(filename_);
|
||||
}
|
||||
|
||||
void ReadFile::Reset()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (file_.is_open())
|
||||
{
|
||||
file_.clear();
|
||||
file_.seekg(0, std::ios::beg);
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 屏蔽所有信号
|
||||
void blockAllSignals()
|
||||
{
|
||||
// 忽略全部的信号
|
||||
for (int ii = 1; ii <= 64; ii++)
|
||||
signal(ii, SIG_IGN);
|
||||
}
|
||||
|
||||
std::string Ltrim(const std::string &s)
|
||||
{
|
||||
size_t start = 0;
|
||||
while (start < s.size() && std::isspace(static_cast<unsigned char>(s[start])))
|
||||
{
|
||||
++start;
|
||||
}
|
||||
return s.substr(start);
|
||||
}
|
||||
|
||||
std::string Rtrim(const std::string &s)
|
||||
{
|
||||
if (s.empty())
|
||||
return s;
|
||||
|
||||
size_t end = s.size();
|
||||
while (end > 0 && std::isspace(static_cast<unsigned char>(s[end - 1])))
|
||||
{
|
||||
--end;
|
||||
}
|
||||
return s.substr(0, end);
|
||||
}
|
||||
|
||||
std::string LRtrim(const std::string &s)
|
||||
{
|
||||
return Ltrim(Rtrim(s));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
91
ApCreate/NetraLib/src/encrypt.cpp
Normal file
91
ApCreate/NetraLib/src/encrypt.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "encrypt.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace encrypt
|
||||
{
|
||||
string MD5(const string &info)
|
||||
{
|
||||
auto leftrotate = [](uint32_t x, uint32_t c) -> uint32_t { return (x << c) | (x >> (32 - c)); };
|
||||
|
||||
static const uint32_t s[64] = {
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
|
||||
};
|
||||
static const uint32_t K[64] = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
};
|
||||
|
||||
uint32_t a0 = 0x67452301;
|
||||
uint32_t b0 = 0xefcdab89;
|
||||
uint32_t c0 = 0x98badcfe;
|
||||
uint32_t d0 = 0x10325476;
|
||||
|
||||
std::vector<uint8_t> msg(info.begin(), info.end());
|
||||
uint64_t bit_len = static_cast<uint64_t>(msg.size()) * 8ULL;
|
||||
msg.push_back(0x80);
|
||||
while ((msg.size() % 64) != 56) msg.push_back(0x00);
|
||||
for (int i = 0; i < 8; ++i) msg.push_back(static_cast<uint8_t>((bit_len >> (8 * i)) & 0xff));
|
||||
|
||||
for (size_t offset = 0; offset < msg.size(); offset += 64)
|
||||
{
|
||||
uint32_t M[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
size_t j = offset + i * 4;
|
||||
M[i] = static_cast<uint32_t>(msg[j]) |
|
||||
(static_cast<uint32_t>(msg[j + 1]) << 8) |
|
||||
(static_cast<uint32_t>(msg[j + 2]) << 16) |
|
||||
(static_cast<uint32_t>(msg[j + 3]) << 24);
|
||||
}
|
||||
|
||||
uint32_t A = a0, B = b0, C = c0, D = d0;
|
||||
for (uint32_t i = 0; i < 64; ++i)
|
||||
{
|
||||
uint32_t F, g;
|
||||
if (i < 16) { F = (B & C) | ((~B) & D); g = i; }
|
||||
else if (i < 32) { F = (D & B) | ((~D) & C); g = (5 * i + 1) % 16; }
|
||||
else if (i < 48) { F = B ^ C ^ D; g = (3 * i + 5) % 16; }
|
||||
else { F = C ^ (B | (~D)); g = (7 * i) % 16; }
|
||||
|
||||
F = F + A + K[i] + M[g];
|
||||
A = D;
|
||||
D = C;
|
||||
C = B;
|
||||
B = B + leftrotate(F, s[i]);
|
||||
}
|
||||
|
||||
a0 += A; b0 += B; c0 += C; d0 += D;
|
||||
}
|
||||
|
||||
uint8_t digest[16];
|
||||
auto u32_to_le = [](uint32_t v, uint8_t out[4]) {
|
||||
out[0] = static_cast<uint8_t>(v & 0xff);
|
||||
out[1] = static_cast<uint8_t>((v >> 8) & 0xff);
|
||||
out[2] = static_cast<uint8_t>((v >> 16) & 0xff);
|
||||
out[3] = static_cast<uint8_t>((v >> 24) & 0xff);
|
||||
};
|
||||
u32_to_le(a0, digest + 0);
|
||||
u32_to_le(b0, digest + 4);
|
||||
u32_to_le(c0, digest + 8);
|
||||
u32_to_le(d0, digest + 12);
|
||||
|
||||
static const char *hex = "0123456789abcdef";
|
||||
std::string out;
|
||||
out.resize(32);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
out[i * 2] = hex[(digest[i] >> 4) & 0x0f];
|
||||
out[i * 2 + 1] = hex[digest[i] & 0x0f];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user