using System.Diagnostics; using LiteNetLib; using LiteNetLib.Utils; using Serilog; namespace ClientTester.EchoDummyService; public class DummyClients { public NetManager Manager; public EventBasedNetListener Listener; public NetPeer? Peer; public int ClientId; // seq → 송신 타임스탬프 (Stopwatch tick) private readonly Dictionary _pendingPings = new(); private int _seqNumber; // 통계 public int SentCount; public int ReceivedCount; public double LastRttMs; public double TotalRttMs; public int RttCount; public DummyClients(int clientId, string ip, int port, string key) { ClientId = clientId; Listener = new EventBasedNetListener(); Manager = new NetManager(Listener); Listener.PeerConnectedEvent += peer => { Peer = peer; Log.Information("[Client {ClientId:00}] 연결됨", ClientId); }; Listener.NetworkReceiveEvent += (peer, reader, channel, deliveryMethod) => { var msg = reader.GetString(); // "ack:seq:{seqNum}" 파싱 var parts = msg.Split(':'); if (parts.Length == 3 && parts[0] == "ack" && parts[1] == "seq" && int.TryParse(parts[2], out int seq) && _pendingPings.TryGetValue(seq, out long sentTick)) { double rttMs = (Stopwatch.GetTimestamp() - sentTick) * 1000.0 / Stopwatch.Frequency; LastRttMs = rttMs; TotalRttMs += rttMs; RttCount++; _pendingPings.Remove(seq); } ReceivedCount++; reader.Recycle(); }; Listener.PeerDisconnectedEvent += (peer, info) => { Log.Warning("[Client {ClientId:00}] 연결 끊김: {Reason}", ClientId, info.Reason); Peer = null; }; Manager.Start(); Manager.Connect(ip, port, key); } public void SendPing() { if (Peer is null) return; int seq = _seqNumber++; _pendingPings[seq] = Stopwatch.GetTimestamp(); var writer = new NetDataWriter(); writer.Put($"seq:{seq}"); Peer.Send(writer, DeliveryMethod.ReliableOrdered); SentCount++; } public double AvgRttMs => RttCount > 0 ? TotalRttMs / RttCount : 0.0; public void PollEvents() => Manager.PollEvents(); public void Stop() => Manager.Stop(); }