feat: local server 添加连接

This commit is contained in:
zerlei 2024-07-31 17:14:07 +08:00
parent bff3e114fc
commit 02c162ee8f
8 changed files with 479 additions and 6 deletions

128
Server/Common/AES.cs Normal file
View file

@ -0,0 +1,128 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Common;
/// <summary>
/// 与目标服务器通信将会加密
/// </summary>
public class AESHelper
{
static readonly byte[] Key =
[
0x11,
0xF2,
0xAF,
0xCF,
0xFF,
0x8B,
0x4C,
0x7D,
0x23,
0x96,
0x1B,
0x32,
0x43,
0xA4,
0x55,
0xF6,
0x29,
0x1C,
0x1B,
0x92,
0x23,
0x44,
0xB5,
0xF6,
];
static readonly byte[] IV =
[
0xD1,
0xF7,
0xAB,
0xCA,
0xBC,
0x7B,
0x2C,
0x3D,
0xFA,
0xAA,
0xFC,
0xA8,
0x28,
0x19,
0x9C,
0xB6,
];
public static byte[] EncryptStringToBytes_Aes(string plainText)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException(nameof(plainText), "can't be null");
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using MemoryStream msEncrypt = new();
using CryptoStream csEncrypt = new(
msEncrypt,
encryptor,
CryptoStreamMode.Write
);
using (StreamWriter swEncrypt = new(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
public static string DecryptStringFromBytes_Aes(byte[] cipherText)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException(nameof(cipherText), "can't be null");
// Declare the string used to hold
// the decrypted text.
string plaintext = string.Empty;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using MemoryStream msDecrypt = new(cipherText);
using CryptoStream csDecrypt = new(msDecrypt, decryptor, CryptoStreamMode.Read);
using StreamReader srDecrypt = new(csDecrypt);
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
return plaintext;
}
}

7
Server/Common/Message.cs Normal file
View file

@ -0,0 +1,7 @@
namespace Common;
public class SyncMsg(bool isSuccess, string body)
{
public bool IsSuccess { get; set; } = isSuccess;
public string Body { get; set; } = body;
}

View file

@ -1,3 +1,4 @@
using System.Net.NetworkInformation;
using System.Text;
using Microsoft.AspNetCore.Mvc;
@ -28,6 +29,19 @@ namespace LocalServer.Controllers
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
[Route("/macaddr")]
public string GetMacAddress()
{
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
string macaddrs = "";
foreach (NetworkInterface nic in nics)
{
PhysicalAddress physicalAddress = nic.GetPhysicalAddress();
macaddrs += physicalAddress.ToString() + ";";
}
return macaddrs;
}
//TODO 是否在本地记载同步日志?
}
}

View file

@ -10,4 +10,8 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>

View file

@ -1,19 +1,108 @@
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using Common;
using LocalServer.Models;
namespace LocalServer;
public class LocalSyncServer
{
public readonly WebSocket Socket;
public StateHelpBase StateHelper;
public Config? SyncConfig;
/// <summary>
/// 发布源连接
/// </summary>
public readonly WebSocket LocalSocket;
/// <summary>
/// 发布源-缓冲区,存储数据 最大1MB
/// </summary>
public byte[] Buffer = new byte[1024 * 1024];
/// <summary>
/// 发布目标-连接
/// </summary>
public readonly ClientWebSocket RemoteSocket = new();
/// <summary>
/// 发布开始时间
/// </summary>
private readonly DateTime StartTime = DateTime.Now;
/// <summary>
/// 发布名称
/// </summary>
public readonly string Name;
/// <summary>
/// 父工程,用于释放资源
/// </summary>
public readonly LocalSyncServerFactory Factory;
public LocalSyncServer(WebSocket socket, string name, LocalSyncServerFactory factory)
{
Socket = socket;
LocalSocket = socket;
Name = name;
Factory = factory;
StateHelper = new LocalAuthorityState(this);
}
public async Task Start()
{
//最大1MB
var buffer = new byte[1024 * 1024];
var receiveResult = await LocalSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
CancellationToken.None
);
while (!receiveResult.CloseStatus.HasValue)
{
await LocalSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None
);
receiveResult = await LocalSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
CancellationToken.None
);
}
Factory.RemoveLocalSyncServer(this);
await LocalSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None
);
}
public async void LocalSocketSendMsg(object msgOb)
{
string msg = JsonSerializer.Serialize(msgOb);
await LocalSocket.SendAsync(
new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)),
WebSocketMessageType.Text,
true,
CancellationToken.None
);
}
public async void RemoteSocketSendMsg(object msgOb)
{
string msg = JsonSerializer.Serialize(msgOb);
var buffer = AESHelper.EncryptStringToBytes_Aes(msg);
await RemoteSocket.SendAsync(
buffer,
WebSocketMessageType.Text,
true,
CancellationToken.None
);
}
public void Close() { }
}

View file

@ -4,19 +4,29 @@ namespace LocalServer;
public class LocalSyncServerFactory
{
public void CreateLocalSyncServer(WebSocket socket, string name)
private readonly object Lock = new();
public async void CreateLocalSyncServer(WebSocket socket, string name)
{
if (Servers.Select(x => x.Name == name).Any())
{
throw new Exception("there already is a server with that name is Runing!");
}
Servers.Add(new LocalSyncServer(socket, name, this));
var server = new LocalSyncServer(socket, name, this);
lock (Lock)
{
Servers.Add(server);
}
await server.Start();
}
private readonly List<LocalSyncServer> Servers = [];
public void RemoveLocalSyncServer(LocalSyncServer server)
{
Servers.Remove(server);
lock (Lock)
{
Servers.Remove(server);
}
}
}

