using System.Text.Json; using ClientTester.DummyService; using ClientTester.EchoDummyService; using ClientTester.StressTest; using ClientTester.Utils; using Serilog; class EcoClientTester { public static string SERVER_IP = "localhost"; public static int SERVER_PORT = 9500; public static string CONNECTION_KEY = ""; public static int CLIENT_COUNT = 50; private static void LoadConfig() { string path = Path.Combine(AppContext.BaseDirectory, "appsettings.json"); if (!File.Exists(path)) return; using JsonDocument doc = JsonDocument.Parse(File.ReadAllText(path)); JsonElement root = doc.RootElement; if (root.TryGetProperty("ServerIp", out JsonElement ip)) SERVER_IP = ip.GetString() ?? SERVER_IP; if (root.TryGetProperty("ServerPort", out JsonElement port)) SERVER_PORT = port.GetInt32(); if (root.TryGetProperty("ConnectionKey", out JsonElement key)) CONNECTION_KEY = key.GetString() ?? CONNECTION_KEY; if (root.TryGetProperty("ClientCount", out JsonElement count)) CLIENT_COUNT = count.GetInt32(); } private async Task StartEchoDummyTest() { try { CancellationTokenSource cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, e) => { e.Cancel = true; Log.Warning("[SHUTDOWN] Ctrl+C 감지, 종료 중..."); cts.Cancel(); }; EchoDummyClientService service = new EchoDummyClientService(CLIENT_COUNT, SERVER_IP, SERVER_PORT, CONNECTION_KEY, 100); service.OnAllDisconnected += () => { Log.Warning("[SHUTDOWN] 종료 이벤트 발생, 종료 중..."); cts.Cancel(); }; // service.IsTest = true; // service.TestCount = 100; await service.RunAsync(cts.Token); service.PrintStats(); service.Stop(); await Log.CloseAndFlushAsync(); } catch (Exception e) { Log.Error($"[SHUTDOWN] 예외 발생 : {e}"); } } private async Task StartDummyTest() { try { CancellationTokenSource cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, e) => { e.Cancel = true; Log.Warning("[SHUTDOWN] Ctrl+C 감지, 종료 중..."); cts.Cancel(); }; DummyClientService service = new DummyClientService(CLIENT_COUNT, SERVER_IP, SERVER_PORT, CONNECTION_KEY, 100); service.OnAllDisconnected += () => { Log.Warning("[SHUTDOWN] 종료 이벤트 발생, 종료 중..."); cts.Cancel(); }; await service.RunAsync(cts.Token); service.PrintStats(); service.Stop(); await Log.CloseAndFlushAsync(); } catch (Exception e) { Log.Error($"[SHUTDOWN] 예외 발생 : {e}"); } } private async Task StartStressTest(int duration, int sendInterval, int rampInterval, int clientsPerRamp) { try { CancellationTokenSource cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, e) => { e.Cancel = true; Log.Warning("[SHUTDOWN] Ctrl+C 감지, 종료 중..."); cts.Cancel(); }; StressTestService service = new StressTestService( CLIENT_COUNT, SERVER_IP, SERVER_PORT, CONNECTION_KEY, durationSec: duration, sendIntervalMs: sendInterval, rampUpIntervalMs: rampInterval, clientsPerRamp: clientsPerRamp); service.OnTestCompleted += () => { Log.Information("[STRESS] 테스트 시간 종료."); }; try { await service.RunAsync(cts.Token); } catch (OperationCanceledException) { } service.PrintFinalReport(); string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string csvPath = $"results/stress_{CLIENT_COUNT}clients_{timestamp}.csv"; Directory.CreateDirectory("results"); service.ExportCsv(csvPath); service.Stop(); await Log.CloseAndFlushAsync(); } catch (Exception e) { Log.Error($"[SHUTDOWN] 예외 발생 : {e}"); } } private static void PrintUsage() { Console.WriteLine(); Console.WriteLine("사용법:"); Console.WriteLine(" EchoClientTester 대화형 모드"); Console.WriteLine(" EchoClientTester stress [옵션] 스트레스 테스트"); Console.WriteLine(); Console.WriteLine("스트레스 테스트 옵션:"); Console.WriteLine(" -c, --clients <수> 클라이언트 수 (기본: 50)"); Console.WriteLine(" -d, --duration <초> 테스트 시간 (기본: 60, 0=무제한)"); Console.WriteLine(" -i, --interval 전송 주기 (기본: 100)"); Console.WriteLine(" -r, --ramp Ramp-up 간격 (기본: 1000)"); Console.WriteLine(" -b, --batch <수> Ramp-up 당 클라이언트 수 (기본: 10)"); Console.WriteLine(" --ip <주소> 서버 IP (기본: localhost)"); Console.WriteLine(" --port <포트> 서버 포트 (기본: 9500)"); Console.WriteLine(); Console.WriteLine("예시:"); Console.WriteLine(" EchoClientTester stress -c 100 -d 120"); Console.WriteLine(" EchoClientTester stress -c 200 -d 60 -r 500 -b 20 --ip 192.168.0.10"); } private static async Task Main(string[] args) { // 유니코드 문자(═, ║ 등) 콘솔 깨짐 방지 Console.OutputEncoding = System.Text.Encoding.UTF8; // appsettings.json 에서 설정 로드 LoadConfig(); // 크래시 덤프 핸들러 (Release: .log + .dmp / Debug: .log) CrashDumpHandler.Register(); // .MinimumLevel.Warning() // Warning 이상만 출력 배포시 string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File($"logs2/log_{timestamp}.txt") .CreateLogger(); EcoClientTester tester = new EcoClientTester(); // CLI 모드: stress 명령 if (args.Length > 0 && args[0].Equals("stress", StringComparison.OrdinalIgnoreCase)) { // 기본값 // CLIENT_COUNT = 50; int duration = 60; int sendInterval = 100; int rampInterval = 1000; int clientsPerRamp = 10; for (int i = 1; i < args.Length; i++) { switch (args[i]) { case "-c": case "--clients": if (i + 1 < args.Length) CLIENT_COUNT = int.Parse(args[++i]); break; case "-d": case "--duration": if (i + 1 < args.Length) duration = int.Parse(args[++i]); break; case "-i": case "--interval": if (i + 1 < args.Length) sendInterval = int.Parse(args[++i]); break; case "-r": case "--ramp": if (i + 1 < args.Length) rampInterval = int.Parse(args[++i]); break; case "-b": case "--batch": if (i + 1 < args.Length) clientsPerRamp = int.Parse(args[++i]); break; case "--ip": if (i + 1 < args.Length) SERVER_IP = args[++i]; break; case "--port": if (i + 1 < args.Length) SERVER_PORT = int.Parse(args[++i]); break; case "-h": case "--help": PrintUsage(); return; } } await tester.StartStressTest(duration, sendInterval, rampInterval, clientsPerRamp); return; } // CLI 모드: help if (args.Length > 0 && (args[0] == "-h" || args[0] == "--help")) { PrintUsage(); return; } // 대화형 모드 (기존) Log.Information("========== 더미 클라 테스터 =========="); Log.Information("1. 에코 서버"); Log.Information("2. 더미 클라(이동만)"); Log.Information("3. 스트레스 테스트 (부하)"); Log.Information("===================================="); Log.Information("1 / 2 / 3 : "); string? input = Console.ReadLine(); if (!int.TryParse(input, out int choice) || choice < 1 || choice > 3) { Log.Warning("1, 2, 3 중 입력하세요."); return; } if (choice == 1) { await tester.StartEchoDummyTest(); } else if (choice == 2) { await tester.StartDummyTest(); } else if (choice == 3) { // 대화형 스트레스 테스트 설정 Log.Information("클라이언트 수 (기본 50): "); string? countInput = Console.ReadLine(); CLIENT_COUNT = string.IsNullOrEmpty(countInput) ? 50 : int.Parse(countInput); Log.Information("테스트 시간(초) (기본 60, 0=무제한): "); string? durInput = Console.ReadLine(); int dur = string.IsNullOrEmpty(durInput) ? 60 : int.Parse(durInput); await tester.StartStressTest(dur, 100, 1000, 10); } } }