Правильная отправка и чтение данных из NetworkStream. Многопоточность. С#

Помогите правильно отправить/принять данные через NetworkStream в многопоточном приложении(подобие чата).

Есть 3 главных класса: Client, Server, ServerClient.

Client

private readonly ServerClient _serverClient;

public Client() { }

public Client(string localHostIp, int localHostPort, int id, string nickname)
{
    _serverClient = new ServerClient(localHostIp, localHostPort);
    ID = id;
    Nickname = nickname;
}

public int ID { get; private set; }

public string Nickname { get; private set; }
        
public void SendMessage(string message)
{
    MessageStruct clientMessage = new MessageStruct(Guid.NewGuid().ToString(), message, DateTime.Now);
    _serverClient.SendMessage(this, clientMessage);
}

public void AddSubToEvent(Action<Client, MessageStruct> action) => _serverClient.MessageRecived += action;

public void RemvodeSubToEvent(Action<Client, MessageStruct> action) => _serverClient.MessageRecived -= action;

Server

private TcpListener _server;
private List<TcpClient> _tcpClients;
private Thread _threadForListeningProcess;
public override event Action<Client, MessageStruct> MessageRecived;

public Server(string localHostIp, int localHostPort) : base(localHostIp, localHostPort)
{
    _server = new TcpListener(LocalHostIP, LocalHostPort);
    _server.Start();

    _tcpClients = new List<TcpClient>();

    _threadForListeningProcess = new Thread(ListeningProcess);
    _threadForListeningProcess.Start();
}

public void ListeningProcess()
{
    while (true)
    {
        TcpClient tcpClient = _server.AcceptTcpClient();
        _tcpClients.Add(tcpClient);

        ThreadForReceivingMessages = new Thread(MessageReceivingProcess);
        ThreadForReceivingMessages.Start(tcpClient);
    }
}

public void MessageReceivingProcess(dynamic tcpClient)
{
    using var client = tcpClient as TcpClient;
    NetworkStream = client?.GetStream();

    while (true)
    {
        GetMessage();
    }
}

private string GetMessage()
{
    var clientMessage = new MessageStruct();
    var client = new Client();
    do
    {
        if (NetworkStream.CanRead)
        {
            var data = new byte[StreamBufferSize];
            NetworkStream.Read(data, 0, data.Length);
            MyConverter.GetClientMessageFromBytes(data, ref clientMessage);

            Array.Clear(data, 0, data.Length);

            NetworkStream.Read(data, 0, data.Length);
            client = MyConverter.GetClientFromBytes(data);
        }
    }
    while (NetworkStream.DataAvailable);

    MessageRecived?.Invoke(client, clientMessage);
    return clientMessage.Message;
}

public void BroadcastMessage(string message)
{
    if(_tcpClients.Count > 0)
    {
        MessageStruct serverMessage = new MessageStruct(Guid.NewGuid().ToString(), message, DateTime.Now);
        byte[] data = MyConverter.GetBytesFromClientMessage(serverMessage);
        foreach (var client in _tcpClients)
        {
            client.GetStream().Write(data, 0, data.Length);
        }
        return;
    }
    throw new Exception("Сurrently there are no connected clients on the server");
}

ServerClient

private TcpClient _TcpClient;
private Client _client;
public override event Action<Client, MessageStruct> MessageRecived;

public ServerClient(string localHostIp, int localHostPort) : base(localHostIp, localHostPort)
{
    _TcpClient = new TcpClient();
    _TcpClient.Connect(LocalHostIP, LocalHostPort);

    NetworkStream = _TcpClient.GetStream();

    ThreadForReceivingMessages = new Thread(MessageReceivingProcess);
    ThreadForReceivingMessages.Start();
}

public void SendMessage(Client client, MessageStruct message)
{
    byte[] data = MyConverter.GetBytesFromClientMessage(message);
    _TcpClient?.GetStream().Write(data, 0, data.Length);

    Array.Clear(data, 0, data.Length);

    data = MyConverter.GetBytesFromClient(client);
    _TcpClient?.GetStream().Write(data, 0, data.Length);
}

private void MessageReceivingProcess()
{
    while (true)
    {
        var clientMessage = new MessageStruct();
        do
        {
            if (NetworkStream.CanRead)
            {
                var data = new byte[StreamBufferSize];
                NetworkStream.Read(data, 0, data.Length);
                MyConverter.GetClientMessageFromBytes(data, ref clientMessage);
            }
        }
        while (NetworkStream.DataAvailable);

        MessageRecived?.Invoke(_client, clientMessage);
    }
}

Прошу помочь с 2 моментами:

  1. В данный момент метод MessageReceivingProcess класса ServerClient работает не правильно, потому что я не знаю, как правильно этот класс должен знать с каким объектом класса Client он работает, чтобы при инициировании события он отправлял нужный параметр(сейчас там нигде не присваивающийся _client).
  2. Ну и выскакивает ошибка в методе класса MyConvert
public static void GetClientMessageFromBytes(byte[] bytes, ref MessageStruct message)
{
    int len = Marshal.SizeOf(message);
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.Copy(bytes, 0, ptr, len);
    message = (MessageStruct)Marshal.PtrToStructure(ptr, message.GetType());
    Marshal.FreeHGlobal(ptr);
}

Метод сам по себе написан корректно(это я уже тестил), но из-за того, что я не знаю как правильно нужно или синхронизировать доступ к этому методу потокам, или еще что-то сделать, выскакивает ошибка. Сразу скажу, что пишу такого рода приложение первый раз. Приложение не будет использоваться нигде, а нужно для хоть приблизительного понимая того, как бы это выглядело на случай реального использования. Все данные хранятся в памяти, на это не обращайте внимание, БД специально здесь не использую, так как цель немного иная. Прошу доходчиво помочь с косяками.


Ответы (0 шт):