View file

@ -0,0 +1,32 @@
namespace LocalServer.Models;
public class DirFileConfig
{
/// <summary>
/// 本地-源根目录
/// </summary>
public required string LocalRootPath { get; set; }
/// <summary>
/// 远程-目标根目录
/// </summary>
public required string RemoteRootPath { get; set; }
/// <summary>
/// 排除的文件,它是根目录的相对路径
/// </summary>
public List<string>? ExcludeFiles { get; set; }
/// <summary>
/// 除此外全部忽略最高优先级若有值ExcludeFiles 将被忽略,它是根目录的相对路径
/// </summary>
public List<DirFileConfig>? CherryPickFiles { get; set; }
}
public class Config
{
public required string Name { get; set; }
public required string RemoteUrl { get; set; }
public List<DirFileConfig>? DirFileConfigs { get; set; }
}

View file

@ -0,0 +1,189 @@
using System.Net.NetworkInformation;
using System.Net.WebSockets;
using System.Runtime.Intrinsics.Arm;
using System.Security.AccessControl;
using System.Text;
using System.Text.Json;
using Common;
using LocalServer.Models;
namespace LocalServer;
// enum StateWhenMsg
// {
// Authority = 0,
// ConfigInfo = 1,
// LocalPackAndUpload = 2,
// RemoteUnPackAndRelease = 3,
// }
public abstract class StateHelpBase(LocalSyncServer context)
{
protected readonly LocalSyncServer Context = context;
public abstract void HandleRemoteMsg(SyncMsg? msg);
public abstract void HandleLocalMsg(SyncMsg? msg);
}
/// <summary>
/// 0. 发布源验证密码
/// </summary>
/// <param name="context"></param>
public class LocalAuthorityState(LocalSyncServer context) : StateHelpBase(context)
{
public override void HandleRemoteMsg(SyncMsg? msg)
{
throw new NotImplementedException("error usage!");
}
public override void HandleLocalMsg(SyncMsg? msg)
{
if (msg == null)
{
return;
}
else
{
string Pwd = msg.Body;
if (Pwd == "Xfs1%$@_fdYU.>>")
{
Context.LocalSocketSendMsg(new SyncMsg(true, "源服务密码校验成功!"));
Context.StateHelper = new WaitingConfigInfoState(Context);
}
else
{
throw new UnauthorizedAccessException("pwd error!");
}
}
}
}
/// <summary>
/// 1. 获取配置信息,它包含目标的服务器的配置信息
/// </summary>
/// <param name="context"></param>
public class WaitingConfigInfoState(LocalSyncServer context) : StateHelpBase(context)
{
public override void HandleRemoteMsg(SyncMsg? msg) { }
public override void HandleLocalMsg(SyncMsg? msg)
{
if (msg == null)
{
return;
}
else
{
string ConfigInfo = msg.Body;
Context.SyncConfig =
JsonSerializer.Deserialize<Config>(ConfigInfo)
?? throw new NullReferenceException("ConfigInfo is null");
var task = Context.RemoteSocket.ConnectAsync(
new Uri(Context.SyncConfig.RemoteUrl),
CancellationToken.None
);
if (task.Wait(10000))
{
if (Context.RemoteSocket.State == WebSocketState.Open)
{
var state = new RemoteAuthorityState(Context);
state.SendPwdToRemoteServer();
Context.StateHelper = state;
}
else
{
throw new Exception("connect remote server failed!");
}
}
else
{
throw new TimeoutException("connect remote server timeout");
}
}
}
}
/// <summary>
/// 2. 目标服务器权限校验
/// </summary>
/// <param name="context"></param>
public class RemoteAuthorityState(LocalSyncServer context) : StateHelpBase(context)
{
public override void HandleRemoteMsg(SyncMsg? msg)
{
if (msg == null)
{
return;
}
else { }
}
public override void HandleLocalMsg(SyncMsg? msg) { }
public void SendPwdToRemoteServer()
{
var authorityInfo = new
{
Pwd = "xfs@#123hd??1>>|12#4",
MacAdr = new LocalServer.Controllers.LocalServerController(
Context.Factory
).GetMacAddress()
};
Context.RemoteSocketSendMsg(authorityInfo);
}
}
/// <summary>
/// 3. 文件比较
/// </summary>
/// <param name="context"></param>
public class DirFilesDiffState(LocalSyncServer context) : StateHelpBase(context)
{
public override void HandleRemoteMsg(SyncMsg? msg)
{
if (msg == null)
{
return;
}
else { }
}
public override void HandleLocalMsg(SyncMsg? msg) { }
}
/// <summary>
/// 4. 本地打包并上传
/// </summary>
/// <param name="context"></param>
public class LocalPackAndUploadState(LocalSyncServer context) : StateHelpBase(context)
{
public override void HandleRemoteMsg(SyncMsg? msg)
{
if (msg == null)
{
return;
}
else { }
}
public override void HandleLocalMsg(SyncMsg? msg) { }
}
/// <summary>
/// 5. 目标服务器解包并发布
/// </summary>
/// <param name="context"></param>
public class RemoteUnPackAndReleaseState(LocalSyncServer context) : StateHelpBase(context)
{
public override void HandleRemoteMsg(SyncMsg? msg)
{
if (msg == null)
{
return;
}
else { }
}
public override void HandleLocalMsg(SyncMsg? msg) { }
}