远程桌面控制(一)

单例模式是怎么实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CServerSocket {
private:
static CServerSocket* m_instance;
static CHelper m_helper; // 辅助类,确保构造和析构
public:
static CServerSocket* getInstance() {
if (!m_instance) m_instance = new CServerSocket();
return m_instance;
}
private:
CServerSocket() { ... } // 私有构造
~CServerSocket() { ... } // 私有析构
class CHelper { // RAII 管理单例生命周期
public:
CHelper() { CServerSocket::getInstance(); }
~CHelper() { CServerSocket::releaseInstance(); }
};
};

设计思想

  • 禁止用户直接构造,必须通过 getInstance() 获取唯一对象。
  • CHelper 辅助类:
    • 构造时自动创建 CServerSocket 单例。
    • 析构时自动释放,防止内存泄漏。

总结

模块 核心功能
CPacket 协议封装,提供 打包/解包 能力
CServerSocket 服务器核心,单例管理收发数据
单例模式 确保全局唯一服务器实例
字节对齐 (#pragma pack) 保证数据包严格对齐,避免解析错误

适用场景

  • 小型客户端-服务器通信(如文件传输、远程控制等)。
  • 可扩展性较强,sCmd 可用于扩展不同业务逻辑。

1
2
3
CServerSocket* CServerSocket::m_instance = NULL;  // 静态成员初始化
CServerSocket::CHelper CServerSocket::m_helper; // RAII辅助对象
CServerSocket* pserver = CServerSocket::getInstance(); // 全局访问点

设计模式解析

  1. 单例控制

    • m_instance 作为静态指针,保证全进程唯一实例
    • 通过 getInstance() 获取唯一实例,首次调用时构造对象
  2. RAII助手类 (CHelper)

    1
    class CHelper {public:    CHelper() { CServerSocket::getInstance(); }  // 构造时触发单例创建    ~CHelper() { CServerSocket::releaseInstance(); } // 析构时释放单例};
    • 构造阶段:程序启动时,全局 m_helper 的构造自动触发单例创建
    • 析构阶段:程序退出时,自动调用 releaseInstance() 释放资源
  3. 全局访问点

    • pserver 提供全局统一访问入口

    • 使用示例:

      1
      pserver->InitSocket();  // 任何地方都可直接使用

CPacket 打包构造函数

构造函数原型

1
CPacket::CPacket(WORD nCmd, const BYTE* pData, size_t nSize) 

参数说明

参数 类型 说明
nCmd WORD 协议命令字(2字节)
pData const BYTE* 负载数据指针
nSize size_t 负载数据长度

主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// RemoveCtrl.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include "framework.h"
#include "RemoveCtrl.h"
#include "ServerSocket.h"
#include <direct.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//#pragma comment(linker, "/subsystem:windows /entry:WinMainCRTStartup" )
//#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup" )
//#pragma comment(linker, "/subsystem:console /entry:WinMainCRTStartup" )
//#pragma comment(linker, "/subsystem:console /entry:mainCRTStartup" )


// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

// 将给定的二进制数据以十六进制格式打印出来,并将输出发送到调试器(通过OutputDebugStringA)
void Dump(BYTE* pData, size_t nSize) {
//
std::string strOut;
for (size_t i = 0; i < nSize; i++) {
char buf[8] = "";
if (i > 0 && (i % 16 == 0)) strOut += "\n";
snprintf(buf, sizeof(buf), "%02X ", pData[i] & 0xFF);
strOut += buf;
}
strOut += "\n";
OutputDebugStringA(strOut.c_str());
}


int MakeDriverInfo() { // 1 => A 2 => B 3 => C
std::string result;
for (int i = 1; i <= 26; i++) {
//查看每个驱动器是否存在(通过_chdrive函数)
if (_chdrive(i) == 0) {
//将存在的驱动字母(如A、B、C)拼接成一个字符串,用逗号分隔。
if (result.size() > 0)
result += ',';
result += 'A' + i - 1;
}
}
//最后调用 CServerSocket::getInstance()->Send(CPacket()) 发送数据包。
//CServerSocket::getInstance()->Send(CPacket(1,(BYTE*)result.c_str(),result.size()));
CPacket pack(1, (BYTE*)result.c_str(), result.size());
Dump((BYTE*)pack.Data(), pack.Size());
//CServerSocket::getInstance()->Send(pack);

return 0;
}

#include <stdio.h>
#include <io.h>
#include <list>
#include "RemoteCtrl.h"
/*结构体和类十分相似 区别是结构体默认是public 类默认是private*/
typedef struct file_info{
file_info() {
IsInvalid = 0;
IsDirectory = -1;
HasNext = true;
memset(szFileName, 0, sizeof(szFileName));
}
BOOL IsInvalid; // 是否有效
BOOL HasNext; // 是否还有后续 0 否 1 是
char szFileName[256]; // 文件名
BOOL IsDirectory; // 是否为目录 0 否 1 是


}FILEINFO, *PFILEINFO;
int main()
{
int nRetCode = 0;

HMODULE hModule = ::GetModuleHandle(nullptr);

if (hModule != nullptr)
{
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
{
// TODO: 在此处为应用程序的行为编写代码。
wprintf(L"错误: MFC 初始化失败\n");
nRetCode = 1;
}
else
{
// 套接字初始化

// 观察文件
int nCmd = 1;
switch (nCmd)
{
case 1: // 查看磁盘分区
MakeDriverInfo();
break;
case 2:
MakeDirectoryInfo();
break;
}
}
}
else
{
// TODO: 更改错误代码以符合需要
wprintf(L"错误: GetModuleHandle 失败\n");
nRetCode = 1;
}

return nRetCode;
}