// This file is part of the EonaCat project(s) which is released under the Apache License. // See the LICENSE file or go to https://EonaCat.com/license for full license details. using EonaCat.Connections.Models; using EonaCat.Connections.Processors; using System.Reflection; using System.Text; namespace EonaCat.Connections.Client.Example { public class Program { private const bool UseProcessor = true; private const bool IsHeartBeatEnabled = false; private static List _clients = new List(); private static long clientCount = 1; private static long clientsConnected = 0; private static string _jsonContent; //public static string SERVER_IP = "10.40.11.22"; public static string SERVER_IP = "127.0.0.1"; private static Dictionary> _clientsProcessors = new Dictionary>(); private static bool testDataProcessor; public static bool WaitForMessage { get; private set; } public static bool PressEnterForNextMessage { get; private set; } public static bool ToConsoleOnly { get; private set; } = true; public static bool UseJson { get; private set; } = true; public static bool TESTBYTES { get; private set; } public static bool UseJsonProcessorTest { get; private set; } = false; public static async Task Main(string[] args) { for (long i = 0; i < clientCount; i++) { var clientName = $"User {i}"; var client = await CreateClientAsync().ConfigureAwait(false); _clients.Add(client); if (testDataProcessor) { _clientsProcessors[client] = new JsonDataProcessor(); } else { _ = StartClientAsync(clientName, client); } } while (true) { string message = string.Empty; if (WaitForMessage) { Console.Write("Enter message to send (or 'exit' to quit): "); message = Console.ReadLine(); } if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase)) { foreach (var client in _clients) { await client.DisconnectClientAsync().ConfigureAwait(false); } break; } var jsonUrl = "https://samples.json-format.com/employees/json/employees_500KB.json"; try { if (string.IsNullOrEmpty(_jsonContent) && UseJson) { using var httpClient = new HttpClient(); _jsonContent = await httpClient.GetStringAsync(jsonUrl); var jsonSize = Encoding.UTF8.GetByteCount(_jsonContent); WriteToLog($"Using large JSON file (size: {jsonSize / 1024 / 1024} MB)"); } if (UseJson) { message = _jsonContent; } if (UseJsonProcessorTest) { foreach (var client in _clients) { var processor = _clientsProcessors[client]; processor.OnProcessTextMessage += (sender, e) => { WriteToLog($"Processed message from {e.ClientName}: {e.Text}"); }; processor.OnProcessMessage += (sender, e) => { WriteToLog($"Processed JSON message from {e.ClientName} ({e.ClientEndpoint}): {e.RawData}"); }; processor.MaxAllowedBufferSize = 10 * 1024 * 1024; // 10 MB processor.MaxMessagesPerBatch = 5; var json = _jsonContent; while (true) { processor.Process(json, "TestClient"); await Task.Delay(100).ConfigureAwait(false); } } } } catch (Exception exception) { WriteToLog($"Failed to download large JSON file: {exception.Message}"); } if (!string.IsNullOrEmpty(message)) { foreach (var client in _clients) { if (TESTBYTES) { var bytes = new byte[] { 0x00, 0x04, 0x31, 0x32, 0x30, 0x30 }; await client.SendAsync(bytes).ConfigureAwait(false); } else { await client.SendAsync(message).ConfigureAwait(false); } } } await Task.Delay(1000).ConfigureAwait(false); } } private static async Task StartClientAsync(string clientName, NetworkClient client) { await client.ConnectAsync().ConfigureAwait(false); // Send nickname await client.SendNicknameAsync(clientName); // Send a message await client.SendAsync($"Hello server, my name is {clientName}!"); } private static async Task CreateClientAsync() { var config = new Configuration { Protocol = ProtocolType.TCP, Host = SERVER_IP, Port = 1111, UseSsl = false, UseAesEncryption = false, EnableHeartbeat = IsHeartBeatEnabled, AesPassword = "EonaCat.Connections.Password", Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("client.pfx", "p@ss"), }; var client = new NetworkClient(config); // Subscribe to events client.OnConnected += (sender, e) => { WriteToLog($"Connected to server at {e.RemoteEndPoint}"); Console.WriteLine($"Connected to server at {e.RemoteEndPoint}"); Console.Title = $"Total clients {++clientsConnected}"; }; if (UseProcessor) { _clientsProcessors[client] = new JsonDataProcessor(); _clientsProcessors[client].OnError += (sender, e) => { Console.WriteLine($"Processor error: {e.Message}"); }; _clientsProcessors[client].OnMessageError += (sender, e) => { Console.WriteLine($"Processor message error: {e.Message}"); }; _clientsProcessors[client].OnProcessTextMessage += (sender, e) => { Console.WriteLine($"Processed text message from {e.ClientName}: {e.Text}"); }; _clientsProcessors[client].OnProcessMessage += (sender, e) => { ProcessMessage(e.RawData, e.ClientName, e.ClientEndpoint ?? "Unknown endpoint"); }; } client.OnDataReceived += (sender, e) => { if (UseProcessor) { _clientsProcessors[client].Process(e, currentClientName: e.Nickname); return; } else { WriteToLog($"Server says: {(e.IsBinary ? $"{e.Data.Length} bytes" : "We got a json message")}"); Console.WriteLine($"{e.StringData}"); if (PressEnterForNextMessage) { Console.ReadKey(); } } }; client.OnDisconnected += (sender, e) => { var message = string.Empty; if (e.Reason == DisconnectReason.LocalClosed) { if (e.Exception != null) { message = $"Disconnected from server (local close). Exception: {e.Exception.Message}"; } else { message = "Disconnected from server (local close)."; } } else { if (e.Exception != null) { message = $"Disconnected from server (remote close). Reason: {e.Reason}. Exception: {e.Exception.Message}"; } else { message = $"Disconnected from server (remote close). Reason: {e.Reason}"; } } WriteToLog(message); Console.WriteLine(message); Console.Title = $"Total clients {--clientsConnected}"; }; client.OnEncryptionError += Client_OnEncryptionError; client.OnGeneralError += Client_OnGeneralError; client.OnSslError += Client_OnSslError; return client; } private static void ProcessMessage(object message, string clientName, string remoteEndpoint) { WriteToLog($"Processed message from {clientName} ({remoteEndpoint}): {message}"); if (PressEnterForNextMessage) { Console.ReadKey(); } } private static void Client_OnSslError(object? sender, EventArguments.ErrorEventArgs e) { WriteToLog($"SSL error: {e.Message} => {e.Exception?.Message} => {e.Nickname}"); } private static void Client_OnGeneralError(object? sender, EventArguments.ErrorEventArgs e) { WriteToLog($"General error: {e.Message} => {e.Exception?.Message} => {e.Nickname}"); } private static void Client_OnEncryptionError(object? sender, EventArguments.ErrorEventArgs e) { WriteToLog($"Encryption error: {e.Message} => {e.Exception?.Message} => {e.Nickname}"); } public static void WriteToLog(string message) { try { if (ToConsoleOnly) { var dateTimeNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); Console.WriteLine($"{dateTimeNow}: {message}"); return; } var logFilePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "client_log.txt"); var logMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}{Environment.NewLine}"; if (!File.Exists(logFilePath)) { File.WriteAllText(logFilePath, logMessage); return; } if (new FileInfo(logFilePath).Length > 5 * 1024 * 1024) // 5 MB { var archiveFilePath = Path.Combine(Path.GetDirectoryName(logFilePath) ?? ".", $"client_log_{DateTime.Now:yyyyMMdd_HHmmss}.txt"); File.Move(logFilePath, archiveFilePath); File.WriteAllText(logFilePath, logMessage); return; } File.AppendAllText(logFilePath, logMessage); } catch { // Ignore logging errors } } } }