Added new projects of new RPC core: MyClient and MtServivce

This commit is contained in:
Viacheslav Demydiuk
2024-01-04 18:35:15 +02:00
parent 2a13699900
commit feb0192b11
24 changed files with 2078 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+180
View File
@@ -0,0 +1,180 @@
namespace MtClient
{
public enum MessageType
{
Command = 0,
Response = 1,
Event = 2,
ExpertList = 3,
ExpertAdded = 4,
ExpertRemoved = 5,
}
public abstract class MtMessage
{
public abstract MessageType MsgType { get; }
public string Serialize()
{
return $"{(int)MsgType};{GetMessageBody()}";
}
protected abstract string GetMessageBody();
}
public class MtCommand(int expertHandle, int commandType, int commandId, string payload) : MtMessage
{
public override MessageType MsgType => MessageType.Command;
public int ExpertHandle { private set; get; } = expertHandle;
public int CommandType { private set; get; } = commandType;
public int CommandId { private set; get; } = commandId;
public string Payload { private set; get; } = payload;
protected override string GetMessageBody()
{
return $"{ExpertHandle};{CommandId};{CommandType};{Payload}";
}
}
public class MtEvent(int expertHandle, int eventType, string payload) : MtMessage
{
public override MessageType MsgType => MessageType.Event;
public int ExpertHandle { private set; get; } = expertHandle;
public int EventType { private set; get; } = eventType;
public string Payload { private set; get; } = payload;
protected override string GetMessageBody()
{
throw new NotImplementedException();
}
public static MtMessage? Parse(string payload)
{
var pieces = payload.Split(";", 3);
if (pieces.Length == 3
&& int.TryParse(pieces[0], out int expertHandle)
&& int.TryParse(pieces[1], out int eventType))
return new MtEvent(expertHandle, eventType, payload);
return null;
}
}
public class MtExpertAddedMsg(int expertHandle) : MtMessage
{
public override MessageType MsgType => MessageType.ExpertAdded;
public int ExpertHandle { private set; get; } = expertHandle;
protected override string GetMessageBody()
{
throw new NotImplementedException();
}
public static MtMessage? Parse(string payload)
{
if (int.TryParse(payload, out int expertHandle) == false)
return null;
return new MtExpertAddedMsg(expertHandle);
}
}
public class MtExpertRemovedMsg(int expertHandle) : MtMessage
{
public override MessageType MsgType => MessageType.ExpertRemoved;
public int ExpertHandle { private set; get; } = expertHandle;
protected override string GetMessageBody()
{
throw new NotImplementedException();
}
public static MtMessage? Parse(string payload)
{
if (int.TryParse(payload, out int expertHandle) == false)
return null;
return new MtExpertRemovedMsg(expertHandle);
}
}
public class MtExpertListMsg(List<int> experts) : MtMessage
{
public override MessageType MsgType => MessageType.ExpertList;
public List<int> Experts { private set; get; } = experts;
protected override string GetMessageBody()
{
throw new NotImplementedException();
}
public static MtMessage? Parse(string payload)
{
var pieces = payload.Split(",");
List<int> handles = [];
foreach (var p in pieces)
{
if (int.TryParse(p, out int expertHandle) == false)
return null;
handles.Add(expertHandle);
}
return new MtExpertListMsg(handles);
}
}
public class MtResponse(int expertHandle, int commandId, string payload) : MtMessage
{
public override MessageType MsgType => MessageType.Response;
public int ExpertHandle { private set; get; } = expertHandle;
public int CommandId { private set; get; } = commandId;
public string Payload { private set; get; } = payload;
protected override string GetMessageBody()
{
throw new NotImplementedException();
}
public static MtMessage? Parse(string payload)
{
var pieces = payload.Split(";", 3);
if (pieces.Length != 3
|| string.IsNullOrEmpty(pieces[0])
|| string.IsNullOrEmpty(pieces[1])
|| string.IsNullOrEmpty(pieces[2]))
return null;
if (int.TryParse(pieces[0], out int expertHandle) == false)
return null;
if (int.TryParse(pieces[1], out int commandId) == false)
return null;
return new MtResponse(expertHandle, commandId, pieces[2]);
}
}
public static class MtMessageParser
{
static MtMessageParser()
{
msgHandlers_[MessageType.Event] = MtEvent.Parse;
msgHandlers_[MessageType.Response] = MtResponse.Parse;
msgHandlers_[MessageType.ExpertList] = MtExpertListMsg.Parse;
msgHandlers_[MessageType.ExpertAdded] = MtExpertAddedMsg.Parse;
msgHandlers_[MessageType.ExpertRemoved] = MtExpertRemovedMsg.Parse;
}
public static MtMessage? Parse(MessageType msgType, string payload)
{
return msgHandlers_[msgType](payload);
}
private static readonly Dictionary<MessageType, Func<string, MtMessage?>> msgHandlers_ = new();
}
}
+184
View File
@@ -0,0 +1,184 @@
using System.Net.WebSockets;
using System.Text;
namespace MtClient
{
public class MtRpcClient
{
public MtRpcClient(string host, int port)
{
host_ = host;
port_ = port;
receiveThread_ = new Thread(new ThreadStart(DoReceive));
sendThread_ = new Thread(new ThreadStart(DoWrite));
}
public async Task Connect()
{
Log($"Connect: started to {host_}:{port_}");
try
{
await ws_.ConnectAsync(new Uri($"ws://{host_}:{port_}/ws"), CancellationToken.None);
}
catch (Exception ex)
{
Log($"Connect failed: {ex.Message}");
throw new Exception($"Failed connection to {host_}:{port_}");
}
receiveThread_.Start();
sendThread_.Start();
Log($"Connect: success.");
}
public async void Disconnect()
{
Log($"Disconnect: {host_}:{port_}");
try
{
await ws_.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
ws_.Dispose();
}
catch (Exception ex)
{
Log($"Disconnect: {ex.Message}");
}
sendWaiter_.Set();
sendThread_.Join();
receiveThread_.Join();
Log($"Disconnect: success");
}
public void Send(MtMessage message)
{
pendingMessages_.Enqueue(message.Serialize());
sendWaiter_.Set();
}
private async void DoWrite()
{
while(ws_.State == WebSocketState.Open)
{
string? message = null;
lock(pendingMessages_)
{
if (pendingMessages_.Count > 0)
message = pendingMessages_.Dequeue();
}
if (message == null)
{
sendWaiter_.WaitOne();
continue;
}
try
{
Log($"DoWrite: sending message: {message}");
byte[] bytes = Encoding.ASCII.GetBytes(message);
await ws_.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);
}
catch (Exception e)
{
Log($"DoWrite: {e.Message}");
}
}
}
private async void DoReceive()
{
try
{
byte[] recvBuffer = new byte[64 * 1024];
while (ws_.State == WebSocketState.Open)
{
var result = await ws_.ReceiveAsync(new ArraySegment<byte>(recvBuffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
Log($"DoReceive: close signal {result.CloseStatusDescription}");
await ws_.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
break;
}
else
{
var msg = Encoding.ASCII.GetString(recvBuffer, 0, result.Count);
OnReceive(msg);
}
}
}
catch (Exception ex)
{
Log($"Exception in receive - {ex.Message}");
ConnectionFailed?.Invoke(this, EventArgs.Empty);
}
}
private void OnReceive(string msg)
{
Log($"OnReceive: {msg}");
if (string.IsNullOrEmpty(msg))
{
Log("OnReceive: Invalid message (null or empty)");
return;
}
var pieces = msg.Split(";", 2);
if (pieces.Length != 2
|| string.IsNullOrEmpty(pieces[0])
|| string.IsNullOrEmpty(pieces[1]))
{
Log("OnReceive: Invalid message format.");
return;
}
MessageType msgType;
try
{
var msgTypeValue = int.Parse(pieces[0]);
msgType = (MessageType)Enum.ToObject(typeof(MessageType), msgTypeValue);
}
catch (Exception e)
{
Log($"OnReceive: Parse MessageType failed. {e.Message}");
return;
}
var message = MtMessageParser.Parse(msgType, (pieces[1]));
if (message == null)
{
Log("OnReceive: Failed parse message payload");
return;
}
MessageReceived?.Invoke(this, message);
}
private void Log(string msg)
{
Console.WriteLine($"[{Environment.CurrentManagedThreadId}] {msg}");
}
public event EventHandler<MtMessage>? MessageReceived;
public event EventHandler<EventArgs>? ConnectionFailed;
private readonly ClientWebSocket ws_ = new();
private readonly string host_;
private readonly int port_;
private readonly byte[] buf_ = new byte[10000];
private readonly Dictionary<MessageType, Func<string, MtMessage?>> msgHandlers_ = new();
private readonly Queue<string> pendingMessages_ = [];
private readonly Thread receiveThread_;
private readonly Thread sendThread_;
private readonly EventWaitHandle sendWaiter_ = new AutoResetEvent(false);
}
}