Skip to content

Commit 5953a4a

Browse files
committed
add client server
1 parent 7c8e816 commit 5953a4a

18 files changed

+16883
-0
lines changed

client/TerminalClient.h

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
#pragma once
2+
#include <windows.h>
3+
#include <string>
4+
#include <thread>
5+
#include <functional>
6+
#include "../string_utils.h"
7+
#pragma comment(lib, "rpcrt4.lib")
8+
9+
10+
// 全交互式CMD主控端封装
11+
class TerminalClient {
12+
public:
13+
typedef std::function<void(void* buf, int len)> func_callback;
14+
typedef std::function<void()> func_failed;
15+
16+
TerminalClient() {
17+
hCmdProcess_ = NULL;
18+
hPipeInCMD_ = NULL;
19+
hPipeOutCMD_ = NULL;
20+
hJob_ = CreateJobObject(NULL, NULL);
21+
if (hJob_) {
22+
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
23+
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
24+
SetInformationJobObject(hJob_, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
25+
}
26+
}
27+
~TerminalClient() {
28+
if (hCmdProcess_) {
29+
HANDLE hTmp = hCmdProcess_;
30+
hCmdProcess_ = NULL;
31+
TerminateProcess(hTmp, -1);
32+
CloseHandle(hTmp);
33+
Sleep(100);
34+
}
35+
if (hPipeInCMD_) {
36+
CloseHandle(hPipeInCMD_);
37+
}
38+
if (hPipeOutCMD_) {
39+
CloseHandle(hPipeOutCMD_);
40+
}
41+
if (hJob_)
42+
CloseHandle(hJob_);
43+
}
44+
45+
// CmdEmulatorFullPath表示cmd模拟器全路径
46+
// cb 表示cmd模拟器输入数据发送回调
47+
bool init(const std::string& CmdEmulatorFullPath, const std::string& title, func_callback cb, func_failed fail) {
48+
if (!cb || CmdEmulatorFullPath.empty()) {
49+
return false;
50+
}
51+
cb_ = cb;
52+
53+
// 1. 创建管道
54+
std::string randPipeName = GetGuidString();
55+
std::string pipe = std::string("\\\\.\\pipe\\") + randPipeName + "inCMD";
56+
HANDLE hPipe = CreateNamedPipeA(
57+
pipe.c_str(), // pipe name
58+
PIPE_ACCESS_DUPLEX, // read/write access
59+
PIPE_TYPE_MESSAGE | // message type pipe
60+
PIPE_READMODE_MESSAGE | // message-read mode
61+
PIPE_WAIT, // blocking mode
62+
PIPE_UNLIMITED_INSTANCES, // max. instances
63+
4096, // output buffer size
64+
4096, // input buffer size
65+
0, // client time-out
66+
NULL); // default security attribute
67+
if (hPipe == INVALID_HANDLE_VALUE) {
68+
return false;
69+
}
70+
hPipeInCMD_ = hPipe;
71+
72+
pipe = std::string("\\\\.\\pipe\\") + randPipeName + "outCMD";
73+
hPipe = CreateNamedPipeA(
74+
pipe.c_str(), // pipe name
75+
PIPE_ACCESS_DUPLEX, // read/write access
76+
PIPE_TYPE_MESSAGE | // message type pipe
77+
PIPE_READMODE_MESSAGE | // message-read mode
78+
PIPE_WAIT, // blocking mode
79+
PIPE_UNLIMITED_INSTANCES, // max. instances
80+
4096, // output buffer size
81+
4096, // input buffer size
82+
0, // client time-out
83+
NULL); // default security attribute
84+
if (hPipe == INVALID_HANDLE_VALUE) {
85+
CloseHandle(hPipeInCMD_);
86+
hPipeInCMD_ = NULL;
87+
return false;
88+
}
89+
hPipeOutCMD_ = hPipe;
90+
91+
// 2.创建进程
92+
std::string commandLine = CmdEmulatorFullPath;
93+
commandLine += " ";
94+
commandLine += randPipeName;
95+
commandLine += " \"";
96+
commandLine += title;
97+
commandLine += "\"";
98+
PROCESS_INFORMATION pi = { 0 };
99+
STARTUPINFOA si = { sizeof(si) };
100+
if (CreateProcessA(
101+
NULL,
102+
(LPSTR)commandLine.c_str(), //在Unicode版本中此参数不能为常量字符串,因为此参数会被修改
103+
NULL,
104+
NULL,
105+
FALSE,
106+
CREATE_NEW_CONSOLE,
107+
NULL,
108+
NULL,
109+
&si,
110+
&pi))
111+
{
112+
if (hJob_) {
113+
AssignProcessToJobObject(hJob_, pi.hProcess);
114+
}
115+
CloseHandle(pi.hThread);
116+
//CloseHandle(pi.hProcess);
117+
hCmdProcess_ = pi.hProcess;
118+
std::thread t([](func_failed fail, HANDLE*h) {
119+
WaitForSingleObject(*h, INFINITE);
120+
if (*h == NULL)
121+
return;
122+
fail();
123+
}, fail, &hCmdProcess_);
124+
t.detach();
125+
}
126+
else {
127+
CloseHandle(hPipeInCMD_);
128+
hPipeInCMD_ = NULL;
129+
CloseHandle(hPipeOutCMD_);
130+
hPipeOutCMD_ = NULL;
131+
return false;
132+
}
133+
134+
BOOL fConnected = ConnectNamedPipe(hPipeInCMD_, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
135+
if (!fConnected) {
136+
CloseHandle(hPipeInCMD_);
137+
hPipeInCMD_ = NULL;
138+
CloseHandle(hPipeOutCMD_);
139+
hPipeOutCMD_ = NULL;
140+
return false;
141+
}
142+
143+
fConnected = ConnectNamedPipe(hPipeOutCMD_, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
144+
if (!fConnected) {
145+
CloseHandle(hPipeInCMD_);
146+
hPipeInCMD_ = NULL;
147+
CloseHandle(hPipeOutCMD_);
148+
hPipeOutCMD_ = NULL;
149+
return false;
150+
}
151+
152+
// 3. 创建读管道线程
153+
HANDLE hTmp = hPipeOutCMD_;
154+
std::thread th([hTmp](func_callback cb) {
155+
while (true) {
156+
char szReadBuffer[1024] = { 0 };
157+
DWORD cbBytesRead;
158+
BOOL fSuccess = ReadFile(
159+
hTmp, // handle to pipe
160+
szReadBuffer, // buffer to receive data
161+
1024, // size of buffer
162+
&cbBytesRead, // number of bytes read
163+
NULL); // not overlapped I/O
164+
if (!fSuccess && GetLastError() == 0x218) {
165+
Sleep(1000);
166+
continue;
167+
}
168+
if (!fSuccess || cbBytesRead == 0) {
169+
DisconnectNamedPipe(hTmp);
170+
break;
171+
}
172+
cb(szReadBuffer, cbBytesRead);
173+
}
174+
}, cb);
175+
th.detach();
176+
return true;
177+
}
178+
179+
// 处理远端发过来的数据
180+
void recv(const char *buf, int len) {
181+
auto u = common::string_utils::utf8_to_unicode(std::string(buf, len));
182+
auto x = common::string_utils::unicode_to_mutibyte(u, GetOEMCP());
183+
DWORD cbWritten = 0;
184+
BOOL fSuccess = WriteFile(
185+
hPipeInCMD_, // pipe handle
186+
x.c_str(), // message
187+
x.length(), // message length
188+
&cbWritten, // bytes written
189+
NULL); // not overlapped
190+
if (!fSuccess || cbWritten != cbWritten) {
191+
// If failed, what should I do???
192+
}
193+
}
194+
private:
195+
std::string GetGuidString() {
196+
UUID uuid = { 0 };
197+
std::string guid;
198+
199+
// Create uuid or load from a string by UuidFromString() function
200+
::UuidCreate(&uuid);
201+
202+
// If you want to convert uuid to string, use UuidToString() function
203+
RPC_CSTR szUuid = NULL;
204+
if (::UuidToStringA(&uuid, &szUuid) == RPC_S_OK)
205+
{
206+
guid = (char*)szUuid;
207+
::RpcStringFreeA(&szUuid);
208+
}
209+
return guid;
210+
}
211+
212+
HANDLE hJob_;
213+
HANDLE hPipeInCMD_;
214+
HANDLE hPipeOutCMD_;
215+
HANDLE hCmdProcess_;
216+
func_callback cb_;
217+
};

client/client.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include <cstdlib>
2+
#include <string>
3+
#include <iostream>
4+
#include <boost/asio.hpp>
5+
#include <locale.h>
6+
#include <conio.h>
7+
#include <functional>
8+
#include "TerminalClient.h"
9+
10+
#define CMD_EMULATOR_EXE "CmdEmulator.exe"
11+
12+
using boost::asio::ip::tcp;
13+
enum { max_length = 1024 };
14+
15+
16+
int main(int argc, char* argv[])
17+
{
18+
char reply[max_length] = { 0 };
19+
try
20+
{
21+
boost::asio::io_context io_context;
22+
tcp::socket socket_(io_context);
23+
tcp::resolver resolver(io_context);
24+
25+
TerminalClient tc;
26+
tc.init(CMD_EMULATOR_EXE, "console default title",
27+
[&](void* buf, int len) {boost::asio::write(socket_, boost::asio::buffer(buf, len)); },
28+
[] {printf("failed.\r\n"); });
29+
30+
std::function<void(void)> read_some_data = [&]() {
31+
ZeroMemory(reply, max_length);
32+
socket_.async_read_some(boost::asio::buffer(reply, max_length), [&](const boost::system::error_code ec, std::size_t length) {
33+
if (!ec) {
34+
tc.recv(reply, length);
35+
read_some_data();
36+
}
37+
});
38+
};
39+
40+
boost::asio::connect(socket_, resolver.resolve("127.0.0.1", "5000"));
41+
read_some_data();
42+
43+
std::thread t([&io_context]() { io_context.run(); });
44+
t.join();
45+
}
46+
catch (std::exception& e)
47+
{
48+
std::cerr << "Exception: " << e.what() << "\n";
49+
}
50+
51+
return 0;
52+
}

client/client.sln

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 14
4+
VisualStudioVersion = 14.0.25420.1
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "client", "client.vcxproj", "{3051C6FC-79AF-4F98-8175-AEABE0690EF0}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|x86 = Debug|x86
11+
Release|x86 = Release|x86
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{3051C6FC-79AF-4F98-8175-AEABE0690EF0}.Debug|x86.ActiveCfg = Debug|Win32
15+
{3051C6FC-79AF-4F98-8175-AEABE0690EF0}.Debug|x86.Build.0 = Debug|Win32
16+
{3051C6FC-79AF-4F98-8175-AEABE0690EF0}.Release|x86.ActiveCfg = Release|Win32
17+
{3051C6FC-79AF-4F98-8175-AEABE0690EF0}.Release|x86.Build.0 = Release|Win32
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
EndGlobal

client/client.vcxproj

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup Label="ProjectConfigurations">
4+
<ProjectConfiguration Include="Debug|Win32">
5+
<Configuration>Debug</Configuration>
6+
<Platform>Win32</Platform>
7+
</ProjectConfiguration>
8+
<ProjectConfiguration Include="Release|Win32">
9+
<Configuration>Release</Configuration>
10+
<Platform>Win32</Platform>
11+
</ProjectConfiguration>
12+
</ItemGroup>
13+
<PropertyGroup Label="Globals">
14+
<ProjectGuid>{3051C6FC-79AF-4F98-8175-AEABE0690EF0}</ProjectGuid>
15+
<Keyword>Win32Proj</Keyword>
16+
<RootNamespace>server</RootNamespace>
17+
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
18+
<ProjectName>client</ProjectName>
19+
</PropertyGroup>
20+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
21+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
22+
<ConfigurationType>Application</ConfigurationType>
23+
<UseDebugLibraries>true</UseDebugLibraries>
24+
<PlatformToolset>v140_xp</PlatformToolset>
25+
<CharacterSet>Unicode</CharacterSet>
26+
</PropertyGroup>
27+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
28+
<ConfigurationType>Application</ConfigurationType>
29+
<UseDebugLibraries>false</UseDebugLibraries>
30+
<PlatformToolset>v140_xp</PlatformToolset>
31+
<WholeProgramOptimization>true</WholeProgramOptimization>
32+
<CharacterSet>Unicode</CharacterSet>
33+
</PropertyGroup>
34+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
35+
<ImportGroup Label="ExtensionSettings">
36+
</ImportGroup>
37+
<ImportGroup Label="Shared">
38+
</ImportGroup>
39+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
40+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
41+
</ImportGroup>
42+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
43+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
44+
</ImportGroup>
45+
<PropertyGroup Label="UserMacros" />
46+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
47+
<LinkIncremental>true</LinkIncremental>
48+
</PropertyGroup>
49+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
50+
<LinkIncremental>false</LinkIncremental>
51+
</PropertyGroup>
52+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
53+
<ClCompile>
54+
<PrecompiledHeader>
55+
</PrecompiledHeader>
56+
<WarningLevel>Level3</WarningLevel>
57+
<Optimization>Disabled</Optimization>
58+
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
59+
<AdditionalIncludeDirectories>../../../../_common_header_only;D:\boost_1_68_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
60+
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
61+
</ClCompile>
62+
<Link>
63+
<SubSystem>Console</SubSystem>
64+
<GenerateDebugInformation>true</GenerateDebugInformation>
65+
<AdditionalLibraryDirectories>D:\boost_1_68_0\lib32-msvc-14.0;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
66+
</Link>
67+
</ItemDefinitionGroup>
68+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
69+
<ClCompile>
70+
<WarningLevel>Level3</WarningLevel>
71+
<PrecompiledHeader>
72+
</PrecompiledHeader>
73+
<Optimization>MaxSpeed</Optimization>
74+
<FunctionLevelLinking>true</FunctionLevelLinking>
75+
<IntrinsicFunctions>true</IntrinsicFunctions>
76+
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
77+
<AdditionalIncludeDirectories>D:\boost_1_68_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
78+
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
79+
</ClCompile>
80+
<Link>
81+
<SubSystem>Console</SubSystem>
82+
<EnableCOMDATFolding>true</EnableCOMDATFolding>
83+
<OptimizeReferences>true</OptimizeReferences>
84+
<GenerateDebugInformation>true</GenerateDebugInformation>
85+
<AdditionalLibraryDirectories>D:\boost_1_68_0\lib32-msvc-14.0;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
86+
</Link>
87+
</ItemDefinitionGroup>
88+
<ItemGroup>
89+
<ClCompile Include="client.cpp" />
90+
</ItemGroup>
91+
<ItemGroup>
92+
<ClInclude Include="TerminalClient.h" />
93+
</ItemGroup>
94+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
95+
<ImportGroup Label="ExtensionTargets">
96+
</ImportGroup>
97+
</Project>

0 commit comments

Comments
 (0)