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;
}
}
}