using System; using System.Text; using System.Threading; // 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. namespace EonaCat.QuicNet { /// /// EonaCat.QuicNet — Demo /// Starts a server and two clients to demonstrate various features of the library, including sending messages, groups, nicknames, and graceful disconnects. /// internal static class Demo { private static void Main() { Console.WriteLine("EonaCat QuicNet — Features demo "); // 1. Server setup var serverOptions = new QuicServerOptions { Port = 9876, MaxConnections = 100_000, HeartbeatIntervalInMilliseconds = 5_000, ClientTimeoutInMilliseconds = 15_000, EnableHeartbeat = true, NoDelay = true }; var server = new QuicServer(serverOptions); server.Started += (s, e) => Log("SERVER", $"Listening on port {e.Port}"); server.Stopped += (s, e) => Log("SERVER", "Stopped"); server.ClientConnected += (s, e) => Log("SERVER", string.Format("+ Connected [{0}...]", e.Client.SessionId.Substring(0, 8))); server.ClientDisconnected += (s, e) => Log("SERVER", string.Format("- Disconnected [{0}...] Reason={1}", e.Client.SessionId.Substring(0, 8), e.Reason)); server.DataReceived += (s, e) => { var id = e.Client.Nickname ?? (e.Client.SessionId.Length > 8 ? e.Client.SessionId.Substring(0, 8) : e.Client.SessionId); Log("SERVER", $" Data from [{id}]: {e.Text}"); }; server.ClientJoinedGroup += (s, e) => Log("SERVER", $" [{e.Client.Nickname ?? "?"}] joined group '{e.GroupName}'"); server.ClientLeftGroup += (s, e) => Log("SERVER", $" [{e.Client.Nickname ?? "?"}] left group '{e.GroupName}'"); server.Error += (s, e) => Log("SERVER", $" ERROR: {e.Exception.Message} [{e.Context}]"); server.Start(); Thread.Sleep(200); // 2. Client A var clientA = new QuicClient(new QuicClientOptions { Host = "127.0.0.1", Port = 9876, Nickname = "Alice", AutoReconnect = true, ReconnectMaxAttempts = 5, ReconnectBaseDelayMs = 500 }); clientA.Connected += (s, e) => Log("CLIENT-A", string.Format("Connected session={0}...", e.SessionId.Substring(0, 8))); clientA.Disconnected += (s, e) => Log("CLIENT-A", $"Disconnected reason={e.Reason}"); clientA.DataReceived += (s, e) => Log("CLIENT-A", $" ← {e.Text}"); clientA.Reconnecting += (s, e) => Log("CLIENT-A", $" Reconnecting attempt {e.Attempt}/{e.MaxAttempts}..."); clientA.ReconnectFailed += (s, e) => Log("CLIENT-A", " Reconnect gave up."); clientA.Error += (s, e) => Log("CLIENT-A", $" ERROR: {e.Exception.Message}"); clientA.Connect(); Thread.Sleep(300); // 3. Client B var clientB = new QuicClient("127.0.0.1", 9876, "Bob"); clientB.Connected += (s, e) => Log("CLIENT-B", string.Format("Connected session={0}...", e.SessionId.Substring(0, 8))); clientB.Disconnected += (s, e) => Log("CLIENT-B", $"Disconnected reason={e.Reason}"); clientB.DataReceived += (s, e) => Log("CLIENT-B", $" ← {e.Text}"); clientB.Error += (s, e) => Log("CLIENT-B", $" ERROR: {e.Exception.Message}"); clientB.Connect(); Thread.Sleep(300); Console.WriteLine(); Log("DEMO", "Basic sends"); // 4. Send string from client clientA.Send("Hello from Alice!"); clientB.Send("Hello from Bob!"); Thread.Sleep(100); // 5. Send bytes clientA.Send(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }); // raw bytes Thread.Sleep(100); // 6. Broadcast from server Console.WriteLine(); Log("DEMO", "Broadcast"); int n = server.Broadcast("Server broadcast to everyone!"); Log("SERVER", $" Sent to {n} clients"); Thread.Sleep(100); // 7. Send to single client Console.WriteLine(); Log("DEMO", "SendTo (single)"); var clients = new System.Collections.Generic.List(server.GetClients()); if (clients.Count >= 2) { var aliceSession = FindByNickname(server, "Alice"); var bobSession = FindByNickname(server, "Bob"); if (aliceSession != null) { server.SendTo(aliceSession.SessionId, "Private message to Alice only"); } if (bobSession != null) { server.SendTo(bobSession.SessionId, "Private message to Bob only"); } } Thread.Sleep(100); // 8. Groups Console.WriteLine(); Log("DEMO", "Groups"); var alice = FindByNickname(server, "Alice"); var bob = FindByNickname(server, "Bob"); if (alice != null) { server.AddToGroup(alice.SessionId, "vip"); } if (bob != null) { server.AddToGroup(bob.SessionId, "vip"); } if (alice != null) { server.AddToGroup(alice.SessionId, "team-a"); } Thread.Sleep(100); int vipCount = server.SendToGroup("vip", "VIP broadcast (Alice + Bob)"); Log("SERVER", $" Sent to {vipCount} VIP clients"); int teamACount = server.SendToGroup("team-a", "Team-A message (Alice only)"); Log("SERVER", $" Sent to {teamACount} team-a clients"); Thread.Sleep(100); // 9. Nickname change Console.WriteLine(); Log("DEMO", "Nickname change"); clientA.Nickname = "Alice_Renamed"; Thread.Sleep(100); Log("CLIENT-A", $"Nickname is now: {clientA.Nickname}"); // 10. Encoding variants Console.WriteLine(); Log("DEMO", "Unicode / Encoding"); clientB.Send("あなたを決してあきらめない", Encoding.UTF8); Thread.Sleep(100); // 11. Server queries Console.WriteLine(); Log("DEMO", "Server queries"); Log("SERVER", $" Connected clients: {server.ClientCount}"); foreach (var g in server.GetGroups()) { int cnt = 0; foreach (var _ in server.GetGroupClients(g)) { cnt++; } Log("SERVER", $" Group '{g}': {cnt} member(s)"); } // 12. Remove from group if (alice != null) { server.RemoveFromGroup(alice.SessionId, "vip"); Log("SERVER", " Alice removed from 'vip'"); } Thread.Sleep(100); // 13. Extension methods Console.WriteLine(); Log("DEMO", "Extensions"); server.BroadcastText("Extension broadcast text!"); server.SendToGroupText("team-a", "Extension group-text to team-a"); Thread.Sleep(100); // 14. Graceful disconnect Console.WriteLine(); Log("DEMO", "Graceful disconnect"); clientB.Disconnect("Bob says goodbye"); Thread.Sleep(300); // 15. Server kick if (alice != null) { server.Kick(alice.SessionId, "Demo kick"); Thread.Sleep(300); } // 16. Shutdown Console.WriteLine(); Log("DEMO", "Server shutdown"); server.Stop(); Thread.Sleep(200); clientA.Dispose(); clientB.Dispose(); server.Dispose(); Console.WriteLine("Demo complete!"); } private static void Log(string source, string message) => Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [{source,-10}] {message}"); private static IQuicClient FindByNickname(QuicServer server, string nickname) { foreach (var client in server.GetClients()) { if (string.Equals(client.Nickname, nickname, StringComparison.OrdinalIgnoreCase)) { return client; } } return null; } } }