给桌面共享增加代理功能

This commit is contained in:
snltty
2024-04-13 17:06:26 +08:00
parent 6ddcfcfb23
commit 0f9c06e524
77 changed files with 3337 additions and 1671 deletions

View File

@@ -28,17 +28,17 @@
1. 这是一个粗略的局域网监控程序(说是局域网,你放外网也不是不行) 1. 这是一个粗略的局域网监控程序(说是局域网,你放外网也不是不行)
2. 使用组件式,非常方便扩展,可由 **内存共享(MemoryMappedFiles)** 提供自己定义数据 2. 使用组件式,非常方便扩展,可由 **内存共享(MemoryMappedFiles)** 提供自己定义数据
3. 内存占用小非定时自动GClinux无解 3. 内存占用小非定时自动GClinux无解
4. 使用 **MemoryPack**、**SharpDX**、**NAudio** 4. 使用 **MemoryPack**、**SharpDX**、**NAudio**、**RdpSession+RdpViewer**
1. 5.
<p><img src="./readme/size.jpg"></p> <p><img src="./readme/size.jpg"></p>
## 功能 ## 功能
###### 系统 ###### 系统
- [x] 桌面捕获,捕获鼠标,**sharpDX** - [x] 桌面捕获,捕获鼠标,**screen**
- [x] 登录界面捕获和登录页面键盘输入 - [x] 登录界面捕获和登录页面键盘输入
- [x] 双指画面缩放 - [x] 双指画面缩放
- [ ] 区域热更新 - [ ] 区域热更新
- [x] 功能禁用,禁用各种系统功能 **regedit** **第一次成功连接服务端后自动恢复限制** - [x] 功能禁用,禁用各种系统功能 **system**
- 任务栏锁定,任务栏设置,任务栏菜单 - 任务栏锁定,任务栏设置,任务栏菜单
- 任务管理器 - 任务管理器
- 注册表编辑,组策略编辑 - 注册表编辑,组策略编辑
@@ -49,35 +49,36 @@
- 关机按钮,注销按钮,锁定按钮,修改密码,切换用户 - 关机按钮,注销按钮,锁定按钮,修改密码,切换用户
- 安全SAS - 安全SAS
- 禁用U盘 - 禁用U盘
- [x] 系统信息展示CPU内存利用率硬盘使用率发呆时间 **win api** - [x] 系统信息展示CPU内存利用率硬盘使用率发呆时间 **system**
- [x] 音量控制,音量和静音 **NAudio** - [x] 音量控制,音量和静音 **volume**
- [x] 音频峰值,展示音频峰值,是否在播放音视频,及激昂程度 **NAudio** - [x] 音频峰值,展示音频峰值,是否在播放音视频,及激昂程度 **volume**
- [x] 系统亮度,暂不支持外界显示器 **WMI** - [x] 系统亮度,暂不支持外界显示器 **light**
- [x] 模拟键盘键盘操作模拟ctrl+alt+delete模拟win+l等等 **win api** - [x] 模拟键盘键盘操作模拟ctrl+alt+delete模拟win+l等等 **keyboard**
- [ ] 模拟鼠标,鼠标操作 **win api** - [ ] 模拟鼠标,鼠标操作 **volume**
###### 程序 ###### 程序
- [x] 程序限制,分为禁止打开程序,和自定检测关闭程序 **regedit** **第一次成功连接服务端后自动恢复限制** - [x] 程序限制,分为禁止打开程序,和自定检测关闭程序 **active**
- [x] 前景窗口,当前焦点程序捕获,手动关闭之 **win api** - [x] 前景窗口,当前焦点程序捕获,手动关闭之 **active**
- [x] 时间统计,查看程序使用时间记录 - [x] 时间统计,查看程序使用时间记录 **active**
###### 网络 ###### 网络
- [x] 网络限制程序域名IP 黑白名单 **第一次成功连接服务端后恢复限制** - [x] 网络限制程序域名IP 黑白名单 **hijack**
- [x] 网速显示,由网络限制组件提供 - [x] 网速显示,由网络限制组件提供 **hijack**
- [x] 自动连接wifi **wlan**
###### 消息 ###### 消息
- [x] 消息提醒,向设备发送消息提醒 **winform** - [x] 消息提醒,向设备发送消息提醒 **message**
- [x] 全局广播,向所有设备发送广播 **winform** - [x] 全局广播,向所有设备发送广播 **notify**
- [x] 语音消息,向设备发送语音消息 **NAudio** - [x] 语音消息,向设备发送语音消息 **message**
###### 命令 ###### 命令
- [x] 发送命令执行cmd命令等等 - [x] 发送命令执行cmd命令等等 **command**
###### 互动 ###### 互动
- [x] 互动答题 **winform** - [x] 互动答题 **snatch**
- [x] 屏幕共享,以某一设备为主机,向其它设备共享屏幕,用于演示 **RDPSession+RDPViewer** **第一次成功连接服务端后自动恢复** - [x] 屏幕共享,以某一设备为主机,向其它设备共享屏幕,用于演示 **viewer**第一次成功连接服务端后自动恢复**
###### 壁纸 ###### 壁纸
- [x] 壁纸程序,为所有设备设置统一壁纸,以程序的方式 **winform** **第一次成功连接服务端后自动恢复** - [x] 壁纸程序,为所有设备设置统一壁纸,以程序的方式 **wallpaper**
- [x] 键盘按键,显示键盘按键(当前键盘按键由壁纸程序提供) **win api** - [x] 键盘按键,显示键盘按键(当前键盘按键由壁纸程序提供) **wallpaper**
###### 锁屏 ###### 锁屏
- [x] 锁屏程序,打开锁屏程序,禁用键盘 **winform** **第一次成功连接服务端后自动恢复** - [x] 锁屏程序,打开锁屏程序,禁用键盘 **llock**
###### 其它 ###### 其它
- [x] 设备用户,显示当前使用设备用户姓名 **MemoryMappedFiles** - [x] 设备用户,显示当前使用设备用户姓名 **devices**
@@ -86,8 +87,7 @@
## 运行参数 ## 运行参数
``` ```
第一次运行【cmonitor.exe】后,在 configs/ 文件夹下,会生成配置文件,可以根据需要进行修改,然后再次运行 第一次运行后,在 configs/ 文件夹下,会生成配置文件,可以根据需要进行修改,然后再次运行
``` ```
## 安装示例 ## 安装示例
@@ -129,7 +129,10 @@ systemctl restart cmonitor
docker镜像 snltty/cmonitor-alpine-x64 or snltty/cmonitor-alpine-arm64 docker镜像 snltty/cmonitor-alpine-x64 or snltty/cmonitor-alpine-arm64
``` ```
docker run -it -d --name="cmonitor" \ docker run -it -d --name="cmonitor" \
-p 1800:1800/tcp -p 1801:1801/tcp -p 1802:1802/tcp -p 1802:1802/udp \ -p 1800:1800/tcp -p 1800:1800/udp \
-p 1801:1801/tcp -p 1801:1801/udp \
-p 1802:1802/tcp -p 1802:1802/udp \
-p 1802:1803/tcp -p 1802:1803/udp \
-v /usr/local/cmonitor/configs:/app/configs \ -v /usr/local/cmonitor/configs:/app/configs \
snltty/cmonitor-alpine-x64 snltty/cmonitor-alpine-x64
``` ```

View File

@@ -126,12 +126,12 @@ namespace cmonitor.install.win
} }
} }
public sealed class ConfigCommonInfo public sealed partial class ConfigCommonInfo
{ {
public string[] Modes { get; set; } = new string[] { "client", "server" }; public string[] Modes { get; set; } = new string[] { "client", "server" };
public bool BlueProtect { get; set; } public bool BlueProtect { get; set; }
} }
public sealed class ConfigClientInfo public sealed partial class ConfigClientInfo
{ {
private string server = new IPEndPoint(IPAddress.Loopback, 1802).ToString(); private string server = new IPEndPoint(IPAddress.Loopback, 1802).ToString();
public string Server public string Server
@@ -165,7 +165,7 @@ namespace cmonitor.install.win
public int ShareMemorySize { get; set; } = 1024; public int ShareMemorySize { get; set; } = 1024;
} }
public sealed class ConfigServerInfo public sealed partial class ConfigServerInfo
{ {
public int WebPort { get; set; } = 1800; public int WebPort { get; set; } = 1800;
public string WebRoot { get; set; } = "./web/"; public string WebRoot { get; set; } = "./web/";
@@ -174,3 +174,25 @@ namespace cmonitor.install.win
} }
} }
namespace cmonitor.install.win
{
public sealed class ViewerConfigInfo
{
[JsonIgnore]
public const string userNameKey = "viewer-username";
public int ProxyPort { get; set; } = 1803;
}
public sealed partial class ConfigClientInfo
{
public ViewerConfigInfo Viewer { get; set; } = new ViewerConfigInfo();
}
public sealed partial class ConfigServerInfo
{
public ViewerConfigInfo Viewer { get; set; } = new ViewerConfigInfo();
}
}

View File

@@ -53,6 +53,10 @@
gbClient = new GroupBox(); gbClient = new GroupBox();
gbServer = new GroupBox(); gbServer = new GroupBox();
cbBlueProtect = new CheckBox(); cbBlueProtect = new CheckBox();
tbViewerPortClient = new TextBox();
label7 = new Label();
tbViewerPortServer = new TextBox();
label8 = new Label();
gbClient.SuspendLayout(); gbClient.SuspendLayout();
gbServer.SuspendLayout(); gbServer.SuspendLayout();
SuspendLayout(); SuspendLayout();
@@ -173,6 +177,8 @@
// gbClient // gbClient
// //
resources.ApplyResources(gbClient, "gbClient"); resources.ApplyResources(gbClient, "gbClient");
gbClient.Controls.Add(tbViewerPortClient);
gbClient.Controls.Add(label7);
gbClient.Controls.Add(machineName); gbClient.Controls.Add(machineName);
gbClient.Controls.Add(label16); gbClient.Controls.Add(label16);
gbClient.Controls.Add(serverIP); gbClient.Controls.Add(serverIP);
@@ -189,6 +195,8 @@
// gbServer // gbServer
// //
resources.ApplyResources(gbServer, "gbServer"); resources.ApplyResources(gbServer, "gbServer");
gbServer.Controls.Add(tbViewerPortServer);
gbServer.Controls.Add(label8);
gbServer.Controls.Add(apiPort); gbServer.Controls.Add(apiPort);
gbServer.Controls.Add(serverPort); gbServer.Controls.Add(serverPort);
gbServer.Controls.Add(label2); gbServer.Controls.Add(label2);
@@ -204,6 +212,26 @@
cbBlueProtect.Name = "cbBlueProtect"; cbBlueProtect.Name = "cbBlueProtect";
cbBlueProtect.UseVisualStyleBackColor = true; cbBlueProtect.UseVisualStyleBackColor = true;
// //
// tbViewerPortClient
//
resources.ApplyResources(tbViewerPortClient, "tbViewerPortClient");
tbViewerPortClient.Name = "tbViewerPortClient";
//
// label7
//
resources.ApplyResources(label7, "label7");
label7.Name = "label7";
//
// tbViewerPortServer
//
resources.ApplyResources(tbViewerPortServer, "tbViewerPortServer");
tbViewerPortServer.Name = "tbViewerPortServer";
//
// label8
//
resources.ApplyResources(label8, "label8");
label8.Name = "label8";
//
// MainForm // MainForm
// //
resources.ApplyResources(this, "$this"); resources.ApplyResources(this, "$this");
@@ -253,5 +281,16 @@
private GroupBox gbClient; private GroupBox gbClient;
private GroupBox gbServer; private GroupBox gbServer;
private CheckBox cbBlueProtect; private CheckBox cbBlueProtect;
/* 项目“cmonitor.install.win (net8.0-windows)”的未合并的更改
在此之前:
private TextBox textBox1;
在此之后:
private TextBox tbViewerPortCLient;
*/
private TextBox tbViewerPortClient;
private Label label7;
private TextBox tbViewerPortServer;
private Label label8;
} }
} }

View File

@@ -38,6 +38,7 @@ namespace cmonitor.install.win
machineName.Text = config.Client.Name; machineName.Text = config.Client.Name;
serverIP.Text = config.Client.Server; serverIP.Text = config.Client.Server;
tbViewerPortClient.Text = config.Client.Viewer.ProxyPort.ToString();
shareKey.Text = config.Client.ShareMemoryKey; shareKey.Text = config.Client.ShareMemoryKey;
shareLen.Text = config.Client.ShareMemoryCount.ToString(); shareLen.Text = config.Client.ShareMemoryCount.ToString();
@@ -46,6 +47,7 @@ namespace cmonitor.install.win
serverPort.Text = config.Server.ServicePort.ToString(); serverPort.Text = config.Server.ServicePort.ToString();
apiPort.Text = config.Server.ApiPort.ToString(); apiPort.Text = config.Server.ApiPort.ToString();
webPort.Text = config.Server.WebPort.ToString(); webPort.Text = config.Server.WebPort.ToString();
tbViewerPortServer.Text = config.Server.Viewer.ProxyPort.ToString();
} }
private bool loading = false; private bool loading = false;
@@ -162,10 +164,20 @@ namespace cmonitor.install.win
MessageBox.Show("web端口必填"); MessageBox.Show("web端口必填");
return false; return false;
} }
if (string.IsNullOrWhiteSpace(tbViewerPortClient.Text) || string.IsNullOrWhiteSpace(tbViewerPortServer.Text))
{
MessageBox.Show("共享桌面代理端口必填");
return false;
}
config.Client.Server = serverIP.Text; config.Client.Server = serverIP.Text;
config.Server.WebPort = int.Parse(webPort.Text); config.Server.WebPort = int.Parse(webPort.Text);
config.Server.ApiPort = int.Parse(apiPort.Text); config.Server.ApiPort = int.Parse(apiPort.Text);
config.Server.ServicePort = int.Parse(serverPort.Text); config.Server.ServicePort = int.Parse(serverPort.Text);
config.Client.Viewer.ProxyPort = int.Parse(tbViewerPortClient.Text);
config.Server.Viewer.ProxyPort = int.Parse(tbViewerPortServer.Text);
return true; return true;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -131,7 +131,7 @@
<value>服务端地址</value> <value>服务端地址</value>
</data> </data>
<data name="label2.Location" type="System.Drawing.Point, System.Drawing"> <data name="label2.Location" type="System.Drawing.Point, System.Drawing">
<value>17, 85</value> <value>17, 80</value>
</data> </data>
<data name="label2.Size" type="System.Drawing.Size, System.Drawing"> <data name="label2.Size" type="System.Drawing.Size, System.Drawing">
<value>56, 17</value> <value>56, 17</value>
@@ -140,7 +140,7 @@
<value>服务端口</value> <value>服务端口</value>
</data> </data>
<data name="serverPort.Location" type="System.Drawing.Point, System.Drawing"> <data name="serverPort.Location" type="System.Drawing.Point, System.Drawing">
<value>79, 82</value> <value>79, 77</value>
</data> </data>
<data name="label3.Location" type="System.Drawing.Point, System.Drawing"> <data name="label3.Location" type="System.Drawing.Point, System.Drawing">
<value>17, 28</value> <value>17, 28</value>
@@ -149,7 +149,7 @@
<value>79, 24</value> <value>79, 24</value>
</data> </data>
<data name="label4.Location" type="System.Drawing.Point, System.Drawing"> <data name="label4.Location" type="System.Drawing.Point, System.Drawing">
<value>17, 56</value> <value>17, 54</value>
</data> </data>
<data name="label4.Size" type="System.Drawing.Size, System.Drawing"> <data name="label4.Size" type="System.Drawing.Size, System.Drawing">
<value>56, 17</value> <value>56, 17</value>
@@ -158,7 +158,7 @@
<value>管理端口</value> <value>管理端口</value>
</data> </data>
<data name="apiPort.Location" type="System.Drawing.Point, System.Drawing"> <data name="apiPort.Location" type="System.Drawing.Point, System.Drawing">
<value>79, 53</value> <value>79, 51</value>
</data> </data>
<data name="modeClient.Location" type="System.Drawing.Point, System.Drawing"> <data name="modeClient.Location" type="System.Drawing.Point, System.Drawing">
<value>105, 45</value> <value>105, 45</value>
@@ -167,16 +167,16 @@
<value>298, 45</value> <value>298, 45</value>
</data> </data>
<data name="label5.Location" type="System.Drawing.Point, System.Drawing"> <data name="label5.Location" type="System.Drawing.Point, System.Drawing">
<value>19, 130</value> <value>19, 150</value>
</data> </data>
<data name="shareLen.Location" type="System.Drawing.Point, System.Drawing"> <data name="shareLen.Location" type="System.Drawing.Point, System.Drawing">
<value>93, 127</value> <value>93, 147</value>
</data> </data>
<data name="label6.Location" type="System.Drawing.Point, System.Drawing"> <data name="label6.Location" type="System.Drawing.Point, System.Drawing">
<value>14, 104</value> <value>14, 124</value>
</data> </data>
<data name="shareKey.Location" type="System.Drawing.Point, System.Drawing"> <data name="shareKey.Location" type="System.Drawing.Point, System.Drawing">
<value>93, 100</value> <value>93, 120</value>
</data> </data>
<data name="label11.Location" type="System.Drawing.Point, System.Drawing"> <data name="label11.Location" type="System.Drawing.Point, System.Drawing">
<value>26, 29</value> <value>26, 29</value>
@@ -185,25 +185,25 @@
<value>93, 25</value> <value>93, 25</value>
</data> </data>
<data name="installBtn.Location" type="System.Drawing.Point, System.Drawing"> <data name="installBtn.Location" type="System.Drawing.Point, System.Drawing">
<value>175, 268</value> <value>175, 299</value>
</data> </data>
<data name="runBtn.Location" type="System.Drawing.Point, System.Drawing"> <data name="runBtn.Location" type="System.Drawing.Point, System.Drawing">
<value>88, 268</value> <value>88, 299</value>
</data> </data>
<data name="checkStateBtn.Location" type="System.Drawing.Point, System.Drawing"> <data name="checkStateBtn.Location" type="System.Drawing.Point, System.Drawing">
<value>305, 274</value> <value>305, 305</value>
</data> </data>
<data name="label16.Location" type="System.Drawing.Point, System.Drawing"> <data name="label16.Location" type="System.Drawing.Point, System.Drawing">
<value>7, 159</value> <value>7, 179</value>
</data> </data>
<data name="shareItemLen.Location" type="System.Drawing.Point, System.Drawing"> <data name="shareItemLen.Location" type="System.Drawing.Point, System.Drawing">
<value>93, 156</value> <value>93, 176</value>
</data> </data>
<data name="gbClient.Location" type="System.Drawing.Point, System.Drawing"> <data name="gbClient.Location" type="System.Drawing.Point, System.Drawing">
<value>12, 72</value> <value>12, 72</value>
</data> </data>
<data name="gbClient.Size" type="System.Drawing.Size, System.Drawing"> <data name="gbClient.Size" type="System.Drawing.Size, System.Drawing">
<value>200, 190</value> <value>200, 212</value>
</data> </data>
<data name="gbClient.Text" xml:space="preserve"> <data name="gbClient.Text" xml:space="preserve">
<value>客户端参数</value> <value>客户端参数</value>
@@ -212,7 +212,7 @@
<value>219, 73</value> <value>219, 73</value>
</data> </data>
<data name="gbServer.Size" type="System.Drawing.Size, System.Drawing"> <data name="gbServer.Size" type="System.Drawing.Size, System.Drawing">
<value>187, 189</value> <value>187, 211</value>
</data> </data>
<data name="gbServer.Text" xml:space="preserve"> <data name="gbServer.Text" xml:space="preserve">
<value>服务端参数</value> <value>服务端参数</value>
@@ -221,20 +221,63 @@
<data name="cbBlueProtect.AutoSize" type="System.Boolean, mscorlib"> <data name="cbBlueProtect.AutoSize" type="System.Boolean, mscorlib">
<value>True</value> <value>True</value>
</data> </data>
<data name="cbBlueProtect.Location" type="System.Drawing.Point, System.Drawing">
<value>174, 12</value>
</data>
<data name="cbBlueProtect.Size" type="System.Drawing.Size, System.Drawing"> <data name="cbBlueProtect.Size" type="System.Drawing.Size, System.Drawing">
<value>111, 21</value> <value>111, 21</value>
</data> </data>
<data name="cbBlueProtect.TabIndex" type="System.Int32, mscorlib">
<value>39</value>
</data>
<data name="cbBlueProtect.Text" xml:space="preserve"> <data name="cbBlueProtect.Text" xml:space="preserve">
<value>异常关闭则蓝屏</value> <value>异常关闭则蓝屏</value>
</data> </data>
<data name="tbViewerPortClient.Location" type="System.Drawing.Point, System.Drawing">
<value>93, 80</value>
</data>
<data name="tbViewerPortClient.Size" type="System.Drawing.Size, System.Drawing">
<value>100, 23</value>
</data>
<data name="tbViewerPortClient.TabIndex" type="System.Int32, mscorlib">
<value>37</value>
</data>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="label7.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms">
<value>NoControl</value>
</data>
<data name="label7.Location" type="System.Drawing.Point, System.Drawing">
<value>11, 83</value>
</data>
<data name="label7.Size" type="System.Drawing.Size, System.Drawing">
<value>80, 17</value>
</data>
<data name="label7.TabIndex" type="System.Int32, mscorlib">
<value>38</value>
</data>
<data name="label7.Text" xml:space="preserve">
<value>共享桌面端口</value>
</data>
<data name="tbViewerPortServer.Location" type="System.Drawing.Point, System.Drawing">
<value>79, 104</value>
</data>
<data name="tbViewerPortServer.Size" type="System.Drawing.Size, System.Drawing">
<value>100, 23</value>
</data>
<data name="tbViewerPortServer.TabIndex" type="System.Int32, mscorlib">
<value>39</value>
</data>
<data name="label8.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms">
<value>NoControl</value>
</data>
<data name="label8.Location" type="System.Drawing.Point, System.Drawing">
<value>-3, 107</value>
</data>
<data name="label8.Size" type="System.Drawing.Size, System.Drawing">
<value>80, 17</value>
</data>
<data name="label8.TabIndex" type="System.Int32, mscorlib">
<value>40</value>
</data>
<data name="label8.Text" xml:space="preserve">
<value>共享桌面端口</value>
</data>
<data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing"> <data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
<value>418, 315</value> <value>418, 346</value>
</data> </data>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<project ver="10" name="cmonitor.viewer.client.win" libEmbed="true" icon="D:\desktop\cmonitor\cmonitor\favicon.ico" ui="win" output="cmonitor.viewer.client.win.exe" CompanyName="snltty" FileDescription="cmonitor.viewer.client.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="cmonitor.viewer.client.win" InternalName="cmonitor.viewer.client.win" FileVersion="0.0.0.29" ProductVersion="0.0.0.29" publishDir="/dist/" dstrip="false"> <project ver="10" name="cmonitor.viewer.client.win" libEmbed="true" icon="D:\desktop\cmonitor\cmonitor\favicon.ico" ui="win" output="cmonitor.viewer.client.win.exe" CompanyName="snltty" FileDescription="cmonitor.viewer.client.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="cmonitor.viewer.client.win" InternalName="cmonitor.viewer.client.win" FileVersion="0.0.0.31" ProductVersion="0.0.0.31" publishDir="/dist/" dstrip="false">
<file name="main.aardio" path="main.aardio" comment="main.aardio"/> <file name="main.aardio" path="main.aardio" comment="main.aardio"/>
<folder name="资源文件" path="res" embed="true"/> <folder name="资源文件" path="res" embed="true"/>
<folder name="窗体文件" path="dlg" comment="目录" embed="true"/> <folder name="窗体文件" path="dlg" comment="目录" embed="true"/>

