using System.Net.Sockets; using System.Text; using CatLink.Models; using Microsoft.Extensions.Logging; namespace CatLink.Relay { public class ActiveClient { private readonly ILogger _logger; private readonly Socket _socket; private readonly StreamReader _reader; private readonly StreamWriter _writer; private readonly object _writeLock = new(); private readonly Thread _thread; private readonly Action _messageHandler; private readonly Action _disconnectHandler; public string ClientKey { get; } public uint StubIp { get; } public Dictionary TcpStreams { get; } = new(); public HashSet PendingStreams { get; } = new(); public long LastHeartbeat { get; set; } public ActiveClient( string clientKey, uint stubIp, Socket socket, Action messageHandler, Action disconnectHandler, ILogger logger) { ClientKey = clientKey; StubIp = stubIp; _socket = socket; _messageHandler = messageHandler; _disconnectHandler = disconnectHandler; _logger = logger; var stream = new NetworkStream(socket); _reader = new StreamReader(stream, Encoding.UTF8); _writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true }; LastHeartbeat = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); _thread = new Thread(HandleMessages) { IsBackground = true }; _thread.Start(); } private void HandleMessages() { try { while (_socket.Connected) { var line = _reader.ReadLine(); if (line == null) break; var msg = Msg.FromString(line); _messageHandler(this, msg); } } catch (Exception ex) { _logger.LogError(ex, "Error handling messages for client {ClientKey}", ClientKey); } finally { _disconnectHandler(this); } } public void Send(Msg msg) { lock (_writeLock) { try { _writer.WriteLine(msg.ToString()); } catch (Exception ex) { _logger.LogError(ex, "Error sending message to client {ClientKey}", ClientKey); Disconnect(); } } } public void Disconnect() { try { _socket.Close(); _reader.Dispose(); _writer.Dispose(); } catch (Exception ex) { _logger.LogError(ex, "Error disconnecting client {ClientKey}", ClientKey); } } } }