style: 将连接抽象为一个单独的模块,符合单一职责原则,增加了系统的可扩展性
- 好的设计模式也将减少代码
This commit is contained in:
parent
62366f0d97
commit
8d1e08a370
8 changed files with 202 additions and 196 deletions
103
Server/Common/ConnectPipeline.cs
Normal file
103
Server/Common/ConnectPipeline.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Common;
|
||||
|
||||
public abstract class AbsPipeLine
|
||||
{
|
||||
public abstract IAsyncEnumerable<int> Work(Func<byte[], bool> receiveCb, string addr = "");
|
||||
protected Func<byte[], bool> ReceiveMsg = (byte[] a) =>
|
||||
{
|
||||
return true;
|
||||
};
|
||||
protected abstract Task Listen(Func<byte[], bool> receiveCb);
|
||||
|
||||
/// <summary>
|
||||
/// 关闭连接
|
||||
/// </summary>
|
||||
/// <param name="CloseReason"></param>
|
||||
/// <returns></returns>
|
||||
public abstract Task Close(string? CloseReason);
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns></returns>
|
||||
public abstract Task SendMsg(SyncMsg msg);
|
||||
}
|
||||
|
||||
public class WebSocPipeLine<TSocket>(TSocket socket) : AbsPipeLine
|
||||
where TSocket : WebSocket
|
||||
{
|
||||
public readonly TSocket Socket = socket;
|
||||
|
||||
public override async IAsyncEnumerable<int> Work(
|
||||
Func<byte[], bool> receiveCb,
|
||||
string addr = ""
|
||||
)
|
||||
{
|
||||
if (Socket is ClientWebSocket CSocket)
|
||||
{
|
||||
//连接失败会抛出异常
|
||||
await CSocket.ConnectAsync(new Uri(addr), CancellationToken.None);
|
||||
yield return 0;
|
||||
}
|
||||
// 从controller 来,这个已经连接上了
|
||||
else if (Socket is WebSocket)
|
||||
{
|
||||
yield return 0;
|
||||
}
|
||||
await Listen(receiveCb);
|
||||
yield return 1;
|
||||
}
|
||||
|
||||
protected override async Task Listen(Func<byte[], bool> receiveCb)
|
||||
{
|
||||
//最大1MB!=
|
||||
var buffer = new byte[1024 * 1024];
|
||||
|
||||
while (Socket.State == WebSocketState.Open)
|
||||
{
|
||||
var receiveResult = await Socket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
throw new Exception(receiveResult.CloseStatusDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nbuffer = new byte[receiveResult.Count];
|
||||
System.Buffer.BlockCopy(buffer, 0, nbuffer, 0, receiveResult.Count);
|
||||
receiveCb(nbuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Close(string? CloseReason)
|
||||
{
|
||||
if (Socket.State == WebSocketState.Open)
|
||||
{
|
||||
await Socket.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
CloseReason,
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task SendMsg(SyncMsg msg)
|
||||
{
|
||||
string msgStr = JsonSerializer.Serialize(msg);
|
||||
await Socket.SendAsync(
|
||||
new ArraySegment<byte>(Encoding.UTF8.GetBytes(msgStr)),
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Common;
|
||||
using System.Net.WebSockets;
|
||||
namespace LocalServer.Controllers
|
||||
{
|
||||
public class LocalServerController(LocalSyncServerFactory factory) : ControllerBase
|
||||
|
@ -16,7 +17,8 @@ namespace LocalServer.Controllers
|
|||
try
|
||||
{
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
Factory.CreateLocalSyncServer(webSocket, Name);
|
||||
var pipeLine = new WebSocPipeLine<WebSocket>(webSocket);
|
||||
Factory.CreateLocalSyncServer(pipeLine, Name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -14,34 +14,26 @@ public class LocalSyncServer
|
|||
|
||||
public Config? SyncConfig;
|
||||
|
||||
public Config NotNullSyncConfig {get {
|
||||
public Config NotNullSyncConfig
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SyncConfig == null)
|
||||
{
|
||||
throw new ArgumentNullException("SyncConfig");
|
||||
}
|
||||
return 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;
|
||||
public readonly AbsPipeLine LocalPipe;
|
||||
|
||||
public readonly AbsPipeLine RemotePipe = new WebSocPipeLine<ClientWebSocket>(
|
||||
new ClientWebSocket()
|
||||
);
|
||||
/// <summary>
|
||||
/// 发布名称
|
||||
/// </summary>
|
||||
|
@ -52,148 +44,39 @@ public class LocalSyncServer
|
|||
/// </summary>
|
||||
public readonly LocalSyncServerFactory Factory;
|
||||
|
||||
public LocalSyncServer(WebSocket socket, string name, LocalSyncServerFactory factory)
|
||||
public LocalSyncServer(AbsPipeLine pipe, string name, LocalSyncServerFactory factory)
|
||||
{
|
||||
LocalSocket = socket;
|
||||
LocalPipe = pipe;
|
||||
Name = name;
|
||||
Factory = factory;
|
||||
StateHelper = new ConnectAuthorityHelper(this);
|
||||
}
|
||||
|
||||
public async Task RemoteSocketConnect()
|
||||
Task.Run(async () =>
|
||||
{
|
||||
if (SyncConfig != null)
|
||||
var rs = LocalPipe.Work(
|
||||
(byte[] b) =>
|
||||
{
|
||||
await RemoteSocket.ConnectAsync(
|
||||
new Uri(SyncConfig.RemoteUrl + "/websoc"),
|
||||
CancellationToken.None
|
||||
return StateHelper.ReceiveLocalMsg(b);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("SyncConfig is null!");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoteSocketLiten()
|
||||
{
|
||||
string CloseMsg = "任务结束关闭";
|
||||
try
|
||||
{
|
||||
while (RemoteSocket.State == WebSocketState.Open)
|
||||
{
|
||||
var buffer = new byte[1024 * 1024];
|
||||
var receiveResult = await RemoteSocket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
CancellationToken.None
|
||||
);
|
||||
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
Close(receiveResult.CloseStatusDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nbuffer = new byte[receiveResult.Count];
|
||||
System.Buffer.BlockCopy(buffer, 0, nbuffer, 0, receiveResult.Count);
|
||||
StateHelper.ReceiveRemoteMsg(nbuffer);
|
||||
}
|
||||
}
|
||||
await foreach (var r in rs) { }
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CloseMsg = e.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Close(CloseMsg);
|
||||
Close(e.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LocalSocketListen()
|
||||
{
|
||||
string CloseMsg = "任务结束关闭";
|
||||
try
|
||||
{
|
||||
//最大1MB!=
|
||||
var buffer = new byte[1024 * 1024];
|
||||
|
||||
while (LocalSocket.State == WebSocketState.Open)
|
||||
{
|
||||
var receiveResult = await LocalSocket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
Close(receiveResult.CloseStatusDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateHelper.ReceiveLocalMsg(
|
||||
Encoding.UTF8.GetString(buffer, 0, receiveResult.Count)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CloseMsg = e.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Close(CloseMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task 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 Task 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(string? CloseReason)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LocalSocket.State == WebSocketState.Open)
|
||||
{
|
||||
LocalSocket
|
||||
.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
CloseReason,
|
||||
CancellationToken.None
|
||||
)
|
||||
.Wait(60 * 1000);
|
||||
}
|
||||
|
||||
if (RemoteSocket.State == WebSocketState.Open)
|
||||
{
|
||||
RemoteSocket
|
||||
.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
CloseReason,
|
||||
CancellationToken.None
|
||||
)
|
||||
.Wait(60 * 1000);
|
||||
}
|
||||
LocalPipe.Close(CloseReason);
|
||||
RemotePipe.Close(CloseReason);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
using System.Net.WebSockets;
|
||||
|
||||
using Common;
|
||||
namespace LocalServer;
|
||||
|
||||
public class LocalSyncServerFactory
|
||||
{
|
||||
private readonly object Lock = new();
|
||||
|
||||
public void CreateLocalSyncServer(WebSocket socket, string name)
|
||||
public void CreateLocalSyncServer(AbsPipeLine pipeLine, string name)
|
||||
{
|
||||
if (Servers.Select(x => x.Name == name).Any())
|
||||
{
|
||||
throw new Exception("LocalServer:存在同名发布源!");
|
||||
}
|
||||
var server = new LocalSyncServer(socket, name, this);
|
||||
var server = new LocalSyncServer(pipeLine, name, this);
|
||||
lock (Lock)
|
||||
{
|
||||
Servers.Add(server);
|
||||
}
|
||||
//脱离当前函数栈
|
||||
Task.Run(async ()=>{
|
||||
await server.LocalSocketListen();
|
||||
});
|
||||
}
|
||||
|
||||
private readonly List<LocalSyncServer> Servers = [];
|
||||
|
|
|
@ -32,7 +32,7 @@ public abstract class StateHelpBase(
|
|||
return new SyncMsg(type, Step, body);
|
||||
}
|
||||
|
||||
public void ReceiveLocalMsg(string msg)
|
||||
public bool ReceiveLocalMsg(byte[] msg)
|
||||
{
|
||||
var syncMsg =
|
||||
JsonSerializer.Deserialize<SyncMsg>(msg)
|
||||
|
@ -42,9 +42,10 @@ public abstract class StateHelpBase(
|
|||
throw new Exception("Sync step error!");
|
||||
}
|
||||
HandleLocalMsg(syncMsg);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ReceiveRemoteMsg(byte[] bytes)
|
||||
public bool ReceiveRemoteMsg(byte[] bytes)
|
||||
{
|
||||
var msg = AESHelper.DecryptStringFromBytes_Aes(bytes);
|
||||
|
||||
|
@ -56,6 +57,7 @@ public abstract class StateHelpBase(
|
|||
throw new Exception("Sync step error!");
|
||||
}
|
||||
HandleLocalMsg(syncMsg);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void HandleRemoteMsg(SyncMsg msg);
|
||||
|
@ -74,7 +76,7 @@ public class ConnectAuthorityHelper(LocalSyncServer context)
|
|||
protected override void HandleRemoteMsg(SyncMsg msg)
|
||||
{
|
||||
//将remote的消息传递到前端界面
|
||||
Context.LocalSocketSendMsg(msg).Wait();
|
||||
Context.LocalPipe.SendMsg(msg);
|
||||
//下一步
|
||||
var deployHelper = new DeployHelper(Context);
|
||||
Context.StateHelper = deployHelper;
|
||||
|
@ -86,18 +88,24 @@ public class ConnectAuthorityHelper(LocalSyncServer context)
|
|||
//收到配置文件
|
||||
var config = JsonSerializer.Deserialize<Config>(msg.Body);
|
||||
Context.SyncConfig = config;
|
||||
Context.RemoteSocketConnect().Wait(60 * 1000);
|
||||
Task.Run(async () =>
|
||||
{
|
||||
if (Context.SyncConfig != null)
|
||||
var rs = Context.RemotePipe.Work(
|
||||
(byte[] b) =>
|
||||
{
|
||||
await Context.RemoteSocketSendMsg(CreateMsg(Context.SyncConfig.RemotePwd));
|
||||
}
|
||||
else
|
||||
return Context.StateHelper.ReceiveRemoteMsg(b);
|
||||
},
|
||||
Context.NotNullSyncConfig.RemoteUrl + "/websoc"
|
||||
);
|
||||
await foreach (var r in rs)
|
||||
{
|
||||
throw new NullReferenceException("Config is null!");
|
||||
if (r == 0)
|
||||
{
|
||||
await Context.RemotePipe.SendMsg(
|
||||
CreateMsg(Context.NotNullSyncConfig.RemotePwd)
|
||||
);
|
||||
}
|
||||
}
|
||||
await Context.RemoteSocketLiten();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +121,7 @@ public class DeployHelper(LocalSyncServer context)
|
|||
{
|
||||
if (Context.NotNullSyncConfig.IsDeployProject == false)
|
||||
{
|
||||
Context.LocalSocketSendMsg(CreateMsg("配置为不发布跳过此步骤")).Wait();
|
||||
Context.LocalPipe.SendMsg(CreateMsg("配置为不发布跳过此步骤")).Wait();
|
||||
var h = new DiffFileAndPackHelper(Context);
|
||||
Context.StateHelper = h;
|
||||
h.DiffProcess();
|
||||
|
@ -145,14 +153,14 @@ public class DeployHelper(LocalSyncServer context)
|
|||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
Context.LocalSocketSendMsg(CreateMsg("发布成功!")).Wait();
|
||||
Context.LocalPipe.SendMsg(CreateMsg("发布成功!")).Wait();
|
||||
var h = new DiffFileAndPackHelper(Context);
|
||||
Context.StateHelper = h;
|
||||
h.DiffProcess();
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.LocalSocketSendMsg(CreateErrMsg(output)).Wait();
|
||||
Context.LocalPipe.SendMsg(CreateErrMsg(output)).Wait();
|
||||
throw new Exception("执行发布错误,错误信息参考上一条消息!");
|
||||
}
|
||||
}
|
||||
|
@ -181,17 +189,13 @@ public class DiffFileAndPackHelper(LocalSyncServer context)
|
|||
});
|
||||
//将配置信息发送到remoteServer
|
||||
Context
|
||||
.RemoteSocketSendMsg(CreateMsg(JsonSerializer.Serialize(Context.NotNullSyncConfig)))
|
||||
.RemotePipe.SendMsg(CreateMsg(JsonSerializer.Serialize(Context.NotNullSyncConfig)))
|
||||
.Wait();
|
||||
}
|
||||
|
||||
protected override void HandleLocalMsg(SyncMsg msg) { }
|
||||
|
||||
protected override void HandleRemoteMsg(SyncMsg msg) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
protected override void HandleRemoteMsg(SyncMsg msg) { }
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using RemoteServer.Models;
|
||||
using System.Text;
|
||||
namespace RemoteServer.Controllers;
|
||||
|
||||
public class SyncFilesController(SqliteDbContext db) : ControllerBase
|
||||
public class SyncFilesController(RemoteSyncServerFactory factory, SqliteDbContext db) : ControllerBase
|
||||
{
|
||||
private readonly SqliteDbContext _db = db;
|
||||
private readonly RemoteSyncServerFactory Factory = factory;
|
||||
|
||||
[Route("/websoc")]
|
||||
public async Task WebsocketConnection(string Name)
|
||||
{
|
||||
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
Factory.CreateLocalSyncServer(webSocket, Name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
HttpContext.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(e.Message));
|
||||
HttpContext.Response.StatusCode = StatusCodes.Status406NotAcceptable;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("/GetSyncFilesLogs")]
|
||||
public IActionResult GetSyncFilesLogs(
|
|
@ -1,12 +1,25 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using RemoteServer;
|
||||
using RemoteServer.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
ConfigurationBuilder configurationBuilder = new();
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
//添加配置文件路径
|
||||
configurationBuilder
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json");
|
||||
|
||||
//加载文件
|
||||
IConfiguration _configuration = configurationBuilder.Build();
|
||||
RemoteSyncServer.TempRootFile = _configuration["TempDir"] ?? "C:/TempPack";
|
||||
;
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddDbContext<SqliteDbContext>(opions=>{
|
||||
builder.Services.AddDbContext<SqliteDbContext>(opions =>
|
||||
{
|
||||
opions.UseSqlite(builder.Configuration.GetConnectionString("DbPath"));
|
||||
});
|
||||
|
||||
|
@ -23,7 +36,7 @@ if (app.Environment.IsDevelopment())
|
|||
}
|
||||
app.UseWebSockets();
|
||||
app.Urls.Clear();
|
||||
app.Urls.Add("http://0.0.0.0:6828");
|
||||
app.Urls.Add("http://0.0.0.0:6818");
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Common;
|
||||
|
||||
|
@ -28,12 +26,6 @@ public class RemoteSyncServer
|
|||
/// </summary>
|
||||
public readonly WebSocket RemoteSocket;
|
||||
|
||||
/// <summary>
|
||||
/// 发布源-缓冲区,存储数据 最大1MB
|
||||
/// </summary>
|
||||
public byte[] Buffer = new byte[1024 * 1024];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发布开始时间
|
||||
/// </summary>
|
||||
|
@ -95,17 +87,6 @@ public class RemoteSyncServer
|
|||
}
|
||||
}
|
||||
|
||||
// public async Task LocalSocketSendMsg(object msgOb)
|
||||
// {
|
||||
// string msg = JsonSerializer.Serialize(msgOb);
|
||||
// await RemoteSocket.SendAsync(
|
||||
// new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)),
|
||||
// WebSocketMessageType.Text,
|
||||
// true,
|
||||
// CancellationToken.None
|
||||
// );
|
||||
// }
|
||||
|
||||
public async Task RemoteSocketSendMsg(object msgOb)
|
||||
{
|
||||
string msg = JsonSerializer.Serialize(msgOb);
|
||||
|
|
Loading…
Reference in a new issue