View File

@@ -37,7 +37,7 @@ mainForm.connect = function(){
var invitationString = reg.queryValue("viewerConnectStr"); var invitationString = reg.queryValue("viewerConnectStr");
if(string.len(invitationString)>0) if(string.len(invitationString)>0)
{ {
mainForm.tsc.Connect(invitationString,"snltty","snltty"); mainForm.tsc.Connect(invitationString,_ARGV[1],_ARGV[1]);
}else{ }else{
mainForm.connecting = false; mainForm.connecting = false;
} }

View File

@@ -3,11 +3,8 @@ using cmonitor.viewer.server.win.Properties;
using common.libs; using common.libs;
using Microsoft.Win32; using Microsoft.Win32;
using RDPCOMAPILib; using RDPCOMAPILib;
using System.Data.SqlTypes;
using System.Diagnostics; using System.Diagnostics;
using System.Resources; using System.Net;
using System.Text;
using System.Windows.Forms;
using System.Xml; using System.Xml;
namespace cmonitor.viewer.server.win namespace cmonitor.viewer.server.win
@@ -29,18 +26,16 @@ namespace cmonitor.viewer.server.win
private readonly Hook hook = new Hook(); private readonly Hook hook = new Hook();
private readonly ShareMemory shareMemory; private readonly ShareMemory shareMemory;
private int shareIndex = 0;
private Mode shareMode = Mode.Client;
private const string shareClientExe = "cmonitor.viewer.client.win"; private const string shareClientExe = "cmonitor.viewer.client.win";
private byte[] shareKeyBytes = Encoding.UTF8.GetBytes(shareClientExe);
public MainForm(string key, int length, int size, int index, Mode mode) ParamInfo paramInfo;
public MainForm(ParamInfo paramInfo)
{ {
InitializeComponent(); this.paramInfo = paramInfo;
shareMode = mode; InitializeComponent();
shareIndex = index; shareMemory = new ShareMemory(paramInfo.ShareMkey, paramInfo.ShareMLength, paramInfo.ShareItemMLength);
shareMemory = new ShareMemory(key, length, size);
shareMemory.InitLocal(); shareMemory.InitLocal();
} }
@@ -51,12 +46,12 @@ namespace cmonitor.viewer.server.win
this.ShowInTaskbar = false; this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized; this.WindowState = FormWindowState.Minimized;
this.Visible = false; this.Visible = false;
FireWallHelper.Write(Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName));
#endif #endif
FireWallHelper.Write(Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName));
CheckRunning(); CheckRunning();
if (shareMode == Mode.Client) if (paramInfo.Mode == Mode.Client)
{ {
OpenShareClient(); OpenShareClient();
} }
@@ -68,25 +63,25 @@ namespace cmonitor.viewer.server.win
private void CheckRunning() private void CheckRunning()
{ {
hook.Close(); hook.Close();
shareMemory.AddAttribute(shareIndex, ShareMemoryAttribute.Running); shareMemory.AddAttribute(paramInfo.ShareIndex, ShareMemoryAttribute.Running);
shareMemory.RemoveAttribute(shareIndex, ShareMemoryAttribute.Closed); shareMemory.RemoveAttribute(paramInfo.ShareIndex, ShareMemoryAttribute.Closed);
Task.Run(async () => Task.Run(async () =>
{ {
while (true) while (true)
{ {
try try
{ {
if (shareMemory.ReadAttributeEqual(shareIndex, ShareMemoryAttribute.Closed)) if (shareMemory.ReadAttributeEqual(paramInfo.ShareIndex, ShareMemoryAttribute.Closed))
{ {
CloseServer(); CloseServer();
} }
else else
{ {
shareMemory.IncrementVersion(shareIndex); shareMemory.IncrementVersion(paramInfo.ShareIndex);
} }
if (Process.GetProcessesByName(shareClientExe).Length == 0) if (Process.GetProcessesByName(shareClientExe).Length == 0)
{ {
shareMemory.AddAttribute(shareIndex, ShareMemoryAttribute.Error); shareMemory.AddAttribute(paramInfo.ShareIndex, ShareMemoryAttribute.Error);
} }
} }
catch (Exception) catch (Exception)
@@ -99,7 +94,7 @@ namespace cmonitor.viewer.server.win
} }
private void CloseServer() private void CloseServer()
{ {
shareMemory.RemoveAttribute(shareIndex, ShareMemoryAttribute.Running); shareMemory.RemoveAttribute(paramInfo.ShareIndex, ShareMemoryAttribute.Running);
CloseShareClient(); CloseShareClient();
CloseShareDesktop(); CloseShareDesktop();
Application.ExitThread(); Application.ExitThread();
@@ -111,7 +106,7 @@ namespace cmonitor.viewer.server.win
{ {
hook.Start((code) => { return true; }); hook.Start((code) => { return true; });
CommandHelper.Windows(string.Empty, new string[] { $"start {shareClientExe}.exe" }, false); CommandHelper.Windows(string.Empty, new string[] { $"start {shareClientExe}.exe {paramInfo.GroupName}" }, false);
} }
private void CloseShareClient() private void CloseShareClient()
{ {
@@ -155,24 +150,43 @@ namespace cmonitor.viewer.server.win
{ {
try try
{ {
string guid = Guid.NewGuid().ToString();
CloseShareDesktop(); CloseShareDesktop();
session = new RDPSession(); session = new RDPSession();
session.SetDesktopSharedRect(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); session.SetDesktopSharedRect(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
session.OnAttendeeConnected += Session_OnAttendeeConnected; session.OnAttendeeConnected += Session_OnAttendeeConnected;
session.Open(); session.Open();
IRDPSRAPIInvitation invitation = session.Invitations.CreateInvitation(guid, "snltty", "snltty", 1024); IRDPSRAPIInvitation invitation = session.Invitations.CreateInvitation(null, paramInfo.GroupName, paramInfo.GroupName, 1024);
invitationString = invitation.ConnectionString; invitationString = invitation.ConnectionString;
/* if(string.IsNullOrWhiteSpace(paramInfo.ProxyServers) == false)
{
XmlDocument xmlDoc = new XmlDocument(); XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(invitationString); xmlDoc.LoadXml(invitationString);
//留给客户端自己替换为自己本地的代理地址
XmlElement newLNode = xmlDoc.CreateElement("L"); XmlElement newLNode = xmlDoc.CreateElement("L");
newLNode.SetAttribute("P", "12345"); newLNode.SetAttribute("P", "{port}");
newLNode.SetAttribute("N", "192.168.1.35"); newLNode.SetAttribute("N", "{ip}");
xmlDoc.DocumentElement["C"]["T"].AppendChild(newLNode); xmlDoc.DocumentElement["C"]["T"].AppendChild(newLNode);
Debug.WriteLine(xmlDoc.OuterXml);
*/ //插入其它代理地址
foreach (var item in paramInfo.ProxyServers.Split(','))
{
try
{
IPEndPoint ep = IPEndPoint.Parse(item);
XmlElement newLNode1 = xmlDoc.CreateElement("L");
newLNode1.SetAttribute("P", ep.Port.ToString());
newLNode1.SetAttribute("N", ep.Address.ToString());
xmlDoc.DocumentElement["C"]["T"].AppendChild(newLNode1);
}
catch (Exception)
{
}
}
invitationString = xmlDoc.OuterXml;
}
Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\Cmonitor", "viewerConnectStr", invitationString); Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\Cmonitor", "viewerConnectStr", invitationString);
@@ -182,14 +196,11 @@ namespace cmonitor.viewer.server.win
catch (Exception ex) catch (Exception ex)
{ {
Debug.WriteLine(ex + ""); Debug.WriteLine(ex + "");
//MessageBox.Show(ex.Message);
notifyIcon.Icon = Icon.FromHandle(Resources.logo_share_gray.GetHicon()); notifyIcon.Icon = Icon.FromHandle(Resources.logo_share_gray.GetHicon());
notifyIcon.Text = "共享失败"; notifyIcon.Text = "共享失败";
} }
}); });
} }
private void Session_OnAttendeeConnected(object pAttendee) private void Session_OnAttendeeConnected(object pAttendee)
{ {
IRDPSRAPIAttendee attendee = (IRDPSRAPIAttendee)pAttendee; IRDPSRAPIAttendee attendee = (IRDPSRAPIAttendee)pAttendee;
@@ -200,7 +211,7 @@ namespace cmonitor.viewer.server.win
try try
{ {
session?.Close(); session?.Close();
Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\Cmonitor", "viewerConnectStr", string.Empty); //Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\Cmonitor", "viewerConnectStr", string.Empty);
} }
catch (Exception) catch (Exception)
{ {

View File

@@ -1,3 +1,6 @@
using System.Net;
using System.Text.Json;
namespace cmonitor.viewer.server.win namespace cmonitor.viewer.server.win
{ {
internal static class Program internal static class Program
@@ -19,22 +22,26 @@ namespace cmonitor.viewer.server.win
MessageBox.Show(b.ExceptionObject.ToString()); MessageBox.Show(b.ExceptionObject.ToString());
}; };
string shareMkey = "cmonitor/share"; ParamInfo paramInfo = new ParamInfo();
int shareMLength = 10;
int shareItemMLength = 1024;
int shareIndex = 5;
Mode mode = Mode.Server;
if (arg.Length > 0) if (arg.Length > 0)
{ {
shareMkey = arg[0]; paramInfo = JsonSerializer.Deserialize<ParamInfo>(arg[0]);
shareMLength = int.Parse(arg[1]); }
shareItemMLength = int.Parse(arg[2]); ApplicationConfiguration.Initialize();
shareIndex = int.Parse(arg[3]); Application.Run(new MainForm(paramInfo));
mode = (Mode)byte.Parse(arg[4]);
} }
ApplicationConfiguration.Initialize();
Application.Run(new MainForm(shareMkey, shareMLength, shareItemMLength, shareIndex, mode));
} }
public sealed class ParamInfo
{
public string ShareMkey { get; set; } = "cmonitor/share";
public int ShareMLength { get; set; } = 10;
public int ShareItemMLength { get; set; } = 1024;
public int ShareIndex { get; set; } = 5;
public Mode Mode { get; set; } = Mode.Server;
public string GroupName { get; set; } = "snltty";
public string ProxyServers { get; set; } = "127.0.0.1:1803";
} }
} }

View File

@@ -46,10 +46,13 @@ export default {
ElMessage.error('请选择一个共享设备'); ElMessage.error('请选择一个共享设备');
return; return;
} }
const device = globalState.value.devices.filter(c=>c.MachineName == pluginState.value.viewer.device)
viewerUpdate({ viewerUpdate({
Open: true, Open: true,
Server: pluginState.value.viewer.device, Server: pluginState.value.viewer.device,
Clients: clients, Clients: clients,
ShareId: device.Viewer.id,
}).then(() => { }).then(() => {
pluginState.value.viewer.showShare = false; pluginState.value.viewer.showShare = false;
ElMessage.success('操作成功'); ElMessage.success('操作成功');

View File

@@ -31,7 +31,8 @@ export default {
const handleConfirm = () => { const handleConfirm = () => {
viewerUpdate({ viewerUpdate({
open: false, open: false,
server: props.data.MachineName server: props.data.MachineName,
shareid:props.data.Viewer.id
}).then(() => { }).then(() => {
ElMessage.success('已操作!') ElMessage.success('已操作!')
}).catch(() => { }).catch(() => {

View File

@@ -3,7 +3,8 @@ export default {
return { return {
Viewer: { Viewer: {
share: false, share: false,
mode: 'server' mode: 'server',
id: ''
} }
}; };
}, },
@@ -25,6 +26,7 @@ export default {
if (!report.Viewer) return; if (!report.Viewer) return;
item.Viewer.id = report.Viewer.ShareId;
item.Viewer.share = report.Viewer.Value; item.Viewer.share = report.Viewer.Value;
item.Viewer.mode = ['client', 'server'][report.Viewer.Mode]; item.Viewer.mode = ['client', 'server'][report.Viewer.Mode];
} }

View File

@@ -15,10 +15,10 @@ namespace cmonitor
//初始化配置文件 //初始化配置文件
Config config = new Config(); Config config = new Config();
config.Elevated = args.Any(c => c.Contains("elevated")); config.Data.Elevated = args.Any(c => c.Contains("elevated"));
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
StartupTransfer.Init(); StartupTransfer.Init(config);
//依赖注入 //依赖注入
ServiceProvider serviceProvider = null; ServiceProvider serviceProvider = null;
@@ -33,9 +33,6 @@ namespace cmonitor
serviceProvider = serviceCollection.BuildServiceProvider(); serviceProvider = serviceCollection.BuildServiceProvider();
StartupTransfer.Use(serviceProvider, config, assemblies); StartupTransfer.Use(serviceProvider, config, assemblies);
FireWallHelper.Write(Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName));
GCHelper.FlushMemory(); GCHelper.FlushMemory();
await Helper.Await(); await Helper.Await();
} }
@@ -66,6 +63,7 @@ namespace cmonitor
{ {
common.libs.winapis.Win32Interop.RelaunchElevated(); common.libs.winapis.Win32Interop.RelaunchElevated();
} }
FireWallHelper.Write(Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName));
#endif #endif
} }

View File

@@ -38,6 +38,10 @@ namespace cmonitor.api
Type voidType = typeof(void); Type voidType = typeof(void);
IEnumerable<Type> types = assemblys.SelectMany(c => c.GetTypes()).Where(c => c.GetInterfaces().Contains(typeof(IApiController))); IEnumerable<Type> types = assemblys.SelectMany(c => c.GetTypes()).Where(c => c.GetInterfaces().Contains(typeof(IApiController)));
if (config.Data.Common.PluginNames.Length > 0)
{
types = types.Where(c => config.Data.Common.PluginNames.Any(d => c.FullName.Contains(d)));
}
foreach (Type item in types) foreach (Type item in types)
{ {
@@ -76,7 +80,7 @@ namespace cmonitor.api
server = new WebSocketServer(); server = new WebSocketServer();
try try
{ {
server.Start(System.Net.IPAddress.Any, config.Server.ApiPort); server.Start(System.Net.IPAddress.Any, config.Data.Server.ApiPort);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -27,7 +27,7 @@ namespace cmonitor.api
IApiServer clientServer = serviceProvider.GetService<IApiServer>(); IApiServer clientServer = serviceProvider.GetService<IApiServer>();
clientServer.LoadPlugins(assemblies); clientServer.LoadPlugins(assemblies);
clientServer.Websocket(); clientServer.Websocket();
Logger.Instance.Info($"api listen:{config.Server.ApiPort}"); Logger.Instance.Info($"api listen:{config.Data.Server.ApiPort}");
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using cmonitor.config; using cmonitor.client.args;
using cmonitor.config;
using cmonitor.plugins.signIn.messenger; using cmonitor.plugins.signIn.messenger;
using cmonitor.server; using cmonitor.server;
using common.libs; using common.libs;
@@ -15,13 +16,15 @@ namespace cmonitor.client
private readonly Config config; private readonly Config config;
private readonly TcpServer tcpServer; private readonly TcpServer tcpServer;
private readonly MessengerSender messengerSender; private readonly MessengerSender messengerSender;
private readonly SignInArgsTransfer signInArgsTransfer;
public ClientSignInTransfer(ClientSignInState clientSignInState, Config config, TcpServer tcpServer, MessengerSender messengerSender) public ClientSignInTransfer(ClientSignInState clientSignInState, Config config, TcpServer tcpServer, MessengerSender messengerSender, SignInArgsTransfer signInArgsTransfer)
{ {
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
this.config = config; this.config = config;
this.tcpServer = tcpServer; this.tcpServer = tcpServer;
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
this.signInArgsTransfer = signInArgsTransfer;
SignInTask(); SignInTask();
tcpServer.OnDisconnected += (hashcode) => tcpServer.OnDisconnected += (hashcode) =>
@@ -57,45 +60,27 @@ namespace cmonitor.client
} }
private async Task SignIn() private async Task SignIn()
{ {
IPAddress[] ips = new IPAddress[] { config.Client.ServerEP.Address }; IPEndPoint[] ips = new IPEndPoint[] { config.Data.Client.ServerEP };
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Info($"get ip:{ips.ToJsonFormat()}"); Logger.Instance.Info($"get ip:{ips.ToJsonFormat()}");
if (ips.Length == 0) return; foreach (IPEndPoint ip in ips)
foreach (IPAddress ip in ips)
{ {
try try
{ {
IPEndPoint remote = new IPEndPoint(ip, config.Client.ServerEP.Port); if (await ConnectServer(ip) == false)
//Logger.Instance.Info($"connect server {remote}");
Socket socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.KeepAlive();
IAsyncResult result = socket.BeginConnect(remote, null, null);
await Task.Delay(500);
if (result.IsCompleted == false)
{ {
socket.SafeClose();
continue; continue;
} }
clientSignInState.Connection = tcpServer.BindReceive(socket); if (await SignIn2Server() == false)
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{ {
Connection = clientSignInState.Connection,
MessengerId = (ushort)SignInMessengerIds.SignIn,
Payload = MemoryPackSerializer.Serialize(new SignInfo
{
MachineName = config.Client.Name,
Version = config.Version
})
});
if (resp.Code != MessageResponeCodes.OK || resp.Data.Span.SequenceEqual(Helper.TrueArray) == false)
{
clientSignInState.Connection?.Disponse();
continue; continue;
} }
GCHelper.FlushMemory(); GCHelper.FlushMemory();
clientSignInState.PushNetworkEnabled(); clientSignInState.PushNetworkEnabled();
break; break;
} }
catch (Exception ex) catch (Exception ex)
@@ -106,5 +91,42 @@ namespace cmonitor.client
} }
} }
private async Task<bool> ConnectServer(IPEndPoint remote)
{
Socket socket = new Socket(remote.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.KeepAlive();
IAsyncResult result = socket.BeginConnect(remote, null, null);
await Task.Delay(500);
if (result.IsCompleted == false)
{
socket.SafeClose();
return false;
}
clientSignInState.Connection = tcpServer.BindReceive(socket);
return true;
}
private async Task<bool> SignIn2Server()
{
Dictionary<string, string> args = new Dictionary<string, string>();
signInArgsTransfer.Invoke(args);
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)SignInMessengerIds.SignIn,
Payload = MemoryPackSerializer.Serialize(new SignInfo
{
MachineName = config.Data.Client.Name,
Version = config.Data.Version,
Args = args,
})
});
if (resp.Code != MessageResponeCodes.OK || resp.Data.Span.SequenceEqual(Helper.TrueArray) == false)
{
clientSignInState.Connection?.Disponse();
return false;
}
return true;
}
} }
} }

View File

@@ -1,12 +1,12 @@
using cmonitor.client.runningConfig; using cmonitor.client.report;
using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using cmonitor.libs; using cmonitor.libs;
using cmonitor.startup; using cmonitor.startup;
using common.libs; using common.libs;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Reflection; using System.Reflection;
using cmonitor.server.ruleConfig; using cmonitor.client.args;
using cmonitor.client.running;
namespace cmonitor.client namespace cmonitor.client
{ {
@@ -14,30 +14,24 @@ namespace cmonitor.client
{ {
public void AddClient(ServiceCollection serviceCollection, Config config, Assembly[] assemblies) public void AddClient(ServiceCollection serviceCollection, Config config, Assembly[] assemblies)
{ {
serviceCollection.AddSingleton<RunningConfig>();
serviceCollection.AddSingleton<SignInArgsTransfer>();
serviceCollection.AddSingleton<ClientReportTransfer>(); serviceCollection.AddSingleton<ClientReportTransfer>();
serviceCollection.AddSingleton<ClientSignInState>(); serviceCollection.AddSingleton<ClientSignInState>();
serviceCollection.AddSingleton<ClientSignInTransfer>(); serviceCollection.AddSingleton<ClientSignInTransfer>();
if (OperatingSystem.IsWindows()) serviceCollection.AddSingleton<IRunningConfig, RunningConfigWindows>();
else if (OperatingSystem.IsLinux()) serviceCollection.AddSingleton<IRunningConfig, RunningConfigLinux>();
else if (OperatingSystem.IsMacOS()) serviceCollection.AddSingleton<IRunningConfig, RunningConfigMacOS>();
//内存共享 //内存共享
ShareMemory shareMemory = new ShareMemory(config.Client.ShareMemoryKey, config.Client.ShareMemoryCount, config.Client.ShareMemorySize); ShareMemory shareMemory = new ShareMemory(config.Data.Client.ShareMemoryKey, config.Data.Client.ShareMemoryCount, config.Data.Client.ShareMemorySize);
serviceCollection.AddSingleton<ShareMemory>((a) => shareMemory); serviceCollection.AddSingleton<ShareMemory>((a) => shareMemory);
} }
public void AddServer(ServiceCollection serviceCollection, Config config, Assembly[] assemblies)
{
}
public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies) public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{ {
Logger.Instance.Info($"start client"); Logger.Instance.Info($"start client");
Logger.Instance.Info($"server ip {config.Client.ServerEP}"); Logger.Instance.Info($"server ip {config.Data.Client.ServerEP}");
Logger.Instance.Info($"start client report transfer"); Logger.Instance.Info($"start client report transfer");
ClientReportTransfer report = serviceProvider.GetService<ClientReportTransfer>(); ClientReportTransfer report = serviceProvider.GetService<ClientReportTransfer>();
@@ -53,6 +47,11 @@ namespace cmonitor.client
ClientSignInTransfer clientTransfer = serviceProvider.GetService<ClientSignInTransfer>(); ClientSignInTransfer clientTransfer = serviceProvider.GetService<ClientSignInTransfer>();
} }
public void AddServer(ServiceCollection serviceCollection, Config config, Assembly[] assemblies)
{
}
public void UseServer(ServiceProvider serviceProvider, Config config, Assembly[] assemblies) public void UseServer(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{ {
} }

View File

@@ -0,0 +1,7 @@
namespace cmonitor.client.args
{
public interface ISignInArgs
{
public void Invoke(Dictionary<string, string> args);
}
}

View File

@@ -0,0 +1,30 @@
using cmonitor.config;
using common.libs;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace cmonitor.client.args
{
public sealed class SignInArgsTransfer
{
private List<ISignInArgs> startups;
public SignInArgsTransfer(ServiceProvider serviceProvider, Config config)
{
var types = ReflectionHelper.GetInterfaceSchieves(typeof(ISignInArgs));
if (config.Data.Common.PluginNames.Length > 0)
{
types = types.Where(c => config.Data.Common.PluginNames.Any(d => c.FullName.Contains(d)));
}
startups = types.Select(c => serviceProvider.GetService(c) as ISignInArgs).Where(c=>c != null).ToList();
}
public void Invoke(Dictionary<string, string> args)
{
foreach (var item in startups)
{
item.Invoke(args);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using common.libs;
using common.libs.extends;
using System.Net;
using System.Text.Json.Serialization;
namespace cmonitor.config
{
public sealed partial class ConfigInfo
{
public ConfigClientInfo Client { get; set; } = new ConfigClientInfo();
}
public sealed partial class ConfigClientInfo
{
private string server = new IPEndPoint(IPAddress.Loopback, 1802).ToString();
public string Server
{
get => server; set
{
server = value;
if (string.IsNullOrWhiteSpace(server) == false)
{
string[] arr = server.Split(':');
int port = arr.Length == 2 ? int.Parse(arr[1]) : 1802;
IPAddress ip = NetworkHelper.GetDomainIp(arr[0]);
ServerEP = new IPEndPoint(ip, port);
}
}
}
[JsonIgnore]
public IPEndPoint ServerEP { get; set; } = new IPEndPoint(IPAddress.Loopback, 1802);
private string name = Dns.GetHostName().SubStr(0, 12);
public string Name
{
get => name; set
{
name = value.SubStr(0, 12);
}
}
public string ShareMemoryKey { get; set; } = "cmonitor/share";
public int ShareMemoryCount { get; set; } = 100;
public int ShareMemorySize { get; set; } = 1024;
}
}

View File

@@ -34,7 +34,11 @@ namespace cmonitor.client.report
public void LoadPlugins(Assembly[] assembs) public void LoadPlugins(Assembly[] assembs)
{ {
IEnumerable<Type> types = ReflectionHelper.GetInterfaceSchieves(assembs, typeof(IClientReport)); IEnumerable<Type> types = ReflectionHelper.GetInterfaceSchieves(assembs, typeof(IClientReport));
reports = types.Select(c => (IClientReport)serviceProvider.GetService(c)).Where(c => string.IsNullOrWhiteSpace(c.Name) == false).ToList(); if (config.Data.Common.PluginNames.Length > 0)
{
types = types.Where(c => config.Data.Common.PluginNames.Any(d => c.FullName.Contains(d)));
}
reports = types.Select(c => (IClientReport)serviceProvider.GetService(c)).Where(c => c != null).Where(c => string.IsNullOrWhiteSpace(c.Name) == false).ToList();
reportObj = new Dictionary<string, object>(reports.Count); reportObj = new Dictionary<string, object>(reports.Count);
Logger.Instance.Warning($"load reports:{string.Join(",", reports.Select(c => c.Name))}"); Logger.Instance.Warning($"load reports:{string.Join(",", reports.Select(c => c.Name))}");

View File

@@ -0,0 +1,104 @@
using common.libs;
using common.libs.extends;
using System.Net;
using System.Text.Json.Serialization;
namespace cmonitor.client.running
{
public sealed class RunningConfig
{
private FileStream fs = null;
private StreamWriter writer = null;
private StreamReader reader = null;
private SemaphoreSlim slim = new SemaphoreSlim(1);
private string configPath { get; } = "./configs/";
public RunningConfigInfo Data { get; private set; } = new RunningConfigInfo();
public RunningConfig()
{
if (Directory.Exists(configPath) == false)
{
Directory.CreateDirectory(configPath);
}
string path = Path.Join(configPath, "running.json");
fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
reader = new StreamReader(fs, System.Text.Encoding.UTF8);
writer = new StreamWriter(fs, System.Text.Encoding.UTF8);
Load();
Save();
SaveTask();
}
private void Load()
{
slim.Wait();
try
{
fs.Seek(0, SeekOrigin.Begin);
string text = reader.ReadToEnd();
if (string.IsNullOrWhiteSpace(text))
{
return;
}
Data = text.DeJson<RunningConfigInfo>();
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
finally
{
slim.Release();
}
}
private void SaveTask()
{
Task.Run(async () =>
{
while (true)
{
uint updated = Data.Updated;
while (updated > 0)
{
Save();
updated--;
}
await Task.Delay(1000);
}
});
}
private void Save()
{
slim.Wait();
try
{
fs.Seek(0, SeekOrigin.Begin);
fs.SetLength(0);
writer.Write(Data.ToJsonFormat());
writer.Flush();
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
finally
{
slim.Release();
}
}
}
public sealed partial class RunningConfigInfo
{
[JsonIgnore]
public uint Updated { get; set; } = 1;
public void Update()
{
Updated++;
}
}
}

View File

@@ -1,8 +0,0 @@
namespace cmonitor.client.runningConfig
{
public interface IRunningConfig
{
public T Get<T>(T defaultValue);
public void Set<T>(T data);
}
}

View File

@@ -1,15 +0,0 @@
namespace cmonitor.client.runningConfig
{
public sealed class RunningConfigLinux : IRunningConfig
{
public T Get<T>(T defaultValue)
{
return defaultValue;
}
public void Set<T>(T data)
{
}
}
}

View File

@@ -1,15 +0,0 @@
namespace cmonitor.client.runningConfig
{
public sealed class RunningConfigMacOS : IRunningConfig
{
public T Get<T>(T defaultValue)
{
return defaultValue;
}
public void Set<T>(T data)
{
}
}
}

View File

@@ -1,75 +0,0 @@
using common.libs;
using common.libs.database;
using common.libs.extends;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Versioning;
namespace cmonitor.client.runningConfig
{
[SupportedOSPlatform("windows")]
public sealed class RunningConfigWindows : IRunningConfig
{
private readonly IConfigDataProvider<RunningConfigInfo> configDataProvider;
private readonly RunningConfigInfo runningConfigInfo;
private Dictionary<string, object> cache = new Dictionary<string, object>();
public RunningConfigWindows(IConfigDataProvider<RunningConfigInfo> configDataProvider)
{
this.configDataProvider = configDataProvider;
runningConfigInfo = configDataProvider.Load().Result ?? new RunningConfigInfo();
}
public T Get<T>(T defaultValue)
{
try
{
string name = typeof(T).Name;
if (cache.TryGetValue(name, out object cacheValue))
{
return (T)cacheValue;
}
if (runningConfigInfo.Running.TryGetValue(name, out string value))
{
if (string.IsNullOrWhiteSpace(value) == false)
{
T data = value.DeJson<T>();
cache[name] = data;
return data;
}
}
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
return defaultValue;
}
public void Set<T>(T data)
{
try
{
string name = typeof(T).Name;
string value = data.ToJson();
runningConfigInfo.Running[name] = value;
cache[name] = data;
configDataProvider.Save(runningConfigInfo).Wait();
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
}
}
[Table("running")]
public sealed class RunningConfigInfo
{
public Dictionary<string, string> Running { get; set; } = new Dictionary<string, string>();
}
}

View File

@@ -1,9 +0,0 @@
namespace cmonitor.client.trigger
{
public interface ITrigger
{
public string Name { get; }
public string Desc { get; }
public void Execute();
}
}

View File

@@ -7,62 +7,30 @@ namespace cmonitor.config
{ {
public sealed class Config public sealed class Config
{ {
FileStream fs = null; private FileStream fs = null;
StreamWriter writer = null; private StreamWriter writer = null;
StreamReader reader = null; private StreamReader reader = null;
SemaphoreSlim slim = new SemaphoreSlim(1); private SemaphoreSlim slim = new SemaphoreSlim(1);
private string configPath = "./configs/";
public ConfigInfo Data { get; private set; } = new ConfigInfo();
public Config() public Config()
{ {
if (Directory.Exists(ConfigPath) == false) if (Directory.Exists(configPath) == false)
{ {
Directory.CreateDirectory(ConfigPath); Directory.CreateDirectory(configPath);
} }
string path = Path.Join(ConfigPath, "config.json"); string path = Path.Join(configPath, "config.json");
fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
reader = new StreamReader(fs, System.Text.Encoding.UTF8); reader = new StreamReader(fs, System.Text.Encoding.UTF8);
writer = new StreamWriter(fs, System.Text.Encoding.UTF8); writer = new StreamWriter(fs, System.Text.Encoding.UTF8);
Load(); Load();
}
public ConfigCommonInfo Common { get; set; } = new ConfigCommonInfo();
public ConfigClientInfo Client { get; set; } = new ConfigClientInfo();
public ConfigServerInfo Server { get; set; } = new ConfigServerInfo();
public string ConfigPath { get; } = "./configs/";
private Dictionary<string, Dictionary<string, object>> JsonDic = new Dictionary<string, Dictionary<string, object>>();
public T Get<T>(T defaultValue)
{
return Get<T>(typeof(T).Name, defaultValue);
}
public T Get<T>(string name, T defaultValue)
{
if (JsonDic.ContainsKey(name))
{
return JsonDic[name].ToJson().DeJson<T>();
}
return defaultValue;
}
public void Set<T>(T value)
{
Set<T>(typeof(T).Name, value);
}
public void Set<T>(string name, T value)
{
JsonDic[name] = value.ToJson().DeJson<Dictionary<string, object>>();
Save(); Save();
} }
private void Load() private void Load()
{
InitFileConfig();
ReadJson();
Logger.Instance.Debug($"config:{JsonDic.ToJson()}");
Save();
}
private void InitFileConfig()
{ {
slim.Wait(); slim.Wait();
try try
@@ -73,7 +41,7 @@ namespace cmonitor.config
{ {
return; return;
} }
JsonDic = text.DeJson<Dictionary<string, Dictionary<string, object>>>(); Data = text.DeJson<ConfigInfo>();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -83,36 +51,17 @@ namespace cmonitor.config
{ {
slim.Release(); slim.Release();
} }
}
private void ReadJson()
{
if (JsonDic.TryGetValue("Client", out Dictionary<string, object> elClient))
{
Client = elClient.ToJson().DeJson<ConfigClientInfo>();
} }
if (JsonDic.TryGetValue("Server", out Dictionary<string, object> elServer)) private void Save()
{
Server = elServer.ToJson().DeJson<ConfigServerInfo>();
}
if (JsonDic.TryGetValue("Common", out Dictionary<string, object> elCommon))
{
Common = elCommon.ToJson().DeJson<ConfigCommonInfo>();
}
}
public void Save()
{ {
slim.Wait(); slim.Wait();
try try
{ {
JsonDic["Client"] = Client.ToJson().DeJson<Dictionary<string, object>>();
JsonDic["Server"] = Server.ToJson().DeJson<Dictionary<string, object>>();
JsonDic["Common"] = Common.ToJson().DeJson<Dictionary<string, object>>();
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
fs.SetLength(0); fs.SetLength(0);
writer.Write(JsonDic.ToJsonFormat()); writer.Write(Data.ToJsonFormat());
writer.Flush(); writer.Flush();
} }
catch (Exception ex) catch (Exception ex)
@@ -125,59 +74,32 @@ namespace cmonitor.config
} }
} }
}
public sealed partial class ConfigInfo
{
public ConfigCommonInfo Common { get; set; } = new ConfigCommonInfo();
[JsonIgnore] [JsonIgnore]
public string Version { get; set; } = "1.0.0.1"; public string Version { get; set; } = "1.0.0.1";
[JsonIgnore] [JsonIgnore]
public bool Elevated { get; set; } public bool Elevated { get; set; }
} }
public sealed class ConfigCommonInfo public sealed partial class ConfigCommonInfo
{ {
public string[] Modes { get; set; } = new string[] { "client", "server" }; public string[] Modes { get; set; } = new string[] { "client", "server" };
public bool BlueProtect { get; set; }
}
public sealed class ConfigClientInfo
{
private string server = new IPEndPoint(IPAddress.Loopback, 1802).ToString();
public string Server
{
get => server; set
{
server = value;
if (string.IsNullOrWhiteSpace(server) == false)
{
string[] arr = server.Split(':');
int port = arr.Length == 2 ? int.Parse(arr[1]) : 1802;
IPAddress ip = NetworkHelper.GetDomainIp(arr[0]);
ServerEP = new IPEndPoint(ip, port);
}
}
}
public string[] plugins = Array.Empty<string>();
public string[] Plugins
{
get => plugins; set
{
PluginNames = value.Select(c => $"plugins.{c}.").ToArray();
}
}
[JsonIgnore] [JsonIgnore]
public IPEndPoint ServerEP { get; set; } = new IPEndPoint(IPAddress.Loopback, 1802); public string[] PluginNames = Array.Empty<string>();
private string name = Dns.GetHostName().SubStr(0, 12);
public string Name
{
get => name; set
{
name = value.SubStr(0, 12);
} }
}
public string ShareMemoryKey { get; set; } = "cmonitor/share";
public int ShareMemoryCount { get; set; } = 100;
public int ShareMemorySize { get; set; } = 1024;
}
public sealed class ConfigServerInfo
{
public int WebPort { get; set; } = 1800;
public string WebRoot { get; set; } = "./web/";
public int ApiPort { get; set; } = 1801;
public int ServicePort { get; set; } = 1802;
}
} }

View File

@@ -1,5 +1,4 @@
using cmonitor.client; using cmonitor.client;
using cmonitor.client.runningConfig;
using cmonitor.client.report; using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using common.libs; using common.libs;
@@ -7,6 +6,8 @@ using MemoryPack;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Diagnostics; using System.Diagnostics;
using cmonitor.client.running;
using cmonitor.plugins.active.report;
namespace cmonitor.plugins.active.report namespace cmonitor.plugins.active.report
{ {
@@ -14,22 +15,20 @@ namespace cmonitor.plugins.active.report
{ {
public string Name => "ActiveWindow"; public string Name => "ActiveWindow";
private readonly IRunningConfig clientConfig; private readonly RunningConfig runningConfig;
private readonly IActiveWindow activeWindow; private readonly IActiveWindow activeWindow;
private readonly ActiveWindowTimeManager activeWindowTimeManager = new ActiveWindowTimeManager(); private readonly ActiveWindowTimeManager activeWindowTimeManager = new ActiveWindowTimeManager();
private ActiveReportInfo report = new ActiveReportInfo(); private ActiveReportInfo report = new ActiveReportInfo();
private ActiveDisallowInfo activeConfig;
public ActiveWindowReport(Config config, IRunningConfig clientConfig, IActiveWindow activeWindow, ClientSignInState clientSignInState) public ActiveWindowReport(Config config, RunningConfig runningConfig, IActiveWindow activeWindow, ClientSignInState clientSignInState)
{ {
this.clientConfig = clientConfig; this.runningConfig = runningConfig;
this.activeWindow = activeWindow; this.activeWindow = activeWindow;
DisallowRun(Array.Empty<string>()); DisallowRun(Array.Empty<string>());
activeConfig = clientConfig.Get(new ActiveDisallowInfo { });
clientSignInState.NetworkFirstEnabledHandle += () => clientSignInState.NetworkFirstEnabledHandle += () =>
{ {
DisallowRun(activeConfig.FileNames); DisallowRun(runningConfig.Data.Active.FileNames);
Loop(); Loop();
}; };
} }
@@ -38,8 +37,8 @@ namespace cmonitor.plugins.active.report
public object GetReports(ReportType reportType) public object GetReports(ReportType reportType)
{ {
ticks = DateTime.UtcNow.Ticks; ticks = DateTime.UtcNow.Ticks;
report.Ids1 = activeConfig.Ids1; report.Ids1 = runningConfig.Data.Active.Ids1;
report.Ids2 = activeConfig.Ids2; report.Ids2 = runningConfig.Data.Active.Ids2;
if (reportType == ReportType.Full || report.Updated()) if (reportType == ReportType.Full || report.Updated())
{ {
return report; return report;
@@ -50,8 +49,7 @@ namespace cmonitor.plugins.active.report
public void DisallowRun(ActiveDisallowInfo activeDisallowInfo) public void DisallowRun(ActiveDisallowInfo activeDisallowInfo)
{ {
activeConfig = activeDisallowInfo; runningConfig.Data.Active = activeDisallowInfo;
clientConfig.Set(activeConfig);
report.DisallowCount = activeDisallowInfo.FileNames.Length; report.DisallowCount = activeDisallowInfo.FileNames.Length;
activeWindow.DisallowRun(activeDisallowInfo.FileNames); activeWindow.DisallowRun(activeDisallowInfo.FileNames);
} }
@@ -131,13 +129,13 @@ namespace cmonitor.plugins.active.report
private bool Disallow(ActiveWindowInfo window) private bool Disallow(ActiveWindowInfo window)
{ {
if (activeConfig.FileNames.Length > 0) if (runningConfig.Data.Active.FileNames.Length > 0)
{ {
try try
{ {
ReadOnlySpan<char> filenameSpan = window.FileName.AsSpan(); ReadOnlySpan<char> filenameSpan = window.FileName.AsSpan();
uint pid = window.Pid; uint pid = window.Pid;
foreach (string item in activeConfig.FileNames) foreach (string item in runningConfig.Data.Active.FileNames)
{ {
ReadOnlySpan<char> nameSpan = item.AsSpan(); ReadOnlySpan<char> nameSpan = item.AsSpan();
bool result = item == window.Title bool result = item == window.Title
@@ -257,6 +255,7 @@ namespace cmonitor.plugins.active.report
public DateTime StartTime { get; set; } = DateTime.Now; public DateTime StartTime { get; set; } = DateTime.Now;
public List<ActiveWindowTimeInfo> List { get; set; } = new List<ActiveWindowTimeInfo>(); public List<ActiveWindowTimeInfo> List { get; set; } = new List<ActiveWindowTimeInfo>();
} }
[MemoryPackable] [MemoryPackable]
public sealed partial class ActiveWindowTimeInfo public sealed partial class ActiveWindowTimeInfo
{ {
@@ -267,3 +266,19 @@ namespace cmonitor.plugins.active.report
public Dictionary<string, uint> Titles { get; set; } public Dictionary<string, uint> Titles { get; set; }
} }
} }
namespace cmonitor.client.running
{
public sealed partial class RunningConfigInfo
{
private ActiveDisallowInfo active = new ActiveDisallowInfo();
public ActiveDisallowInfo Active
{
get => active; set
{
Updated++;
active = value;
}
}
}
}

View File

@@ -212,10 +212,12 @@ namespace cmonitor.plugins.active.report
private void InitDriver() private void InitDriver()
{ {
AppDomain.CurrentDomain.ProcessExit += (sender, e) => { AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
{
CommandHelper.Windows(string.Empty, new string[] { "sc stop cmonitor.killer & sc delete cmonitor.killer" }, true); CommandHelper.Windows(string.Empty, new string[] { "sc stop cmonitor.killer & sc delete cmonitor.killer" }, true);
}; };
Console.CancelKeyPress += (sender, e) => { Console.CancelKeyPress += (sender, e) =>
{
CommandHelper.Windows(string.Empty, new string[] { "sc stop cmonitor.killer & sc delete cmonitor.killer" }, true); CommandHelper.Windows(string.Empty, new string[] { "sc stop cmonitor.killer & sc delete cmonitor.killer" }, true);
}; };
Task.Run(() => Task.Run(() =>
@@ -228,7 +230,15 @@ namespace cmonitor.plugins.active.report
Logger.Instance.Info($"killer starting"); Logger.Instance.Info($"killer starting");
string sourcePath = Path.GetFullPath(Path.Join("./", "killer.sys")); string sourcePath = Path.GetFullPath(Path.Join("./", "killer.sys"));
string distPath = Path.GetFullPath(Path.Join("./", "killer-temp.sys")); string distPath = $"{Environment.SystemDirectory}\\drivers\\killer.sys";
try
{
File.Delete("killer-temp.sys");
}
catch (Exception)
{
}
try try
{ {
File.Copy(sourcePath, distPath, true); File.Copy(sourcePath, distPath, true);

View File

@@ -11,7 +11,6 @@ namespace cmonitor.plugins.hijack
{ {
public void AddClient(ServiceCollection serviceCollection, Config config, Assembly[] assemblies) public void AddClient(ServiceCollection serviceCollection, Config config, Assembly[] assemblies)
{ {
serviceCollection.AddSingleton<HijackConfig>();
serviceCollection.AddSingleton<HijackReport>(); serviceCollection.AddSingleton<HijackReport>();
if (OperatingSystem.IsWindows()) serviceCollection.AddSingleton<IHijack, HijackWindows>(); if (OperatingSystem.IsWindows()) serviceCollection.AddSingleton<IHijack, HijackWindows>();
else if (OperatingSystem.IsLinux()) serviceCollection.AddSingleton<IHijack, HijackLinux>(); else if (OperatingSystem.IsLinux()) serviceCollection.AddSingleton<IHijack, HijackLinux>();

View File

@@ -1,4 +1,6 @@
namespace cmonitor.plugins.hijack.report using cmonitor.plugins.hijack.report;
namespace cmonitor.plugins.hijack.report
{ {
public sealed class HijackConfig public sealed class HijackConfig
{ {
@@ -42,3 +44,19 @@
} }
} }
namespace cmonitor.client.running
{
public sealed partial class RunningConfigInfo
{
private HijackConfig hijack = new HijackConfig();
public HijackConfig Hijack
{
get => hijack; set
{
Updated++;
hijack = value;
}
}
}
}

View File

@@ -1,9 +1,9 @@
using cmonitor.client; using cmonitor.client;
using cmonitor.client.runningConfig;
using cmonitor.client.report; using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using common.libs; using common.libs;
using MemoryPack; using MemoryPack;
using cmonitor.client.running;
namespace cmonitor.plugins.hijack.report namespace cmonitor.plugins.hijack.report
{ {
@@ -11,19 +11,17 @@ namespace cmonitor.plugins.hijack.report
{ {
public string Name => "Hijack"; public string Name => "Hijack";
private readonly HijackConfig hijackConfig; private readonly RunningConfig runningConfig;
private readonly IRunningConfig clientConfig;
private readonly IHijack hijack; private readonly IHijack hijack;
private readonly ClientSignInState clientSignInState; private readonly ClientSignInState clientSignInState;
private HijackReportInfo hijackReportInfo = new HijackReportInfo(); private HijackReportInfo hijackReportInfo = new HijackReportInfo();
private long ticks = DateTime.UtcNow.Ticks; private long ticks = DateTime.UtcNow.Ticks;
public HijackReport(IHijack hijack, HijackConfig hijackConfig, IRunningConfig clientConfig, Config config, ClientSignInState clientSignInState) public HijackReport(IHijack hijack, RunningConfig runningConfig, Config config, ClientSignInState clientSignInState)
{ {
this.hijack = hijack; this.hijack = hijack;
this.hijackConfig = hijackConfig; this.runningConfig = runningConfig;
this.clientConfig = clientConfig;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
Init(); Init();
} }
@@ -33,18 +31,6 @@ namespace cmonitor.plugins.hijack.report
try try
{ {
clientSignInState.NetworkFirstEnabledHandle += hijack.Start; clientSignInState.NetworkFirstEnabledHandle += hijack.Start;
HijackConfig config = clientConfig.Get(new HijackConfig { });
hijackConfig.DeniedProcesss = config.DeniedProcesss;
hijackConfig.AllowProcesss = config.AllowProcesss;
hijackConfig.DeniedDomains = config.DeniedDomains;
hijackConfig.AllowDomains = config.AllowDomains;
hijackConfig.DeniedIPs = config.DeniedIPs;
hijackConfig.AllowIPs = config.AllowIPs;
hijackConfig.DomainKill = config.DomainKill;
hijackConfig.HijackIds1 = config.HijackIds1;
hijackConfig.HijackIds2 = config.HijackIds2;
UpdateRules(); UpdateRules();
} }
catch (Exception ex) catch (Exception ex)
@@ -55,26 +41,26 @@ namespace cmonitor.plugins.hijack.report
public void Update(HijackSetRuleInfo info) public void Update(HijackSetRuleInfo info)
{ {
hijackConfig.AllowDomains = info.Rules.AllowDomains; runningConfig.Data.Hijack.AllowDomains = info.Rules.AllowDomains;
hijackConfig.DeniedDomains = info.Rules.DeniedDomains; runningConfig.Data.Hijack.DeniedDomains = info.Rules.DeniedDomains;
hijackConfig.AllowProcesss = info.Rules.AllowProcesss; runningConfig.Data.Hijack.AllowProcesss = info.Rules.AllowProcesss;
hijackConfig.DeniedProcesss = info.Rules.DeniedProcesss; runningConfig.Data.Hijack.DeniedProcesss = info.Rules.DeniedProcesss;
hijackConfig.AllowIPs = info.Rules.AllowIPs; runningConfig.Data.Hijack.AllowIPs = info.Rules.AllowIPs;
hijackConfig.DeniedIPs = info.Rules.DeniedIPs; runningConfig.Data.Hijack.DeniedIPs = info.Rules.DeniedIPs;
hijackConfig.DomainKill = info.Rules.DomainKill; runningConfig.Data.Hijack.DomainKill = info.Rules.DomainKill;
hijackConfig.HijackIds1 = info.Ids1; runningConfig.Data.Hijack.HijackIds1 = info.Ids1;
hijackConfig.HijackIds2 = info.Ids2; runningConfig.Data.Hijack.HijackIds2 = info.Ids2;
clientConfig.Set(hijackConfig); runningConfig.Data.Update();
UpdateRules(); UpdateRules();
} }
private void UpdateRules() private void UpdateRules()
{ {
hijack.SetProcess(hijackConfig.AllowProcesss, hijackConfig.DeniedProcesss); hijack.SetProcess(runningConfig.Data.Hijack.AllowProcesss, runningConfig.Data.Hijack.DeniedProcesss);
hijack.SetDomain(hijackConfig.AllowDomains, hijackConfig.DeniedDomains, hijackConfig.DomainKill); hijack.SetDomain(runningConfig.Data.Hijack.AllowDomains, runningConfig.Data.Hijack.DeniedDomains, runningConfig.Data.Hijack.DomainKill);
hijack.SetIP(hijackConfig.AllowIPs, hijackConfig.DeniedIPs); hijack.SetIP(runningConfig.Data.Hijack.AllowIPs, runningConfig.Data.Hijack.DeniedIPs);
hijack.UpdateRules(); hijack.UpdateRules();
} }
@@ -82,10 +68,10 @@ namespace cmonitor.plugins.hijack.report
{ {
hijackReportInfo.Upload = hijack.UdpSend + hijack.TcpSend; hijackReportInfo.Upload = hijack.UdpSend + hijack.TcpSend;
hijackReportInfo.Download = hijack.TcpReceive + hijack.UdpReceive; hijackReportInfo.Download = hijack.TcpReceive + hijack.UdpReceive;
hijackReportInfo.Count = (ulong)(hijackConfig.AllowIPs.Length + hijackConfig.DeniedIPs.Length + hijackConfig.AllowDomains.Length + hijackConfig.DeniedDomains.Length + hijackConfig.AllowProcesss.Length + hijackConfig.DeniedProcesss.Length); hijackReportInfo.Count = (ulong)(runningConfig.Data.Hijack.AllowIPs.Length + runningConfig.Data.Hijack.DeniedIPs.Length + runningConfig.Data.Hijack.AllowDomains.Length + runningConfig.Data.Hijack.DeniedDomains.Length + runningConfig.Data.Hijack.AllowProcesss.Length + runningConfig.Data.Hijack.DeniedProcesss.Length);
hijackReportInfo.Ids1 = hijackConfig.HijackIds1; hijackReportInfo.Ids1 = runningConfig.Data.Hijack.HijackIds1;
hijackReportInfo.Ids2 = hijackConfig.HijackIds2; hijackReportInfo.Ids2 = runningConfig.Data.Hijack.HijackIds2;
hijackReportInfo.DomainKill = hijackConfig.DomainKill; hijackReportInfo.DomainKill = runningConfig.Data.Hijack.DomainKill;
long _ticks = DateTime.UtcNow.Ticks; long _ticks = DateTime.UtcNow.Ticks;
if (((_ticks - ticks) / TimeSpan.TicksPerMillisecond >= 300 && hijackReportInfo.Updated()) || reportType == ReportType.Full) if (((_ticks - ticks) / TimeSpan.TicksPerMillisecond >= 300 && hijackReportInfo.Updated()) || reportType == ReportType.Full)
@@ -97,7 +83,7 @@ namespace cmonitor.plugins.hijack.report
} }
} }
public sealed class HijackReportInfo:ReportInfo public sealed class HijackReportInfo : ReportInfo
{ {
public ulong Upload { get; set; } public ulong Upload { get; set; }
public ulong Download { get; set; } public ulong Download { get; set; }

View File

@@ -77,7 +77,7 @@ namespace cmonitor.plugins.keyboard.report
if (inputActions.TryDequeue(out var action)) if (inputActions.TryDequeue(out var action))
{ {
if (config.Elevated == true && !Win32Interop.SwitchToInputDesktop()) if (config.Data.Elevated == true && !Win32Interop.SwitchToInputDesktop())
{ {
uint code = Kernel32.GetLastError(); uint code = Kernel32.GetLastError();
tks.Cancel(); tks.Cancel();

View File

@@ -1,9 +1,10 @@
using cmonitor.client; using cmonitor.client;
using cmonitor.client.runningConfig;
using cmonitor.client.report; using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using cmonitor.libs; using cmonitor.libs;
using common.libs; using common.libs;
using cmonitor.client.running;
using cmonitor.plugins.llock.report;
namespace cmonitor.plugins.llock.report namespace cmonitor.plugins.llock.report
{ {
@@ -13,21 +14,19 @@ namespace cmonitor.plugins.llock.report
private LLockReportInfo report = new LLockReportInfo(); private LLockReportInfo report = new LLockReportInfo();
private readonly IRunningConfig clientConfig; private readonly RunningConfig runningConfig;
private readonly ILLock lLock; private readonly ILLock lLock;
private readonly ShareMemory shareMemory; private readonly ShareMemory shareMemory;
private readonly LLockConfigInfo lLockConfigInfo = new LLockConfigInfo();
public LLockReport(Config config, IRunningConfig clientConfig, ILLock lLock, ShareMemory shareMemory, ClientSignInState clientSignInState) public LLockReport(Config config, RunningConfig runningConfig, ILLock lLock, ShareMemory shareMemory, ClientSignInState clientSignInState)
{ {
this.clientConfig = clientConfig; this.runningConfig = runningConfig;
this.lLock = lLock; this.lLock = lLock;
this.shareMemory = shareMemory; this.shareMemory = shareMemory;
lLockConfigInfo = clientConfig.Get(new LLockConfigInfo { });
clientSignInState.NetworkFirstEnabledHandle += () => clientSignInState.NetworkFirstEnabledHandle += () =>
{ {
LockScreen(lLockConfigInfo.Open); LockScreen(runningConfig.Data.LLock.Open);
WallpaperTask(); WallpaperTask();
}; };
} }
@@ -44,10 +43,10 @@ namespace cmonitor.plugins.llock.report
public void LockScreen(bool open) public void LockScreen(bool open)
{ {
lLockConfigInfo.Open = open; runningConfig.Data.LLock.Open = open;
runningConfig.Data.Update();
Task.Run(async () => Task.Run(async () =>
{ {
clientConfig.Set(lLockConfigInfo);
shareMemory.AddAttribute((int)ShareMemoryIndexs.LLock, ShareMemoryAttribute.Closed); shareMemory.AddAttribute((int)ShareMemoryIndexs.LLock, ShareMemoryAttribute.Closed);
await Task.Delay(100); await Task.Delay(100);
lLock.Set(open); lLock.Set(open);
@@ -72,11 +71,11 @@ namespace cmonitor.plugins.llock.report
{ {
while (true) while (true)
{ {
if (lLockConfigInfo.Open) if (runningConfig.Data.LLock.Open)
{ {
if (Running() == false) if (Running() == false)
{ {
LockScreen(lLockConfigInfo.Open); LockScreen(runningConfig.Data.LLock.Open);
} }
} }
await Task.Delay(5000); await Task.Delay(5000);
@@ -102,3 +101,21 @@ namespace cmonitor.plugins.llock.report
} }
} }
namespace cmonitor.client.running
{
public sealed partial class RunningConfigInfo
{
private LLockConfigInfo llock = new LLockConfigInfo();
public LLockConfigInfo LLock
{
get => llock; set
{
Updated++;
llock = value;
}
}
}
}

View File

@@ -17,7 +17,7 @@ namespace cmonitor.plugins.llock.report
if (value) if (value)
{ {
CommandHelper.Windows(string.Empty, new string[] { CommandHelper.Windows(string.Empty, new string[] {
$"start cmonitor.llock.win.exe {config.Client.ShareMemoryKey} {config.Client.ShareMemoryCount} {config.Client.ShareMemorySize} {(int)ShareMemoryIndexs.LLock}" $"start cmonitor.llock.win.exe {config.Data.Client.ShareMemoryKey} {config.Data.Client.ShareMemoryCount} {config.Data.Client.ShareMemorySize} {(int)ShareMemoryIndexs.LLock}"
},false); },false);
} }
} }

View File

@@ -1,6 +1,4 @@
using cmonitor.api; using cmonitor.api;
using cmonitor.plugins.signIn.messenger;
using cmonitor.server;
using cmonitor.server.ruleConfig; using cmonitor.server.ruleConfig;
using common.libs.extends; using common.libs.extends;

View File

@@ -63,7 +63,7 @@ namespace cmonitor.plugins.screen.messenger
{ {
if (signCaching.Get(connection.Name, out SignCacheInfo cache)) if (signCaching.Get(connection.Name, out SignCacheInfo cache))
{ {
if (cache.Version == config.Version) if (cache.Version == config.Data.Version)
{ {
clientServer.Notify("/notify/report/screen/full", connection.Name, connection.ReceiveRequestWrap.Payload); clientServer.Notify("/notify/report/screen/full", connection.Name, connection.ReceiveRequestWrap.Payload);
} }

View File

@@ -245,7 +245,7 @@ namespace cmonitor.plugins.screen.report
{ {
try try
{ {
if (config.Elevated) if (config.Data.Elevated)
{ {
Win32Interop.SwitchToInputDesktop(); Win32Interop.SwitchToInputDesktop();
} }
@@ -372,7 +372,7 @@ namespace cmonitor.plugins.screen.report
bool result = false; bool result = false;
try try
{ {
if (config.Elevated == true && !Win32Interop.SwitchToInputDesktop()) if (config.Data.Elevated == true && !Win32Interop.SwitchToInputDesktop())
{ {
var errCode = Marshal.GetLastWin32Error(); var errCode = Marshal.GetLastWin32Error();
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)

View File

@@ -38,7 +38,7 @@ namespace cmonitor.plugins.screen.report
{ {
try try
{ {
if (config.Elevated == true && !Win32Interop.SwitchToInputDesktop()) if (config.Data.Elevated == true && !Win32Interop.SwitchToInputDesktop())
{ {
var errCode = Marshal.GetLastWin32Error(); var errCode = Marshal.GetLastWin32Error();
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG) if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)

View File

@@ -31,7 +31,8 @@ namespace cmonitor.plugins.signIn.messenger
{ {
Connection = connection, Connection = connection,
MachineName = signInfo.MachineName, MachineName = signInfo.MachineName,
Version = signInfo.Version Version = signInfo.Version,
Args = signInfo.Args,
}; };
config.Clients.TryAdd(signInfo.MachineName, cache1); config.Clients.TryAdd(signInfo.MachineName, cache1);
changed = true; changed = true;
@@ -86,6 +87,8 @@ namespace cmonitor.plugins.signIn.messenger
public string MachineName { get; set; } public string MachineName { get; set; }
public string Version { get; set; } = "1.0.0.0"; public string Version { get; set; } = "1.0.0.0";
public Dictionary<string, string> Args { get; set; } = new Dictionary<string, string>();
[JsonIgnore] [JsonIgnore]
public int ReportFlag = 1; public int ReportFlag = 1;
[JsonIgnore] [JsonIgnore]
@@ -115,7 +118,6 @@ namespace cmonitor.plugins.signIn.messenger
ScreenTime = Environment.TickCount; ScreenTime = Environment.TickCount;
} }
public bool Connected public bool Connected
{ {
get get
@@ -130,7 +132,9 @@ namespace cmonitor.plugins.signIn.messenger
[MemoryPackable] [MemoryPackable]
public sealed partial class SignInfo public sealed partial class SignInfo
{ {
public string MachineName { get; set; } public string MachineName { get; set; } = string.Empty;
public string Version { get; set; } public string Version { get; set; } = string.Empty;
public Dictionary<string, string> Args { get; set; } = new Dictionary<string, string>();
} }
} }

View File

@@ -19,7 +19,7 @@ namespace cmonitor.plugins.signIn.messenger
public void SignIn(IConnection connection) public void SignIn(IConnection connection)
{ {
SignInfo info = MemoryPackSerializer.Deserialize<SignInfo>(connection.ReceiveRequestWrap.Payload.Span); SignInfo info = MemoryPackSerializer.Deserialize<SignInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (info.Version == config.Version) if (info.Version == config.Data.Version)
{ {
signCaching.Sign(connection, info); signCaching.Sign(connection, info);
connection.Write(Helper.TrueArray); connection.Write(Helper.TrueArray);

View File

@@ -15,7 +15,7 @@ namespace cmonitor.plugins.snatch.report
public void StartUp(SnatchQuestionInfo snatchQuestionInfo) public void StartUp(SnatchQuestionInfo snatchQuestionInfo)
{ {
CommandHelper.Windows(string.Empty, new string[] { CommandHelper.Windows(string.Empty, new string[] {
$"start cmonitor.snatch.win.exe {config.Client.ShareMemoryKey} {config.Client.ShareMemoryCount} {config.Client.ShareMemorySize} {(int)ShareMemoryIndexs.SnatchQuestion} {(int)ShareMemoryIndexs.SnatchAnswer}" $"start cmonitor.snatch.win.exe {config.Data.Client.ShareMemoryKey} {config.Data.Client.ShareMemoryCount} {config.Data.Client.ShareMemorySize} {(int)ShareMemoryIndexs.SnatchQuestion} {(int)ShareMemoryIndexs.SnatchAnswer}"
},false); },false);
} }
} }

View File

@@ -115,7 +115,7 @@ namespace cmonitor.plugins.system.report
{ {
while (actions.TryDequeue(out Action action)) while (actions.TryDequeue(out Action action))
{ {
if (config.Elevated) if (config.Data.Elevated)
{ {
Win32Interop.SwitchToInputDesktop(); Win32Interop.SwitchToInputDesktop();
} }

View File

@@ -0,0 +1,552 @@
<mxfile host="Electron" modified="2024-04-13T08:49:39.679Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.1.2 Chrome/106.0.5249.199 Electron/21.4.3 Safari/537.36" etag="N35__DeDEHlFxssWZWsV" version="21.1.2" type="device">
<diagram name="第 1 页" id="cBPwx1frQaiKcuuC_Qt-">
<mxGraphModel dx="6020" dy="3312" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="osB9R2kQAUtgP8P5AnCK-91" value="" style="rounded=0;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="-240" width="1240" height="960" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-1" value="服务端" style="image;html=1;image=img/lib/clip_art/computers/Server_Rack_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;imageBorder=default;" parent="1" vertex="1">
<mxGeometry x="420" y="80" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-2" value="客户端A" style="image;html=1;image=img/lib/clip_art/computers/iMac_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Comic Sans MS;fontSize=20;imageBorder=default;" parent="1" vertex="1">
<mxGeometry x="240" y="240" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-3" value="客户端B" style="image;html=1;image=img/lib/clip_art/computers/iMac_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Comic Sans MS;fontSize=20;imageBorder=default;" parent="1" vertex="1">
<mxGeometry x="600" y="240" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-4" value="RDP桌面共享流程" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="285" y="10" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-5" value="RDPViewer" style="image;html=1;image=img/lib/clip_art/computers/iPad_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Comic Sans MS;fontSize=20;imageBorder=default;" parent="1" vertex="1">
<mxGeometry x="-40" y="240" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-6" value="RDPSession" style="image;html=1;image=img/lib/clip_art/computers/iPad_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Comic Sans MS;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="870" y="240" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-7" value="管理员" style="image;html=1;image=img/lib/clip_art/people/Suit_Man_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="-30" y="80" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-8" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-49" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="280" y="830" as="sourcePoint" />
<mxPoint x="280" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-10" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-53" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="640" y="840" as="sourcePoint" />
<mxPoint x="640" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-12" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=0;entryY=0.75;entryDx=0;entryDy=0;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-7" target="osB9R2kQAUtgP8P5AnCK-1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="70" y="230" as="sourcePoint" />
<mxPoint x="260" y="140" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-14" value="发起共享以B为共享端A为访问端" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="100" y="100" width="280" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-17" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-22" target="osB9R2kQAUtgP8P5AnCK-1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="460" y="840" as="sourcePoint" />
<mxPoint x="500" y="420" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-19" value="开启共享,获取连接串" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="690" y="250" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-20" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="620" y="400" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-23" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-51" target="osB9R2kQAUtgP8P5AnCK-22" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="460" y="840" as="sourcePoint" />
<mxPoint x="460" y="160" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-22" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="440" y="400" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-24" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-3" target="osB9R2kQAUtgP8P5AnCK-6" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="840" y="460" as="sourcePoint" />
<mxPoint x="890" y="410" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-25" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="700" y="120" as="sourcePoint" />
<mxPoint x="700" y="120" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-26" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="437" y="240" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-27" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.375;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-26" target="osB9R2kQAUtgP8P5AnCK-3" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="500" y="370" as="sourcePoint" />
<mxPoint x="550" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-28" value="通知B" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="510" y="240" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-29" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-20" target="osB9R2kQAUtgP8P5AnCK-22" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="830" y="470" as="sourcePoint" />
<mxPoint x="880" y="420" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-30" value="通知A" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="527" y="400" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-31" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="260" y="400" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-32" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-22" target="osB9R2kQAUtgP8P5AnCK-31" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="380" y="560" as="sourcePoint" />
<mxPoint x="430" y="510" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-33" value="通知A" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="340" y="400" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-34" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-38" target="osB9R2kQAUtgP8P5AnCK-5" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="850" as="sourcePoint" />
<mxPoint x="30" y="460" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-35" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="-20" y="400" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-36" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-31" target="osB9R2kQAUtgP8P5AnCK-35" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="120" y="520" as="sourcePoint" />
<mxPoint x="170" y="470" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-37" value="开启rdpviewer访问程序" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="70" y="390" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-39" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-47" target="osB9R2kQAUtgP8P5AnCK-38" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="850" as="sourcePoint" />
<mxPoint y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-38" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="-20" y="520" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-42" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=0.625;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-43" target="osB9R2kQAUtgP8P5AnCK-6" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="920" y="880" as="sourcePoint" />
<mxPoint x="1020" y="540" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-44" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=0.625;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-73" target="osB9R2kQAUtgP8P5AnCK-43" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="920" y="960" as="sourcePoint" />
<mxPoint x="920" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-43" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="900" y="520" width="40" height="60" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-45" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-38" target="osB9R2kQAUtgP8P5AnCK-43" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="-150" y="670" as="sourcePoint" />
<mxPoint x="-100" y="620" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-46" value="通过连接串直接连接B" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="50" y="510" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-48" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" target="osB9R2kQAUtgP8P5AnCK-47" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint y="960" as="sourcePoint" />
<mxPoint y="580" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-47" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="-20" y="640" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-50" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" target="osB9R2kQAUtgP8P5AnCK-49" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="280" y="960" as="sourcePoint" />
<mxPoint x="280" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-49" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="260" y="640" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-52" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-69" target="osB9R2kQAUtgP8P5AnCK-51" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="460" y="960" as="sourcePoint" />
<mxPoint x="460" y="460" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-51" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="440" y="640" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-54" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" source="osB9R2kQAUtgP8P5AnCK-71" target="osB9R2kQAUtgP8P5AnCK-53" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="640" y="960" as="sourcePoint" />
<mxPoint x="640" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-53" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="620" y="640" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-55" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="900" y="640" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-57" value="连接A自己的监听" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="60" y="660" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-58" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-49" target="osB9R2kQAUtgP8P5AnCK-51" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="380" y="850" as="sourcePoint" />
<mxPoint x="430" y="800" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-59" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;通知B来连接A&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="340" y="630" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-60" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-51" target="osB9R2kQAUtgP8P5AnCK-53" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="540" y="760" as="sourcePoint" />
<mxPoint x="590" y="710" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-61" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;通知B去连接A&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="530" y="630" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-62" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-53" target="osB9R2kQAUtgP8P5AnCK-55" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="770" y="880" as="sourcePoint" />
<mxPoint x="820" y="830" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-63" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;连接共享&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="760" y="660" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-64" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=-0.025;entryY=0.825;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1.25;exitY=0.833;exitDx=0;exitDy=0;exitPerimeter=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-49" target="osB9R2kQAUtgP8P5AnCK-53" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="500" y="890" as="sourcePoint" />
<mxPoint x="550" y="840" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-65" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;B去连接A&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="510" y="700" width="70" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-66" value="第一次尝试" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="-190" y="520" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-67" value="第二次尝试" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="-190" y="640" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-68" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="-20" y="840" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-70" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" target="osB9R2kQAUtgP8P5AnCK-69" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="460" y="960" as="sourcePoint" />
<mxPoint x="460" y="690" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-69" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="440" y="840" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-72" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" target="osB9R2kQAUtgP8P5AnCK-71" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="640" y="960" as="sourcePoint" />
<mxPoint x="640" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-71" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="620" y="840" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-74" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=0.625;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="osB9R2kQAUtgP8P5AnCK-73" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="920" y="960" as="sourcePoint" />
<mxPoint x="925" y="580" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-73" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" parent="1" vertex="1">
<mxGeometry x="900" y="840" width="40" height="120" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-76" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="280" y="960" as="sourcePoint" />
<mxPoint x="280" y="960" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-77" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1.1;exitY=0.388;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.388;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-47" target="osB9R2kQAUtgP8P5AnCK-49" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="150" y="780" as="sourcePoint" />
<mxPoint x="200" y="730" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-78" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1.063;exitY=0.338;exitDx=0;exitDy=0;exitPerimeter=0;entryX=-0.125;entryY=0.325;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-68" target="osB9R2kQAUtgP8P5AnCK-69" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="100" y="900" as="sourcePoint" />
<mxPoint x="150" y="850" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-79" value="连接服务器的监听" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="80" y="850" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-80" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="osB9R2kQAUtgP8P5AnCK-69" target="osB9R2kQAUtgP8P5AnCK-71" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="500" y="870" as="sourcePoint" />
<mxPoint x="550" y="820" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-81" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;通知B去连接服务器&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="520" y="830" width="70" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-82" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-71" target="osB9R2kQAUtgP8P5AnCK-73" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="670" y="864.5" as="sourcePoint" />
<mxPoint x="910" y="864.5" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-83" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;连接共享&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="760" y="840" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-84" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1.075;exitY=0.696;exitDx=0;exitDy=0;exitPerimeter=0;entryX=-0.075;entryY=0.663;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#00CC00;" parent="1" source="osB9R2kQAUtgP8P5AnCK-69" target="osB9R2kQAUtgP8P5AnCK-71" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="560" y="1030" as="sourcePoint" />
<mxPoint x="610" y="980" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-85" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;B去连接服务器&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Comic Sans MS;" parent="1" vertex="1">
<mxGeometry x="500" y="885" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-86" value="第三次尝试" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="-197.5" y="840" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-87" value="由访问段直接连接共享端" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="-182.5" y="560" width="95" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-88" value="以访问端自身为代理,共享端反向连接,形成链路" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="-210" y="680" width="162.5" height="30" as="geometry" />
</mxCell>
<mxCell id="osB9R2kQAUtgP8P5AnCK-89" value="以服务端为代理,共享端反向连接,形成链路" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontFamily=Architects Daughter;" parent="1" vertex="1">
<mxGeometry x="-223.75" y="880" width="162.5" height="30" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-1" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerApiController&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ Update(ViewerUpdateParamInfo info): bool&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="203.75" y="1340" width="421.25" height="150" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-2" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerServerMessenger&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ HeartNotify(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyNotify(ViewerProxyInfo viewerProxyInfo): void&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="403.75" y="2050" width="490" height="180" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-3" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerClientMessenger&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ Server(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Heart(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyFromClient(ViewerProxyInfo viewerProxyInfo): void&lt;br&gt;&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyFromServer(ViewerProxyInfo viewerProxyInfo): void&lt;br&gt;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="403.75" y="1530" width="560" height="210" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-4" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerReport&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ Server(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Heart(ViewerRunningConfigInfo info): void&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="403.75" y="1820" width="450" height="160" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-5" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerProxyClient /&amp;nbsp;&lt;/b&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;ViewerProxyServer&lt;/b&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ Connect(string name, uint connectId): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Connect(ViewerProxyInfo viewerProxyInfo): void&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="403.75" y="2370" width="560" height="170" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-6" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerProxyInfo&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ConnectId: int&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ViewerMachine: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyEP: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;TargetEP: string&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="-240" y="1010" width="250" height="210" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-7" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerRunningConfigInfo&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Mode: enum&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Open: bool&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ShareId: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ServerMachine: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ClientMachines: string[]&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ConnectStr: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ConnectEP: string&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="80" y="1010" width="270" height="270" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-8" value="管理员" style="image;html=1;image=img/lib/clip_art/people/Suit_Man_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-116.25" y="1400" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-9" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerUpdateParamInfo&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;+&amp;nbsp;Open: bool&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ShareId: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Server: string&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Clients: string[]&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="405" y="1010" width="270" height="200" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-10" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-8" target="XBUbmlRP-4RHgawcbSf2-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="-106.25" y="1610" as="sourcePoint" />
<mxPoint x="-56.25" y="1560" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-25" value="1、开启共享" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-10">
<mxGeometry x="-0.1281" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-13" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.715;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-1" target="XBUbmlRP-4RHgawcbSf2-3">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="203.75" y="1460" as="sourcePoint" />
<mxPoint x="113.75" y="1690" as="targetPoint" />
<Array as="points">
<mxPoint x="53.75" y="1580" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-14" value="2、通知共享服务端开启共享" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-13">
<mxGeometry x="-0.1219" y="2" relative="1" as="geometry">
<mxPoint x="-36" y="-2" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-15" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=-0.007;entryY=0.64;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-3" target="XBUbmlRP-4RHgawcbSf2-4">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="133.75" y="1670" as="sourcePoint" />
<mxPoint x="197.75" y="1670" as="targetPoint" />
<Array as="points">
<mxPoint x="313.75" y="1790" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-16" value="3、开启共享" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-15">
<mxGeometry x="-0.0752" y="3" relative="1" as="geometry">
<mxPoint y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-18" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=-0.006;exitY=0.639;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.611;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-4" target="XBUbmlRP-4RHgawcbSf2-2">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="193.75" y="1920" as="sourcePoint" />
<mxPoint x="143.75" y="2160" as="targetPoint" />
<Array as="points">
<mxPoint x="223.75" y="2020" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-19" value="4、向服务器通知其它共享客户端我已开启共享" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-18">
<mxGeometry x="-0.0149" relative="1" as="geometry">
<mxPoint x="49" y="-13" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-21" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=-0.003;exitY=0.595;exitDx=0;exitDy=0;exitPerimeter=0;entryX=-0.001;entryY=0.641;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-2" target="XBUbmlRP-4RHgawcbSf2-3">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="372.2800000000002" y="2156.82" as="sourcePoint" />
<mxPoint x="333.75" y="1650" as="targetPoint" />
<Array as="points">
<mxPoint x="-36.25" y="2140" />
<mxPoint x="-36.25" y="2025.5" />
<mxPoint x="-26.25" y="1765.5" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-22" value="5、通知共享客户端" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-21">
<mxGeometry x="-0.0152" y="1" relative="1" as="geometry">
<mxPoint x="16" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-23" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=0.002;exitY=0.642;exitDx=0;exitDy=0;exitPerimeter=0;entryX=-0.004;entryY=0.802;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-3" target="XBUbmlRP-4RHgawcbSf2-4">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="-246.25" y="1920" as="sourcePoint" />
<mxPoint x="73.75" y="1940" as="targetPoint" />
<Array as="points">
<mxPoint x="153.75" y="1780" />
<mxPoint x="223.75" y="1870" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-24" value="6、客户端开启共享" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-23">
<mxGeometry x="0.0589" y="2" relative="1" as="geometry">
<mxPoint x="21" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-26" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerClientMessenger&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ Server(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;Heart(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyFromClient(ViewerProxyInfo viewerProxyInfo): void&lt;br&gt;&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyFromServer(ViewerProxyInfo viewerProxyInfo): void&lt;br&gt;&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="330.75" y="2930" width="560" height="210" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-27" value="&lt;p style=&quot;margin: 4px 0px 0px; text-align: center;&quot;&gt;&lt;b&gt;ViewerServerMessenger&lt;/b&gt;&lt;br&gt;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;&lt;/p&gt;&lt;hr style=&quot;font-size: 20px;&quot;&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+ HeartNotify(ViewerRunningConfigInfo info): void&lt;/p&gt;&lt;p style=&quot;margin: 0px 0px 0px 4px; font-size: 20px;&quot;&gt;+&amp;nbsp;ProxyNotify(ViewerProxyInfo viewerProxyInfo): void&lt;/p&gt;" style="verticalAlign=top;align=left;overflow=fill;fontSize=20;fontFamily=Helvetica;html=1;whiteSpace=wrap;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" vertex="1" parent="1">
<mxGeometry x="403.75" y="2650" width="490" height="180" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-28" value="RdpViewer" style="image;html=1;image=img/lib/clip_art/people/Suit_Man_128x128.png;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Comic Sans MS;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-170" y="2360" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-29" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=-0.004;entryY=0.655;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#000000;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-28" target="XBUbmlRP-4RHgawcbSf2-5">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="23.75" y="2600" as="sourcePoint" />
<mxPoint x="73.75" y="2550" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-30" value="连接到客户端/服务端共享代理" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Architects Daughter;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-29">
<mxGeometry x="-0.135" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-31" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=0;entryY=0.75;entryDx=0;entryDy=0;exitX=-0.006;exitY=0.685;exitDx=0;exitDy=0;exitPerimeter=0;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-5" target="XBUbmlRP-4RHgawcbSf2-27">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="353.75" y="2510" as="sourcePoint" />
<mxPoint x="193.75" y="2720" as="targetPoint" />
<Array as="points">
<mxPoint x="253.75" y="2580" />
<mxPoint x="253.75" y="2720" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-32" value="来自客户端代理,&lt;br&gt;向服务端通知共享服务端,&lt;br&gt;让它连接客户端" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Architects Daughter;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-31">
<mxGeometry x="-0.0931" y="-3" relative="1" as="geometry">
<mxPoint x="23" y="37" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-33" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;entryX=-0.001;entryY=0.734;entryDx=0;entryDy=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryPerimeter=0;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-27" target="XBUbmlRP-4RHgawcbSf2-26">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="373.75" y="2800" as="sourcePoint" />
<mxPoint x="290.75" y="3092.5" as="targetPoint" />
<Array as="points">
<mxPoint x="263.75" y="2850" />
<mxPoint x="263.75" y="3040" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-34" value="来自客户端代理,&lt;br&gt;通知共享服务端,&lt;br&gt;让它连接客户端" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Architects Daughter;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-33">
<mxGeometry x="-0.0821" relative="1" as="geometry">
<mxPoint y="6" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-35" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=0;exitY=0.75;exitDx=0;exitDy=0;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-26">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="293.75" y="3100" as="sourcePoint" />
<mxPoint x="403.75" y="2500" as="targetPoint" />
<Array as="points">
<mxPoint x="123.75" y="3000" />
<mxPoint x="123.75" y="2650" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-36" value="两个socket&lt;br&gt;一个连接客户端,&lt;br&gt;一个连接RdpSession" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-35">
<mxGeometry x="-0.0291" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-37" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;exitX=-0.001;exitY=0.627;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.001;entryY=0.877;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#e1d5e7;strokeColor=#9673a6;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-5" target="XBUbmlRP-4RHgawcbSf2-26">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="143.75" y="2640" as="sourcePoint" />
<mxPoint x="193.75" y="3110" as="targetPoint" />
<Array as="points">
<mxPoint x="-6.25" y="2610" />
<mxPoint x="-6.25" y="2990" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-38" value="来自服务端代理,&lt;br&gt;通知共享服务端,&lt;br&gt;让它连接客户端" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Architects Daughter;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-37">
<mxGeometry x="-0.1578" y="-3" relative="1" as="geometry">
<mxPoint x="23" y="15" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-39" value="" style="endArrow=classic;html=1;rounded=0;sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;fillColor=#e1d5e7;strokeColor=#9673a6;exitX=-0.002;exitY=0.889;exitDx=0;exitDy=0;exitPerimeter=0;entryX=-0.003;entryY=0.8;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="XBUbmlRP-4RHgawcbSf2-26" target="XBUbmlRP-4RHgawcbSf2-5">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="303.75" y="3140" as="sourcePoint" />
<mxPoint x="243.75" y="2500" as="targetPoint" />
<Array as="points">
<mxPoint x="-116.25" y="3030" />
<mxPoint x="-116.25" y="2580" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBUbmlRP-4RHgawcbSf2-40" value="两个socket&lt;br&gt;一个连接客户端,&lt;br&gt;一个连接RdpSession" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=20;fontFamily=Comic Sans MS;" vertex="1" connectable="0" parent="XBUbmlRP-4RHgawcbSf2-39">
<mxGeometry x="-0.0291" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -1,6 +1,7 @@
using cmonitor.api; using cmonitor.api;
using cmonitor.plugins.signIn.messenger; using cmonitor.plugins.signIn.messenger;
using cmonitor.plugins.viewer.messenger; using cmonitor.plugins.viewer.messenger;
using cmonitor.plugins.viewer.proxy;
using cmonitor.plugins.viewer.report; using cmonitor.plugins.viewer.report;
using cmonitor.server; using cmonitor.server;
using common.libs.extends; using common.libs.extends;
@@ -12,28 +13,39 @@ namespace cmonitor.plugins.viewer
{ {
private readonly MessengerSender messengerSender; private readonly MessengerSender messengerSender;
private readonly SignCaching signCaching; private readonly SignCaching signCaching;
public ViewerApiController(MessengerSender messengerSender, SignCaching signCaching) private readonly ViewerProxyCaching viewerProxyCaching;
public ViewerApiController(MessengerSender messengerSender, SignCaching signCaching, ViewerProxyCaching viewerProxyCaching)
{ {
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
this.signCaching = signCaching; this.signCaching = signCaching;
this.viewerProxyCaching = viewerProxyCaching;
} }
public bool Update(ApiControllerParamsInfo param) public bool Update(ApiControllerParamsInfo param)
{ {
ViewerUpdateParamInfo viewer = param.Content.DeJson<ViewerUpdateParamInfo>(); ViewerUpdateParamInfo viewer = param.Content.DeJson<ViewerUpdateParamInfo>();
//去掉服务端 //去掉服务端,
var list = viewer.Clients.ToList(); var list = viewer.Clients.ToList();
list.Remove(viewer.Server); list.Remove(viewer.Server);
viewer.Clients = list.ToArray(); viewer.Clients = list.ToArray();
viewerProxyCaching.Remove(viewer.ShareId);
if (signCaching.Get(viewer.Server, out SignCacheInfo cache) && cache.Connected) if (signCaching.Get(viewer.Server, out SignCacheInfo cache) && cache.Connected)
{ {
byte[] serverBytes = MemoryPackSerializer.Serialize(new ViewerConfigInfo ViewerRunningConfigInfo info = new ViewerRunningConfigInfo
{ {
Clients = viewer.Clients, ServerMachine = viewer.Server,
ClientMachines = viewer.Clients,
ConnectStr = string.Empty, ConnectStr = string.Empty,
Mode = ViewerMode.Server, Mode = ViewerMode.Server,
Open = viewer.Open Open = viewer.Open
}); };
if (info.Open)
{
info.ShareId = viewerProxyCaching.Set(viewer.Server);
}
byte[] serverBytes = MemoryPackSerializer.Serialize(info);
_ = messengerSender.SendOnly(new MessageRequestWrap _ = messengerSender.SendOnly(new MessageRequestWrap
{ {
Connection = cache.Connection, Connection = cache.Connection,
@@ -48,6 +60,8 @@ namespace cmonitor.plugins.viewer
public bool Open { get; set; } public bool Open { get; set; }
public string Server { get; set; } = string.Empty; public string Server { get; set; } = string.Empty;
public string[] Clients { get; set; } = Array.Empty<string>(); public string[] Clients { get; set; } = Array.Empty<string>();
public string ShareId { get; set; } = string.Empty;
} }
} }

View File

@@ -1,7 +1,9 @@
using cmonitor.config; using cmonitor.config;
using cmonitor.plugins.viewer.messenger; using cmonitor.plugins.viewer.messenger;
using cmonitor.plugins.viewer.proxy;
using cmonitor.plugins.viewer.report; using cmonitor.plugins.viewer.report;
using cmonitor.startup; using cmonitor.startup;
using common.libs;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Reflection; using System.Reflection;
@@ -18,20 +20,30 @@ namespace cmonitor.plugins.viewer
serviceCollection.AddSingleton<ViewerClientMessenger>(); serviceCollection.AddSingleton<ViewerClientMessenger>();
serviceCollection.AddSingleton<ViewerProxySignInArgs>();
serviceCollection.AddSingleton<ViewerProxyClient>();
} }
public void AddServer(ServiceCollection serviceCollection, Config config, Assembly[] assemblies) public void AddServer(ServiceCollection serviceCollection, Config config, Assembly[] assemblies)
{ {
serviceCollection.AddSingleton<ViewerProxyCaching>();
serviceCollection.AddSingleton<ViewerServerMessenger>(); serviceCollection.AddSingleton<ViewerServerMessenger>();
serviceCollection.AddSingleton<ViewerApiController>(); serviceCollection.AddSingleton<ViewerApiController>();
serviceCollection.AddSingleton<ViewerProxyServer>();
} }
public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies) public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{ {
Logger.Instance.Info($"use viewer proxy server in client mode");
ViewerProxyClient viewerProxyServer = serviceProvider.GetService<ViewerProxyClient>();
} }
public void UseServer(ServiceProvider serviceProvider, Config config, Assembly[] assemblies) public void UseServer(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{ {
Logger.Instance.Info($"use viewer proxy server in server mode");
ViewerProxyServer viewerProxyServer = serviceProvider.GetService<ViewerProxyServer>();
} }
} }
} }

View File

@@ -1,4 +1,8 @@
using cmonitor.plugins.signIn.messenger; using cmonitor.client;
using cmonitor.client.running;
using cmonitor.config;
using cmonitor.plugins.signIn.messenger;
using cmonitor.plugins.viewer.proxy;
using cmonitor.plugins.viewer.report; using cmonitor.plugins.viewer.report;
using cmonitor.server; using cmonitor.server;
using MemoryPack; using MemoryPack;
@@ -8,36 +12,54 @@ namespace cmonitor.plugins.viewer.messenger
public sealed class ViewerClientMessenger : IMessenger public sealed class ViewerClientMessenger : IMessenger
{ {
private readonly ViewerReport viewerReport; private readonly ViewerReport viewerReport;
private readonly ViewerProxyClient viewerProxyClient;
private readonly Config config;
private readonly ClientSignInState clientSignInState;
private readonly RunningConfig runningConfig;
public ViewerClientMessenger(ViewerReport viewerReport) public ViewerClientMessenger(ViewerReport viewerReport, ViewerProxyClient viewerProxyClient, Config config, ClientSignInState clientSignInState, RunningConfig runningConfig)
{ {
this.viewerReport = viewerReport; this.viewerReport = viewerReport;
this.viewerProxyClient = viewerProxyClient;
this.config = config;
this.clientSignInState = clientSignInState;
this.runningConfig = runningConfig;
} }
[MessengerId((ushort)ViewerMessengerIds.Server)] [MessengerId((ushort)ViewerMessengerIds.Server)]
public void Server(IConnection connection) public void Server(IConnection connection)
{ {
ViewerConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerConfigInfo>(connection.ReceiveRequestWrap.Payload.Span); ViewerRunningConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerRunningConfigInfo>(connection.ReceiveRequestWrap.Payload.Span);
viewerReport.Server(viewerConfigInfo); viewerReport.Server(viewerConfigInfo);
} }
[MessengerId((ushort)ViewerMessengerIds.Client)]
public void Client(IConnection connection)
{
ViewerConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerConfigInfo>(connection.ReceiveRequestWrap.Payload.Span);
viewerReport.Client(viewerConfigInfo);
}
[MessengerId((ushort)ViewerMessengerIds.Heart)] [MessengerId((ushort)ViewerMessengerIds.Heart)]
public void Heart(IConnection connection) public void Heart(IConnection connection)
{ {
string connectStr = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span); ViewerRunningConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerRunningConfigInfo>(connection.ReceiveRequestWrap.Payload.Span);
viewerReport.Heart(connectStr); viewerReport.Heart(viewerConfigInfo);
}
[MessengerId((ushort)ViewerMessengerIds.ProxyFromClient)]
public async Task ProxyFromClient(IConnection connection)
{
ViewerProxyInfo proxy = MemoryPackSerializer.Deserialize<ViewerProxyInfo>(connection.ReceiveRequestWrap.Payload.Span);
proxy.TargetEP = runningConfig.Data.Viewer.ConnectEP;
await viewerProxyClient.Connect(proxy);
}
[MessengerId((ushort)ViewerMessengerIds.ProxyFromServer)]
public async Task ProxyFromServer(IConnection connection)
{
ViewerProxyInfo proxy = MemoryPackSerializer.Deserialize<ViewerProxyInfo>(connection.ReceiveRequestWrap.Payload.Span);
proxy.ProxyEP = $"{clientSignInState.Connection.Address.Address}:{config.Data.Client.Viewer.ProxyPort}";
proxy.TargetEP = runningConfig.Data.Viewer.ConnectEP;
await viewerProxyClient.Connect(proxy);
} }
} }
public sealed class ViewerServerMessenger : IMessenger public sealed class ViewerServerMessenger : IMessenger
{ {
private readonly MessengerSender messengerSender; private readonly MessengerSender messengerSender;
@@ -50,35 +72,12 @@ namespace cmonitor.plugins.viewer.messenger
} }
[MessengerId((ushort)ViewerMessengerIds.NotifyClient)]
public void NotifyClient(IConnection connection)
{
ViewerConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerConfigInfo>(connection.ReceiveRequestWrap.Payload.Span);
string[] usernames = viewerConfigInfo.Clients;
viewerConfigInfo.Clients = Array.Empty<string>();
viewerConfigInfo.Mode = ViewerMode.Client;
byte[] bytes = MemoryPackSerializer.Serialize(viewerConfigInfo); [MessengerId((ushort)ViewerMessengerIds.HeartNotify)]
foreach (var item in usernames) public void HeartNotify(IConnection connection)
{ {
if (signCaching.Get(item, out SignCacheInfo cache) && cache.Connected) ViewerRunningConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerRunningConfigInfo>(connection.ReceiveRequestWrap.Payload.Span);
{ string[] usernames = viewerConfigInfo.ClientMachines;
_ = messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
MessengerId = (ushort)ViewerMessengerIds.Client,
Payload = bytes
});
}
}
}
[MessengerId((ushort)ViewerMessengerIds.NotifyHeart)]
public void NotifyHeart(IConnection connection)
{
ViewerConfigInfo viewerConfigInfo = MemoryPackSerializer.Deserialize<ViewerConfigInfo>(connection.ReceiveRequestWrap.Payload.Span);
string[] usernames = viewerConfigInfo.Clients;
byte[] bytes = MemoryPackSerializer.Serialize(viewerConfigInfo.ConnectStr);
foreach (var item in usernames) foreach (var item in usernames)
{ {
if (signCaching.Get(item, out SignCacheInfo cache) && cache.Connected) if (signCaching.Get(item, out SignCacheInfo cache) && cache.Connected)
@@ -87,10 +86,25 @@ namespace cmonitor.plugins.viewer.messenger
{ {
Connection = cache.Connection, Connection = cache.Connection,
MessengerId = (ushort)ViewerMessengerIds.Heart, MessengerId = (ushort)ViewerMessengerIds.Heart,
Payload = bytes Payload = connection.ReceiveRequestWrap.Payload
}); });
} }
} }
} }
[MessengerId((ushort)ViewerMessengerIds.ProxyNotify)]
public void ProxyNotify(IConnection connection)
{
ViewerProxyInfo proxy = MemoryPackSerializer.Deserialize<ViewerProxyInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.Get(proxy.ViewerMachine, out SignCacheInfo cache) && cache.Connected)
{
_ = messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
MessengerId = (ushort)ViewerMessengerIds.ProxyFromClient,
Payload = connection.ReceiveRequestWrap.Payload
});
}
}
} }
} }

View File

@@ -3,10 +3,13 @@
public enum ViewerMessengerIds : ushort public enum ViewerMessengerIds : ushort
{ {
Server = 1800, Server = 1800,
Client = 1801, Heart = 1801,
NotifyClient = 1802, HeartNotify = 1802,
Heart = 1803,
NotifyHeart = 1804, ProxyFromClient = 1803,
ProxyFromServer = 1804,
ProxyNotify = 1805,
None = 1899 None = 1899
} }
} }

View File

@@ -0,0 +1,474 @@
using common.libs;
using common.libs.extends;
using MemoryPack;
using System.Buffers;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace cmonitor.plugins.viewer.proxy
{
public class ViewerProxy
{
private readonly WheelTimer<ConnectServerCache> wheelTimer = new WheelTimer<ConnectServerCache>();
private readonly ConcurrentDictionary<uint, ConnectServerCache> connects = new ConcurrentDictionary<uint, ConnectServerCache>();
private NumberSpaceUInt32 ns = new NumberSpaceUInt32();
private SocketAsyncEventArgs acceptEventArg;
Socket socket;
public IPEndPoint LocalEndpoint => socket?.LocalEndPoint as IPEndPoint ?? new IPEndPoint(IPAddress.Any, 0);
public ViewerProxy()
{
}
public void Start(int port)
{
try
{
Stop();
IPEndPoint localEndPoint = new IPEndPoint(NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any, port);
socket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.IPv6Only(localEndPoint.AddressFamily, false);
socket.ReuseBind(localEndPoint);
socket.Listen(int.MaxValue);
acceptEventArg = new SocketAsyncEventArgs
{
UserToken = new AsyncUserToken
{
SourceSocket = socket
},
SocketFlags = SocketFlags.None,
};
acceptEventArg.Completed += IO_Completed;
StartAccept(acceptEventArg);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
}
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
acceptEventArg.AcceptSocket = null;
AsyncUserToken token = (AsyncUserToken)acceptEventArg.UserToken;
try
{
if (token.SourceSocket.AcceptAsync(acceptEventArg) == false)
{
ProcessAccept(acceptEventArg);
}
}
catch (Exception)
{
token.Clear();
}
}
private void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Accept:
ProcessAccept(e);
break;
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
default:
break;
}
}
private void ProcessAccept(SocketAsyncEventArgs e)
{
if (e.AcceptSocket != null)
{
BindReceive(e);
StartAccept(e);
}
}
private void BindReceive(SocketAsyncEventArgs e)
{
try
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
var socket = e.AcceptSocket;
if (socket == null || socket.RemoteEndPoint == null)
{
return;
}
socket.KeepAlive();
AsyncUserToken userToken = new AsyncUserToken
{
SourceSocket = socket
};
SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs
{
UserToken = userToken,
SocketFlags = SocketFlags.None,
};
readEventArgs.SetBuffer(new byte[8 * 1024], 0, 8 * 1024);
readEventArgs.Completed += IO_Completed;
if (socket.ReceiveAsync(readEventArgs) == false)
{
ProcessReceive(readEventArgs);
}
}
catch (Exception ex)
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Error(ex);
}
}
private async void ProcessReceive(SocketAsyncEventArgs e)
{
try
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
int offset = e.Offset;
int length = e.BytesTransferred;
if (token.Step == ViewerProxyStep.Request)
{
await ReadPacket(e, token, e.Buffer.AsMemory(0, length));
return;
}
await token.TargetSocket.SendAsync(e.Buffer.AsMemory(0, length), SocketFlags.None);
if (token.SourceSocket.Available > 0)
{
while (token.SourceSocket.Available > 0)
{
length = token.SourceSocket.Receive(e.Buffer);
if (length > 0)
{
await token.TargetSocket.SendAsync(e.Buffer.AsMemory(0, length), SocketFlags.None);
}
else
{
CloseClientSocket(e);
return;
}
}
}
if (token.SourceSocket.Connected == false)
{
CloseClientSocket(e);
return;
}
if (token.SourceSocket.ReceiveAsync(e) == false)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
catch (Exception ex)
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Error(ex);
CloseClientSocket(e);
}
}
private async Task ReadPacket(SocketAsyncEventArgs e, AsyncUserToken token, Memory<byte> data)
{
if (GetMachineName(data, out string machine))
{
uint id = ns.Increment();
byte[] tempData = new byte[data.Length];
data.CopyTo(tempData);
ConnectServerCache cache = new ConnectServerCache { Id = id, Saea = e, Data = tempData };
connects.TryAdd(id, cache);
WheelTimerTimeout<ConnectServerCache> timeout = wheelTimer.NewTimeout(new WheelTimerTimeoutTask<ConnectServerCache> { State = cache, Callback = ConnectTimeout, }, 3000);
cache.Timeout = timeout;
await Connect(machine, cache.Id);
}
else if (GetConnectId(data, out uint connectId))
{
if (connects.TryRemove(connectId, out ConnectServerCache cache))
{
cache.Timeout.Cancel();
AsyncUserToken sourceToken = cache.Saea.UserToken as AsyncUserToken;
sourceToken.Step = ViewerProxyStep.Forward;
sourceToken.TargetSocket = token.SourceSocket;
sourceToken.TargetSocket.KeepAlive();
await sourceToken.TargetSocket.SendAsync(cache.Data, SocketFlags.None);
cache.Clear();
if (sourceToken.SourceSocket.ReceiveAsync(cache.Saea) == false)
{
ProcessReceive(cache.Saea);
}
BindReceiveTarget(sourceToken);
}
}
else
{
CloseClientSocket(e);
}
}
private void ConnectTimeout(WheelTimerTimeout<ConnectServerCache> timeout)
{
if (timeout.IsCanceled == false)
{
if (connects.TryRemove(timeout.Task.State.Id, out ConnectServerCache cache))
{
CloseClientSocket(cache.Saea);
cache.Clear();
}
}
}
public virtual async Task Connect(string name, uint connectId)
{
await Task.CompletedTask;
}
public async Task Connect(ViewerProxyInfo viewerProxyInfo)
{
Socket proxySocket = null;
Socket targetSocket = null;
try
{
if (IPEndPoint.TryParse(viewerProxyInfo.ProxyEP, out IPEndPoint proxyEP) == false)
{
return;
}
if (IPEndPoint.TryParse(viewerProxyInfo.TargetEP, out IPEndPoint targetEP) == false)
{
return;
}
proxySocket = new Socket(proxyEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
proxySocket.KeepAlive();
await proxySocket.ConnectAsync(proxyEP);
targetSocket = new Socket(targetEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
targetSocket.KeepAlive();
await targetSocket.ConnectAsync(targetEP);
int length = responseBytes.Length + 4;
byte[] data = ArrayPool<byte>.Shared.Rent(length);
responseBytes.AsMemory().CopyTo(data);
viewerProxyInfo.ConnectId.ToBytes(data.AsMemory(responseBytes.Length));
await proxySocket.SendAsync(data.AsMemory(0, length));
ArrayPool<byte>.Shared.Return(data);
BindReceiveTarget(new AsyncUserToken { SourceSocket = proxySocket, TargetSocket = targetSocket });
BindReceiveTarget(new AsyncUserToken { SourceSocket = targetSocket, TargetSocket = proxySocket });
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
proxySocket?.SafeClose();
targetSocket?.SafeClose();
}
}
private void IO_CompletedTarget(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceiveTarget(e);
break;
default:
break;
}
}
private void BindReceiveTarget(AsyncUserToken userToken)
{
try
{
SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs
{
UserToken = userToken,
SocketFlags = SocketFlags.None,
};
readEventArgs.SetBuffer(new byte[8 * 1024], 0, 8 * 1024);
readEventArgs.Completed += IO_CompletedTarget;
if (userToken.TargetSocket.ReceiveAsync(readEventArgs) == false)
{
ProcessReceiveTarget(readEventArgs);
}
}
catch (Exception ex)
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Error(ex);
}
}
private async void ProcessReceiveTarget(SocketAsyncEventArgs e)
{
try
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
int offset = e.Offset;
int length = e.BytesTransferred;
await token.SourceSocket.SendAsync(e.Buffer.AsMemory(0, length), SocketFlags.None);
if (token.TargetSocket.Available > 0)
{
while (token.TargetSocket.Available > 0)
{
length = token.TargetSocket.Receive(e.Buffer);
if (length > 0)
{
await token.SourceSocket.SendAsync(e.Buffer.AsMemory(0, length), SocketFlags.None);
}
else
{
CloseClientSocket(e);
return;
}
}
}
if (token.TargetSocket.Connected == false)
{
CloseClientSocket(e);
return;
}
if (token.TargetSocket.ReceiveAsync(e) == false)
{
ProcessReceiveTarget(e);
}
}
else
{
CloseClientSocket(e);
}
}
catch (Exception ex)
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Error(ex);
CloseClientSocket(e);
}
}
private void CloseClientSocket(SocketAsyncEventArgs e)
{
if (e == null) return;
AsyncUserToken token = e.UserToken as AsyncUserToken;
if (token.SourceSocket != null)
{
token.Clear();
e.Dispose();
}
}
public void Stop()
{
CloseClientSocket(acceptEventArg);
}
private readonly byte[] endBytes = Encoding.UTF8.GetBytes("\r\n");
private readonly byte[] startBytes = Encoding.UTF8.GetBytes("mstshash=");
private byte[] responseBytes = Encoding.UTF8.GetBytes("snltty=");
private bool GetMachineName(Memory<byte> memory, out string machine)
{
machine = string.Empty;
int start = memory.Span.IndexOf(startBytes);
if (start < 0) return false;
memory = memory.Slice(start);
int end = memory.Span.IndexOf(endBytes);
if (end < 0) return false;
machine = Encoding.UTF8.GetString(memory.Span.Slice(startBytes.Length, end - startBytes.Length));
return true;
}
private bool GetConnectId(Memory<byte> memory, out uint id)
{
var span = memory.Span;
id = 0;
if (span.Length != responseBytes.Length + 4 || span.Slice(0, responseBytes.Length).SequenceEqual(responseBytes) == false)
{
return false;
}
id = span.Slice(responseBytes.Length).ToUInt32();
return true;
}
}
[MemoryPackable]
public sealed partial class ViewerProxyInfo
{
public uint ConnectId { get; set; }
public string ViewerMachine { get; set; }
public string ProxyEP { get; set; }
public string TargetEP { get; set; }
}
public sealed class ConnectServerCache
{
public uint Id { get; set; }
public SocketAsyncEventArgs Saea { get; set; }
public WheelTimerTimeout<ConnectServerCache> Timeout { get; set; }
public Memory<byte> Data { get; set; }
public void Clear()
{
}
}
public sealed class AsyncUserToken
{
public Socket SourceSocket { get; set; }
public Socket TargetSocket { get; set; }
public ViewerProxyStep Step { get; set; } = ViewerProxyStep.Request;
public void Clear()
{
SourceSocket?.SafeClose();
SourceSocket = null;
TargetSocket?.SafeClose();
TargetSocket = null;
GC.Collect();
}
}
public enum ViewerProxyStep : byte
{
Request = 1,
Forward = 2
}
}

View File

@@ -0,0 +1,56 @@
using cmonitor.plugins.signIn.messenger;
using cmonitor.server;
using common.libs.extends;
using System.Collections.Concurrent;
namespace cmonitor.plugins.viewer.proxy
{
public sealed class ViewerProxyCaching
{
private readonly ConcurrentDictionary<string, string> viewerCache = new ConcurrentDictionary<string, string>();
private readonly SignCaching signCaching;
public ViewerProxyCaching(SignCaching signCaching)
{
this.signCaching = signCaching;
}
public string Set(string serverMachineName)
{
while (true)
{
string id = GenerateUniqueId();
if (viewerCache.TryAdd(id, serverMachineName))
{
return id;
}
}
}
public bool Remove(string id)
{
return viewerCache.TryRemove(id, out _);
}
public bool Get(string id, out IConnection connection)
{
connection = null;
if (viewerCache.TryGetValue(id, out string serverMachineName))
{
if (signCaching.Get(serverMachineName, out SignCacheInfo info))
{
connection = info.Connection;
return connection != null && connection.Connected;
}
}
return false;
}
private string GenerateUniqueId()
{
byte[] bytes = Guid.NewGuid().ToByteArray();
return Convert.ToBase64String(bytes).SubStr(0, 9);
}
}
}

View File

@@ -0,0 +1,42 @@
using cmonitor.client;
using cmonitor.client.running;
using cmonitor.config;
using cmonitor.plugins.viewer.messenger;
using cmonitor.server;
using MemoryPack;
namespace cmonitor.plugins.viewer.proxy
{
public sealed class ViewerProxyClient : ViewerProxy
{
private readonly MessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
private readonly Config config;
private readonly RunningConfig runningConfig;
public ViewerProxyClient(MessengerSender messengerSender, ClientSignInState clientSignInState, Config config, RunningConfig runningConfig)
{
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
this.config = config;
this.runningConfig = runningConfig;
Start(0);
}
public override async Task Connect(string name, uint connectId)
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ViewerMessengerIds.ProxyNotify,
Payload = MemoryPackSerializer.Serialize(new ViewerProxyInfo
{
ConnectId = connectId,
ProxyEP = $"{clientSignInState.Connection.LocalAddress.Address}:{LocalEndpoint.Port}",
ViewerMachine = runningConfig.Data.Viewer.ServerMachine
})
});
}
}
}

View File

@@ -0,0 +1,40 @@
using cmonitor.config;
using cmonitor.plugins.viewer.messenger;
using cmonitor.server;
using MemoryPack;
namespace cmonitor.plugins.viewer.proxy
{
public sealed class ViewerProxyServer : ViewerProxy
{
private readonly MessengerSender messengerSender;
private readonly ViewerProxyCaching viewerProxyCaching;
public ViewerProxyServer(MessengerSender messengerSender, Config config, ViewerProxyCaching viewerProxyCaching)
{
this.messengerSender = messengerSender;
this.viewerProxyCaching = viewerProxyCaching;
Start(config.Data.Server.Viewer.ProxyPort);
}
public override async Task Connect(string name, uint connectId)
{
if (viewerProxyCaching.Get(name, out IConnection connection))
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = connection,
MessengerId = (ushort)ViewerMessengerIds.ProxyFromServer,
Payload = MemoryPackSerializer.Serialize(new ViewerProxyInfo
{
ConnectId = connectId,
ProxyEP = string.Empty,
ViewerMachine = string.Empty
})
});
}
}
}
}

View File

@@ -0,0 +1,24 @@
using cmonitor.client.args;
using cmonitor.plugins.viewer.report;
using common.libs.winapis;
namespace cmonitor.plugins.viewer.proxy
{
public sealed class ViewerProxySignInArgs : ISignInArgs
{
public void Invoke(Dictionary<string, string> args)
{
string userName = GetUserName();
args[ViewerConfigInfo.userNameKey] = userName;
}
private string GetUserName()
{
if (OperatingSystem.IsWindows())
{
return Win32Interop.GetUserName();
}
return string.Empty;
}
}
}

View File

@@ -1,22 +1,56 @@
using MemoryPack; using cmonitor.plugins.viewer.report;
using MemoryPack;
using System.Net;
using System.Text.Json.Serialization;
namespace cmonitor.plugins.viewer.report namespace cmonitor.plugins.viewer.report
{ {
public interface IViewer public interface IViewer
{ {
public void Open(bool value, ViewerMode mode); public void Open(bool value, ParamInfo info);
public string GetConnectString(); public string GetConnectString();
public void SetConnectString(string connectStr); public void SetConnectString(string connectStr);
public string GetConnectEP(string connectStr)
{
return string.Empty;
}
}
public sealed class ViewerConfigInfo
{
[JsonIgnore]
public const string userNameKey = "viewer-username";
public int ProxyPort { get; set; } = 1803;
} }
[MemoryPackable] [MemoryPackable]
public sealed partial class ViewerConfigInfo public sealed partial class ViewerRunningConfigInfo
{ {
public ViewerMode Mode { get; set; } public ViewerMode Mode { get; set; }
public bool Open { get; set; } public bool Open { get; set; }
public string[] Clients { get; set; } = Array.Empty<string>();
public string ShareId { get; set; } = string.Empty;
/// <summary>
/// 共享服务端机器名,在通知消息和代理时需要
/// </summary>
public string ServerMachine { get; set; } = string.Empty;
/// <summary>
/// 共享客户端机器名列表
/// </summary>
public string[] ClientMachines { get; set; } = Array.Empty<string>();
/// <summary>
/// 共享连接串,提供给客户端的共享桌面工具,去连接服务
/// </summary>
public string ConnectStr { get; set; } = string.Empty; public string ConnectStr { get; set; } = string.Empty;
/// <summary>
/// 共享服务的连接地址,在代理时需要
/// 比如 B 是共享服务端A是共享客户端
/// 当连不上时会需要代理由B去连接A或者B去连接服务器形成通道那B这边还需要手动连接共享服务就用这个去连
/// </summary>
public string ConnectEP { get; set; } = string.Empty;
} }
public enum ViewerMode : byte public enum ViewerMode : byte
@@ -24,4 +58,44 @@ namespace cmonitor.plugins.viewer.report
Client = 0, Client = 0,
Server = 1, Server = 1,
} }
public sealed class ParamInfo
{
public string ShareMkey { get; set; } = "cmonitor/share";
public int ShareMLength { get; set; } = 10;
public int ShareItemMLength { get; set; } = 1024;
public int ShareIndex { get; set; } = 5;
public ViewerMode Mode { get; set; } = ViewerMode.Server;
public string GroupName { get; set; } = "snltty";
public string ProxyServers { get; set; } = "127.0.0.1:1803";
}
}
namespace cmonitor.config
{
public sealed partial class ConfigClientInfo
{
public ViewerConfigInfo Viewer { get; set; } = new ViewerConfigInfo();
}
public sealed partial class ConfigServerInfo
{
public ViewerConfigInfo Viewer { get; set; } = new ViewerConfigInfo();
}
}
namespace cmonitor.client.running
{
public sealed partial class RunningConfigInfo
{
private ViewerRunningConfigInfo viewer = new ViewerRunningConfigInfo();
public ViewerRunningConfigInfo Viewer
{
get => viewer; set
{
Updated++;
viewer = value;
}
}
}
} }

View File

@@ -1,8 +1,10 @@
namespace cmonitor.plugins.viewer.report using System.Net;
namespace cmonitor.plugins.viewer.report
{ {
public sealed class ViewerLinux : IViewer public sealed class ViewerLinux : IViewer
{ {
public void Open(bool value, ViewerMode mode) public void Open(bool value, ParamInfo info)
{ {
} }
public string GetConnectString() public string GetConnectString()
@@ -13,5 +15,10 @@
{ {
} }
public string GetConnectEP(string connectStr)
{
return string.Empty;
}
} }
} }

View File

@@ -1,8 +1,10 @@
namespace cmonitor.plugins.viewer.report using System.Net;
namespace cmonitor.plugins.viewer.report
{ {
public sealed class ViewerMacOS : IViewer public sealed class ViewerMacOS : IViewer
{ {
public void Open(bool value, ViewerMode mode) public void Open(bool value, ParamInfo info)
{ {
} }
public string GetConnectString() public string GetConnectString()
@@ -13,5 +15,10 @@
{ {
} }
public string GetConnectEP(string connectStr)
{
return string.Empty;
}
} }
} }

View File

@@ -1,5 +1,4 @@
using cmonitor.client; using cmonitor.client;
using cmonitor.client.runningConfig;
using cmonitor.client.report; using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using cmonitor.libs; using cmonitor.libs;
@@ -7,6 +6,10 @@ using cmonitor.plugins.viewer.messenger;
using cmonitor.server; using cmonitor.server;
using common.libs; using common.libs;
using MemoryPack; using MemoryPack;
using cmonitor.client.running;
using System.Net;
using System.Text.Json;
using cmonitor.plugins.viewer.proxy;
namespace cmonitor.plugins.viewer.report namespace cmonitor.plugins.viewer.report
{ {
@@ -20,22 +23,24 @@ namespace cmonitor.plugins.viewer.report
public string Name => "Viewer"; public string Name => "Viewer";
private ViewerReportInfo report = new ViewerReportInfo(); private ViewerReportInfo report = new ViewerReportInfo();
private readonly IRunningConfig clientConfig; private readonly RunningConfig runningConfig;
private readonly IViewer viewer; private readonly IViewer viewer;
private readonly ShareMemory shareMemory; private readonly ShareMemory shareMemory;
private ViewerConfigInfo viewerConfigInfo;
private readonly MessengerSender messengerSender; private readonly MessengerSender messengerSender;
private readonly ClientSignInState clientSignInState; private readonly ClientSignInState clientSignInState;
private readonly Config config;
private readonly ViewerProxyClient viewerProxyClient;
public ViewerReport(Config config, IRunningConfig clientConfig, IViewer viewer, ShareMemory shareMemory, ClientSignInState clientSignInState, MessengerSender messengerSender) public ViewerReport(Config config, RunningConfig runningConfig, IViewer viewer, ShareMemory shareMemory, ClientSignInState clientSignInState, MessengerSender messengerSender, ViewerProxyClient viewerProxyClient)
{ {
this.clientConfig = clientConfig; this.config = config;
this.runningConfig = runningConfig;
this.viewer = viewer; this.viewer = viewer;
this.shareMemory = shareMemory; this.shareMemory = shareMemory;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
this.viewerProxyClient = viewerProxyClient;
viewerConfigInfo = clientConfig.Get(new ViewerConfigInfo { });
clientSignInState.NetworkFirstEnabledHandle += () => clientSignInState.NetworkFirstEnabledHandle += () =>
{ {
Update(); Update();
@@ -46,7 +51,8 @@ namespace cmonitor.plugins.viewer.report
public object GetReports(ReportType reportType) public object GetReports(ReportType reportType)
{ {
report.Value = Running(); report.Value = Running();
report.Mode = viewerConfigInfo.Mode; report.Mode = runningConfig.Data.Viewer.Mode;
report.ShareId = runningConfig.Data.Viewer.ShareId;
if (reportType == ReportType.Full || report.Updated() || shareMemory.ReadVersionUpdated((int)ShareMemoryIndexs.Viewer)) if (reportType == ReportType.Full || report.Updated() || shareMemory.ReadVersionUpdated((int)ShareMemoryIndexs.Viewer))
{ {
return report; return report;
@@ -54,50 +60,99 @@ namespace cmonitor.plugins.viewer.report
return null; return null;
} }
private void Update() public void Server(ViewerRunningConfigInfo info)
{
if (viewerConfigInfo.Mode == ViewerMode.Server && viewerConfigInfo.Open && Running() == false)
{
Server(viewerConfigInfo);
}
}
public void Server(ViewerConfigInfo info)
{ {
if (info.Open) if (info.Open)
{ {
viewerConfigInfo = info; runningConfig.Data.Viewer = info;
} }
else else
{ {
viewerConfigInfo.Open = info.Open; runningConfig.Data.Viewer.Open = info.Open;
} }
runningConfig.Data.Update();
clientConfig.Set(viewerConfigInfo);
viewerConfigInfo.ConnectStr = string.Empty;
viewer.SetConnectString(viewerConfigInfo.ConnectStr);
Task.Run(async () => Task.Run(async () =>
{ {
shareMemory.AddAttribute((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Closed); Close();
shareMemory.RemoveAttribute((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Running); await HeartNotify(false);
await Task.Delay(200); await Task.Delay(500);
Open();
viewer.Open(viewerConfigInfo.Open, viewerConfigInfo.Mode); if (runningConfig.Data.Viewer.Open)
if (viewerConfigInfo.Open) {
runningConfig.Data.Viewer.ConnectStr = await GetNewConnectStr();
if (string.IsNullOrWhiteSpace(runningConfig.Data.Viewer.ConnectStr) == false)
{
UpdateConnectEP();
await HeartNotify(runningConfig.Data.Viewer.Open);
}
}
});
}
public void Heart(ViewerRunningConfigInfo info)
{
if (info.ConnectStr != runningConfig.Data.Viewer.ConnectStr)
{
viewer.SetConnectString(ReplaceProxy(info.ConnectStr));
}
//未运行或者不是client模式或者状态不对都需要重启一下
bool restart = Running() != true
|| runningConfig.Data.Viewer.Mode != ViewerMode.Client
|| runningConfig.Data.Viewer.Open != info.Open;
runningConfig.Data.Viewer = info;
runningConfig.Data.Update();
if (restart)
{
RestartClient();
}
}
private async Task HeartNotify(bool open)
{
ViewerRunningConfigInfo info = JsonSerializer.Deserialize<ViewerRunningConfigInfo>(JsonSerializer.Serialize(runningConfig.Data.Viewer));
info.Mode = ViewerMode.Client;
info.Open = open;
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ViewerMessengerIds.HeartNotify,
Payload = MemoryPackSerializer.Serialize(info)
});
}
private void RestartClient()
{
Task.Run(async () =>
{
Close();
await Task.Delay(500);
Open();
});
}
private void Update()
{
if (runningConfig.Data.Viewer.Mode == ViewerMode.Server && runningConfig.Data.Viewer.Open && Running() == false)
{
Server(runningConfig.Data.Viewer);
}
}
private async Task<string> GetNewConnectStr()
{ {
try try
{ {
for (int i = 0; i < 300; i++) for (int i = 0; i < 300; i++)
{ {
var attr = shareMemory.ReadAttribute((int)ShareMemoryIndexs.Viewer);
if (shareMemory.ReadAttributeEqual((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Running)) if (shareMemory.ReadAttributeEqual((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Running))
{ {
viewerConfigInfo.ConnectStr = viewer.GetConnectString(); string connectStr = viewer.GetConnectString();
if (string.IsNullOrWhiteSpace(viewerConfigInfo.ConnectStr) == false) if (string.IsNullOrWhiteSpace(connectStr) == false) return connectStr;
{
NotifyHeart();
break;
}
} }
await Task.Delay(100); await Task.Delay(100);
} }
@@ -106,69 +161,54 @@ namespace cmonitor.plugins.viewer.report
{ {
Logger.Instance.Error(ex); Logger.Instance.Error(ex);
} }
return string.Empty;
} }
else private string ReplaceProxy(string connectStr)
{ {
NotifyClient(); if (IPAddress.IsLoopback(clientSignInState.Connection.LocalAddress.Address))
} {
}); Logger.Instance.Warning($"use Loopback address【{clientSignInState.Connection.LocalAddress.Address}】 as viewer proxymay fail");
Logger.Instance.Warning($"connect to the local or external network address of the cmonitor server,to fix this");
} }
private void RestartClient() return connectStr
.Replace("{ip}", clientSignInState.Connection.LocalAddress.Address.ToString())
//.Replace("{port}", "12345");
.Replace("{port}", viewerProxyClient.LocalEndpoint.Port.ToString());
}
private void UpdateConnectEP()
{ {
Task.Run(async () => string connectEP = viewer.GetConnectEP(runningConfig.Data.Viewer.ConnectStr);
runningConfig.Data.Viewer.ConnectEP = connectEP;
runningConfig.Data.Update();
}
private void Open()
{
if (runningConfig.Data.Viewer.Open)
{
viewer.Open(runningConfig.Data.Viewer.Open, new ParamInfo
{
GroupName = runningConfig.Data.Viewer.ShareId,
Mode = runningConfig.Data.Viewer.Mode,
ProxyServers = string.Join(",", new string[] {
$"{clientSignInState.Connection.Address.Address}:{config.Data.Client.Viewer.ProxyPort}"
}),
ShareIndex = (int)ShareMemoryIndexs.Viewer,
ShareMkey = config.Data.Client.ShareMemoryKey,
ShareMLength = config.Data.Client.ShareMemoryCount,
ShareItemMLength = config.Data.Client.ShareMemorySize
});
}
}
private void Close()
{ {
shareMemory.AddAttribute((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Closed); shareMemory.AddAttribute((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Closed);
shareMemory.RemoveAttribute((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Running); shareMemory.RemoveAttribute((int)ShareMemoryIndexs.Viewer, ShareMemoryAttribute.Running);
await Task.Delay(500); runningConfig.Data.Viewer.ConnectStr = string.Empty;
viewer.Open(viewerConfigInfo.Open, viewerConfigInfo.Mode); viewer.SetConnectString(runningConfig.Data.Viewer.ConnectStr);
}); runningConfig.Data.Update();
}
public void Client(ViewerConfigInfo info)
{
viewerConfigInfo = info;
clientConfig.Set(viewerConfigInfo);
viewer.SetConnectString(viewerConfigInfo.ConnectStr);
RestartClient();
}
private void NotifyClient()
{
_ = messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ViewerMessengerIds.NotifyClient,
Payload = MemoryPackSerializer.Serialize(viewerConfigInfo)
});
}
public void Heart(string connectStr)
{
bool restart = Running() == false || viewerConfigInfo.Mode == ViewerMode.Server;
viewerConfigInfo.ConnectStr = connectStr;
viewerConfigInfo.Mode = ViewerMode.Client;
viewerConfigInfo.Open = true;
clientConfig.Set(viewerConfigInfo);
viewer.SetConnectString(viewerConfigInfo.ConnectStr);
if (restart)
{
RestartClient();
}
}
private void NotifyHeart()
{
if (string.IsNullOrWhiteSpace(viewerConfigInfo.ConnectStr))
{
return;
}
if (viewerConfigInfo.Open && viewerConfigInfo.Mode == ViewerMode.Server && Running())
{
_ = messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ViewerMessengerIds.NotifyHeart,
Payload = MemoryPackSerializer.Serialize(viewerConfigInfo)
});
}
} }
private bool Running() private bool Running()
@@ -183,20 +223,35 @@ namespace cmonitor.plugins.viewer.report
{ {
while (true) while (true)
{ {
NotifyHeart(); bool heart = string.IsNullOrWhiteSpace(runningConfig.Data.Viewer.ConnectStr) == false
&& runningConfig.Data.Viewer.Open && runningConfig.Data.Viewer.Mode == ViewerMode.Server && Running();
if (heart)
{
await HeartNotify(runningConfig.Data.Viewer.Open);
}
await Task.Delay(5000); await Task.Delay(5000);
} }
}); });
} }
} }
[MemoryPackable]
public sealed partial class ViewerHeartInfo
{
public string Server { get; set; }
public string ConnectStr { get; set; }
}
public sealed class ViewerReportInfo : ReportInfo public sealed class ViewerReportInfo : ReportInfo
{ {
public bool Value { get; set; } public bool Value { get; set; }
public ViewerMode Mode { get; set; } public ViewerMode Mode { get; set; }
public string ShareId { get; set; } = string.Empty;
public override int HashCode() public override int HashCode()
{ {
return Value.GetHashCode() ^ Mode.GetHashCode(); return Value.GetHashCode() ^ Mode.GetHashCode() ^ ShareId.GetHashCode();
} }
} }
} }

View File

@@ -1,7 +1,10 @@
using cmonitor.config; using cmonitor.config;
using common.libs; using common.libs;
using Microsoft.Win32; using Microsoft.Win32;
using System.Net;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Text.Json;
using System.Xml;
namespace cmonitor.plugins.viewer.report namespace cmonitor.plugins.viewer.report
{ {
@@ -13,17 +16,14 @@ namespace cmonitor.plugins.viewer.report
{ {
this.config = config; this.config = config;
} }
public void Open(bool value, ViewerMode mode) public void Open(bool value, ParamInfo info)
{ {
if (value) if (value)
{ {
string command = $"start cmonitor.viewer.server.win.exe {config.Client.ShareMemoryKey} {config.Client.ShareMemoryCount} {config.Client.ShareMemorySize} {(int)ShareMemoryIndexs.Viewer} {(byte)mode}"; string str = JsonSerializer.Serialize(info);
string command = $"start cmonitor.viewer.server.win.exe \"{str.Replace("\"","\\\"")}\"";
CommandHelper.Windows(string.Empty, new string[] { command }, false); CommandHelper.Windows(string.Empty, new string[] { command }, false);
} }
else
{
//CommandHelper.Windows(string.Empty, new string[] { $"taskkill /f /im cmonitor.viewer.server.win.exe" });
}
} }
public string GetConnectString() public string GetConnectString()
@@ -34,5 +34,27 @@ namespace cmonitor.plugins.viewer.report
{ {
Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\Cmonitor", "viewerConnectStr", connectStr); Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\Cmonitor", "viewerConnectStr", connectStr);
} }
public string GetConnectEP(string connectStr)
{
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(connectStr);
var nodes = xmlDoc.DocumentElement["C"]["T"].ChildNodes;
var node = nodes[nodes.Count - 3];
var p = node.Attributes["P"].Value;
var n = node.Attributes["N"].Value;
return $"{n}:{p}";
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
return string.Empty;
}
} }
} }

View File

@@ -1,5 +1,6 @@
using cmonitor.client.report; using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using cmonitor.plugins.volume.report;
namespace cmonitor.plugins.volume.report namespace cmonitor.plugins.volume.report
{ {
@@ -9,13 +10,11 @@ namespace cmonitor.plugins.volume.report
private VolumeReportInfo report = new VolumeReportInfo(); private VolumeReportInfo report = new VolumeReportInfo();
private readonly IVolume volume; private readonly IVolume volume;
private VolumeConfigInfo config; private Config config;
public VolumeReport(Config config, IVolume volume) public VolumeReport(Config config, IVolume volume)
{ {
this.config = config.Get(this.Name, new VolumeConfigInfo()); this.config = config;
config.Set(this.Name, this.config);
this.volume = volume; this.volume = volume;
} }
@@ -23,7 +22,7 @@ namespace cmonitor.plugins.volume.report
{ {
report.Value = volume.GetVolume(); report.Value = volume.GetVolume();
report.Mute = volume.GetMute(); report.Mute = volume.GetMute();
if (config.MasterPeak) if (config.Data.Client.Volume.MasterPeak)
{ {
report.MasterPeak = volume.GetMasterPeak(); report.MasterPeak = volume.GetMasterPeak();
} }
@@ -66,4 +65,13 @@ namespace cmonitor.plugins.volume.report
{ {
public bool MasterPeak { get; set; } public bool MasterPeak { get; set; }
} }
}
namespace cmonitor.config
{
public sealed partial class ConfigClientInfo
{
public VolumeConfigInfo Volume { get; set; } = new VolumeConfigInfo();
}
} }

View File

@@ -1,4 +1,5 @@
using MemoryPack; using cmonitor.plugins.wallpaper.report;
using MemoryPack;
namespace cmonitor.plugins.wallpaper.report namespace cmonitor.plugins.wallpaper.report
{ {
@@ -14,3 +15,19 @@ namespace cmonitor.plugins.wallpaper.report
public string ImgUrl { get; set; } public string ImgUrl { get; set; }
} }
} }
namespace cmonitor.client.running
{
public sealed partial class RunningConfigInfo
{
private WallpaperConfigInfo wallpaper = new WallpaperConfigInfo();
public WallpaperConfigInfo Wallpaper
{
get => wallpaper; set
{
Updated++;
wallpaper = value;
}
}
}
}

View File

@@ -1,9 +1,9 @@
using cmonitor.client; using cmonitor.client;
using cmonitor.client.runningConfig;
using cmonitor.client.report; using cmonitor.client.report;
using cmonitor.config; using cmonitor.config;
using cmonitor.libs; using cmonitor.libs;
using common.libs; using common.libs;
using cmonitor.client.running;
namespace cmonitor.plugins.wallpaper.report namespace cmonitor.plugins.wallpaper.report
{ {
@@ -12,21 +12,19 @@ namespace cmonitor.plugins.wallpaper.report
public string Name => "Wallpaper"; public string Name => "Wallpaper";
private WallpaperReportInfo report = new WallpaperReportInfo(); private WallpaperReportInfo report = new WallpaperReportInfo();
private readonly IRunningConfig clientConfig; private readonly RunningConfig runningConfig;
private readonly IWallpaper wallpaper; private readonly IWallpaper wallpaper;
private readonly ShareMemory shareMemory; private readonly ShareMemory shareMemory;
private WallpaperConfigInfo wallpaperConfig;
public WallpaperReport(Config config, IRunningConfig clientConfig, IWallpaper wallpaper, ShareMemory shareMemory, ClientSignInState clientSignInState) public WallpaperReport(Config config, RunningConfig runningConfig, IWallpaper wallpaper, ShareMemory shareMemory, ClientSignInState clientSignInState)
{ {
this.clientConfig = clientConfig; this.runningConfig = runningConfig;
this.wallpaper = wallpaper; this.wallpaper = wallpaper;
this.shareMemory = shareMemory; this.shareMemory = shareMemory;
wallpaperConfig = clientConfig.Get(new WallpaperConfigInfo { });
clientSignInState.NetworkFirstEnabledHandle += () => clientSignInState.NetworkFirstEnabledHandle += () =>
{ {
Update(wallpaperConfig); Update(runningConfig.Data.Wallpaper);
WallpaperTask(); WallpaperTask();
}; };
} }
@@ -43,13 +41,12 @@ namespace cmonitor.plugins.wallpaper.report
public void Update(WallpaperConfigInfo info) public void Update(WallpaperConfigInfo info)
{ {
wallpaperConfig = info; runningConfig.Data.Wallpaper = info;
Task.Run(async () => Task.Run(async () =>
{ {
clientConfig.Set(wallpaperConfig);
shareMemory.AddAttribute((int)ShareMemoryIndexs.Wallpaper, ShareMemoryAttribute.Closed); shareMemory.AddAttribute((int)ShareMemoryIndexs.Wallpaper, ShareMemoryAttribute.Closed);
await Task.Delay(100); await Task.Delay(100);
wallpaper.Set(wallpaperConfig); wallpaper.Set(runningConfig.Data.Wallpaper);
}); });
} }
@@ -65,11 +62,11 @@ namespace cmonitor.plugins.wallpaper.report
{ {
while (true) while (true)
{ {
if (wallpaperConfig.Open) if (runningConfig.Data.Wallpaper.Open)
{ {
if (Running() == false) if (Running() == false)
{ {
Update(wallpaperConfig); Update(runningConfig.Data.Wallpaper);
} }
} }
await Task.Delay(5000); await Task.Delay(5000);

View File

@@ -15,7 +15,7 @@ namespace cmonitor.plugins.wallpaper.report
if (info.Open) if (info.Open)
{ {
CommandHelper.Windows(string.Empty, new string[] { CommandHelper.Windows(string.Empty, new string[] {
$"start cmonitor.wallpaper.win.exe \"{info.ImgUrl}\" {config.Client.ShareMemoryKey} {config.Client.ShareMemoryCount} {config.Client.ShareMemorySize} {(int)ShareMemoryIndexs.Keyboard} {(int)ShareMemoryIndexs.Wallpaper}" $"start cmonitor.wallpaper.win.exe \"{info.ImgUrl}\" {config.Data.Client.ShareMemoryKey} {config.Data.Client.ShareMemoryCount} {config.Data.Client.ShareMemorySize} {(int)ShareMemoryIndexs.Keyboard} {(int)ShareMemoryIndexs.Wallpaper}"
},false); },false);
} }
} }

View File

@@ -28,7 +28,6 @@ namespace cmonitor.plugins.wlan
public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies) public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{ {
serviceProvider.GetServices<WlanReport>();
} }
public void UseServer(ServiceProvider serviceProvider, Config config, Assembly[] assemblies) public void UseServer(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)

View File

@@ -1,5 +1,4 @@
using ManagedNativeWifi; using ManagedNativeWifi;
using System.Net.NetworkInformation;
namespace cmonitor.plugins.wlan.report namespace cmonitor.plugins.wlan.report
{ {

View File

@@ -11,7 +11,7 @@ namespace cmonitor.server
/// </summary> /// </summary>
public interface IConnection public interface IConnection
{ {
public string Name{ get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// <summary> /// <summary>
/// 已连接 /// 已连接
@@ -19,6 +19,7 @@ namespace cmonitor.server
public bool Connected { get; } public bool Connected { get; }
public IPEndPoint Address { get; } public IPEndPoint Address { get; }
public IPEndPoint LocalAddress { get; }
#region #region
/// <summary> /// <summary>
@@ -77,10 +78,6 @@ namespace cmonitor.server
public void Return(); public void Return();
#endregion #endregion
public Task WaitOne();
public void Release();
} }
public abstract class Connection : IConnection public abstract class Connection : IConnection
@@ -98,6 +95,7 @@ namespace cmonitor.server
/// 地址 /// 地址
/// </summary> /// </summary>
public IPEndPoint Address { get; protected set; } public IPEndPoint Address { get; protected set; }
public IPEndPoint LocalAddress { get; protected set; }
#region #region
@@ -210,61 +208,9 @@ namespace cmonitor.server
/// </summary> /// </summary>
public virtual void Disponse() public virtual void Disponse()
{ {
try
{
if (Semaphore != null)
{
if (locked)
{
locked = false;
Semaphore.Release();
}
Semaphore.Dispose();
}
Semaphore = null;
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
//ReceiveRequestWrap = null;
//ReceiveResponseWrap = null;
} }
SemaphoreSlim Semaphore = new SemaphoreSlim(1);
bool locked = false;
public virtual async Task WaitOne()
{
try
{
if (Semaphore != null)
{
locked = true;
await Semaphore.WaitAsync();
}
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
}
public virtual void Release()
{
try
{
if (Semaphore != null)
{
locked = false;
Semaphore.Release();
}
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
}
} }
@@ -280,6 +226,13 @@ namespace cmonitor.server
address = new IPEndPoint(new IPAddress(address.Address.GetAddressBytes()[^4..]), address.Port); address = new IPEndPoint(new IPAddress(address.Address.GetAddressBytes()[^4..]), address.Port);
} }
Address = address; Address = address;
IPEndPoint localaddress = TcpSocket.LocalEndPoint as IPEndPoint ?? new IPEndPoint(IPAddress.Any, 0);
if (localaddress.Address.AddressFamily == AddressFamily.InterNetworkV6 && localaddress.Address.IsIPv4MappedToIPv6)
{
localaddress = new IPEndPoint(new IPAddress(localaddress.Address.GetAddressBytes()[^4..]), localaddress.Port);
}
LocalAddress = localaddress;
} }
/// <summary> /// <summary>

View File

@@ -30,11 +30,16 @@ namespace cmonitor.server
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
public void LoadMessenger(Assembly[] assemblys) public void LoadMessenger(Assembly[] assemblys, string[] pluginNames)
{ {
Type voidType = typeof(void); Type voidType = typeof(void);
Type midType = typeof(MessengerIdAttribute); Type midType = typeof(MessengerIdAttribute);
var types = ReflectionHelper.GetInterfaceSchieves(assemblys, typeof(IMessenger)).Distinct(); var types = ReflectionHelper.GetInterfaceSchieves(assemblys, typeof(IMessenger)).Distinct();
if (pluginNames.Length > 0)
{
types = types.Where(c => pluginNames.Any(d => c.FullName.Contains(d)));
}
foreach (Type type in types) foreach (Type type in types)
{ {
object obj = serviceProvider.GetService(type); object obj = serviceProvider.GetService(type);

View File

@@ -35,7 +35,7 @@ namespace cmonitor.server
if (loaded == false) if (loaded == false)
{ {
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>(); MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
messengerResolver.LoadMessenger(assemblies); messengerResolver.LoadMessenger(assemblies, config.Data.Common.PluginNames);
loaded = true; loaded = true;
} }
} }
@@ -45,7 +45,7 @@ namespace cmonitor.server
if (loaded == false) if (loaded == false)
{ {
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>(); MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
messengerResolver.LoadMessenger(assemblies); messengerResolver.LoadMessenger(assemblies, config.Data.Common.PluginNames);
loaded = true; loaded = true;
} }
@@ -53,7 +53,7 @@ namespace cmonitor.server
//服务 //服务
TcpServer tcpServer = serviceProvider.GetService<TcpServer>(); TcpServer tcpServer = serviceProvider.GetService<TcpServer>();
tcpServer.Start(); tcpServer.Start();
Logger.Instance.Info($"server listen:{config.Server.ServicePort}"); Logger.Instance.Info($"server listen:{config.Data.Server.ServicePort}");
} }
} }
} }

View File

@@ -31,7 +31,7 @@ namespace cmonitor.server
private Socket BindAccept() private Socket BindAccept()
{ {
IPEndPoint localEndPoint = new IPEndPoint(NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any, config.Server.ServicePort); IPEndPoint localEndPoint = new IPEndPoint(NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any, config.Data.Server.ServicePort);
Socket socket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); Socket socket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.IPv6Only(localEndPoint.AddressFamily, false); socket.IPv6Only(localEndPoint.AddressFamily, false);
socket.ReuseBind(localEndPoint); socket.ReuseBind(localEndPoint);
@@ -48,7 +48,7 @@ namespace cmonitor.server
acceptEventArg.Completed += IO_Completed; acceptEventArg.Completed += IO_Completed;
StartAccept(acceptEventArg); StartAccept(acceptEventArg);
socketUdp = new UdpClient(new IPEndPoint(IPAddress.Any, config.Server.ServicePort)); socketUdp = new UdpClient(new IPEndPoint(IPAddress.Any, config.Data.Server.ServicePort));
//socketUdp.JoinMulticastGroup(config.BroadcastIP); //socketUdp.JoinMulticastGroup(config.BroadcastIP);
socketUdp.Client.EnableBroadcast = true; socketUdp.Client.EnableBroadcast = true;
socketUdp.Client.WindowsUdpBug(); socketUdp.Client.WindowsUdpBug();
@@ -74,9 +74,9 @@ namespace cmonitor.server
{ {
dic.Add(item, new BroadcastEndpointInfo dic.Add(item, new BroadcastEndpointInfo
{ {
Web = config.Server.WebPort, Web = config.Data.Server.WebPort,
Api = config.Server.ApiPort, Api = config.Data.Server.ApiPort,
Service = config.Server.ServicePort Service = config.Data.Server.ServicePort
}); });
} }

View File

@@ -0,0 +1,14 @@
namespace cmonitor.config
{
public sealed partial class ConfigInfo
{
public ConfigServerInfo Server { get; set; } = new ConfigServerInfo();
}
public sealed partial class ConfigServerInfo
{
public int WebPort { get; set; } = 1800;
public string WebRoot { get; set; } = "./web/";
public int ApiPort { get; set; } = 1801;
public int ServicePort { get; set; } = 1802;
}
}

View File

@@ -8,18 +8,23 @@ namespace cmonitor.startup
public static class StartupTransfer public static class StartupTransfer
{ {
static List<IStartup> startups; static List<IStartup> startups;
public static void Init() public static void Init(Config config)
{ {
startups = ReflectionHelper.GetInterfaceSchieves(typeof(IStartup)).Select(c => Activator.CreateInstance(c) as IStartup).ToList(); var types = ReflectionHelper.GetInterfaceSchieves(typeof(IStartup));
if (config.Data.Common.PluginNames.Length > 0)
{
types = types.Where(c => config.Data.Common.PluginNames.Any(d => c.FullName.Contains(d)));
}
startups = types.Select(c => Activator.CreateInstance(c) as IStartup).ToList();
} }
public static void Add(ServiceCollection serviceCollection, Config config, Assembly[] assemblies) public static void Add(ServiceCollection serviceCollection, Config config, Assembly[] assemblies)
{ {
foreach (var startup in startups) foreach (var startup in startups)
{ {
if (config.Common.Modes.Contains("client")) if (config.Data.Common.Modes.Contains("client"))
startup.AddClient(serviceCollection, config, assemblies); startup.AddClient(serviceCollection, config, assemblies);
if (config.Common.Modes.Contains("server")) if (config.Data.Common.Modes.Contains("server"))
startup.AddServer(serviceCollection, config, assemblies); startup.AddServer(serviceCollection, config, assemblies);
} }
} }
@@ -27,9 +32,9 @@ namespace cmonitor.startup
{ {
foreach (var startup in startups) foreach (var startup in startups)
{ {
if (config.Common.Modes.Contains("client")) if (config.Data.Common.Modes.Contains("client"))
startup.UseClient(serviceProvider, config, assemblies); startup.UseClient(serviceProvider, config, assemblies);
if (config.Common.Modes.Contains("server")) if (config.Data.Common.Modes.Contains("server"))
startup.UseServer(serviceProvider, config, assemblies); startup.UseServer(serviceProvider, config, assemblies);
} }
} }

View File

@@ -26,7 +26,7 @@ namespace cmonitor.web
{ {
HttpListener http = new HttpListener(); HttpListener http = new HttpListener();
http.IgnoreWriteExceptions = true; http.IgnoreWriteExceptions = true;
http.Prefixes.Add($"http://+:{config.Server.WebPort}/"); http.Prefixes.Add($"http://+:{config.Data.Server.WebPort}/");
http.Start(); http.Start();
http.BeginGetContext(Callback, http); http.BeginGetContext(Callback, http);
@@ -53,7 +53,7 @@ namespace cmonitor.web
if (path == "/") path = "index.html"; if (path == "/") path = "index.html";
path = Path.Join(config.Server.WebRoot, path); path = Path.Join(config.Data.Server.WebRoot, path);
if (File.Exists(path)) if (File.Exists(path))
{ {
byte[] bytes = File.ReadAllBytes(path); byte[] bytes = File.ReadAllBytes(path);

View File

@@ -25,7 +25,7 @@ namespace cmonitor.web
{ {
IWebServer webServer = serviceProvider.GetService<IWebServer>(); IWebServer webServer = serviceProvider.GetService<IWebServer>();
webServer.Start(); webServer.Start();
Logger.Instance.Info($"web listen:{config.Server.WebPort}"); Logger.Instance.Info($"web listen:{config.Data.Server.WebPort}");
} }
} }
} }

View File

@@ -30,6 +30,8 @@ namespace common.libs
} }
private static void Windows(string fileName) private static void Windows(string fileName)
{
try
{ {
string content = $@"@echo off string content = $@"@echo off
cd ""%CD%"" cd ""%CD%""
@@ -53,5 +55,9 @@ cmd /c netsh advfirewall firewall add rule name=""{fileName}"" dir=in action=all
CommandHelper.Execute("firewall.bat", string.Empty, new string[0]); CommandHelper.Execute("firewall.bat", string.Empty, new string[0]);
System.IO.File.Delete("firewall.bat"); System.IO.File.Delete("firewall.bat");
} }
catch (Exception)
{
}
}
} }
} }

View File

@@ -10,6 +10,7 @@ using System.Runtime.Serialization;
using System.Security.Principal; using System.Security.Principal;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static cmonitor.libs.winapis.WTSAPI32;
using static common.libs.winapis.ADVAPI32; using static common.libs.winapis.ADVAPI32;
using static common.libs.winapis.Kernel32; using static common.libs.winapis.Kernel32;
using static common.libs.winapis.NetApi32; using static common.libs.winapis.NetApi32;
@@ -397,6 +398,24 @@ namespace common.libs.winapis
return currentUsername == "NT AUTHORITY\\SYSTEM"; return currentUsername == "NT AUTHORITY\\SYSTEM";
} }
public static string GetUserName()
{
// 获取活动的控制台会话 ID
uint sessionId = WTSGetActiveConsoleSessionId();
IntPtr buffer;
uint bytesReturned;
string userName = "";
// 获取用户名
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTS_INFO_CLASS.WTSUserName, out buffer, out bytesReturned))
{
userName = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
}
return userName;
}
public static void SetHandleBlockKill(IntPtr handle) public static void SetHandleBlockKill(IntPtr handle)
{ {