feat : 더미 플레이어 구현 / 더미클라 서비스 에코용, 더미 플레이어용 분기
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using ClientTester.Packet;
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace ClientTester.EchoDummyService;
|
||||
|
||||
public class EchoDummyClients
|
||||
{
|
||||
private NetManager manager;
|
||||
private EventBasedNetListener listener;
|
||||
private NetDataWriter? writer;
|
||||
public NetPeer? peer;
|
||||
public int clientId;
|
||||
|
||||
// seq → 송신 타임스탬프 (Stopwatch tick)
|
||||
private ConcurrentDictionary<int, long> pendingPings = new();
|
||||
private int seqNumber;
|
||||
private const int MaxPendingPings = 1000;
|
||||
|
||||
// 유닛 테스트용 (0 = 제한 없음)
|
||||
public int TestCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = 0;
|
||||
|
||||
// 통계
|
||||
public int SentCount
|
||||
{
|
||||
set;
|
||||
get;
|
||||
}
|
||||
|
||||
public int ReceivedCount
|
||||
{
|
||||
set;
|
||||
get;
|
||||
}
|
||||
|
||||
public double LastRttMs
|
||||
{
|
||||
set;
|
||||
get;
|
||||
}
|
||||
|
||||
public double TotalRttMs
|
||||
{
|
||||
set;
|
||||
get;
|
||||
}
|
||||
|
||||
public int RttCount
|
||||
{
|
||||
set;
|
||||
get;
|
||||
}
|
||||
|
||||
public EchoDummyClients(int clientId, string ip, int port, string key)
|
||||
{
|
||||
this.clientId = clientId;
|
||||
listener = new EventBasedNetListener();
|
||||
manager = new NetManager(listener);
|
||||
writer = new NetDataWriter();
|
||||
|
||||
listener.PeerConnectedEvent += netPeer =>
|
||||
{
|
||||
peer = netPeer;
|
||||
Log.Information("[Client {ClientId:00}] 연결됨", this.clientId);
|
||||
};
|
||||
|
||||
listener.NetworkReceiveEvent += (peer, reader, channel, deliveryMethod) =>
|
||||
{
|
||||
short code = reader.GetShort();
|
||||
short bodyLength = reader.GetShort();
|
||||
string? msg = reader.GetString();
|
||||
long sentTick;
|
||||
|
||||
if (msg != null && msg.StartsWith("Echo seq:") &&
|
||||
int.TryParse(msg.Substring("Echo seq:".Length), out int seq) &&
|
||||
pendingPings.TryRemove(seq, out sentTick))
|
||||
{
|
||||
double rttMs = (Stopwatch.GetTimestamp() - sentTick) * 1000.0 / Stopwatch.Frequency;
|
||||
LastRttMs = rttMs;
|
||||
TotalRttMs += rttMs;
|
||||
RttCount++;
|
||||
}
|
||||
|
||||
ReceivedCount++;
|
||||
|
||||
if (TestCount > 0 && ReceivedCount >= TestCount)
|
||||
{
|
||||
peer.Disconnect();
|
||||
}
|
||||
|
||||
reader.Recycle();
|
||||
};
|
||||
|
||||
listener.PeerDisconnectedEvent += (peer, info) =>
|
||||
{
|
||||
Log.Warning("[Client {ClientId:00}] 연결 끊김: {Reason}", this.clientId, info.Reason);
|
||||
this.peer = null;
|
||||
};
|
||||
|
||||
manager.Start();
|
||||
manager.Connect(ip, port, key);
|
||||
}
|
||||
|
||||
public void SendPing()
|
||||
{
|
||||
if (peer == null || writer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int seq = seqNumber++;
|
||||
pendingPings[seq] = Stopwatch.GetTimestamp();
|
||||
|
||||
// 응답 없는 오래된 ping 정리 (패킷 유실 시 메모리 누수 방지)
|
||||
if (pendingPings.Count > MaxPendingPings)
|
||||
{
|
||||
int cutoff = seq - MaxPendingPings;
|
||||
foreach (int key in pendingPings.Keys)
|
||||
{
|
||||
if (key < cutoff)
|
||||
pendingPings.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
PacketHeader packetHeader = new PacketHeader();
|
||||
packetHeader.Code = 0;
|
||||
packetHeader.BodyLength = (ushort)$"Echo seq:{seq}".Length;
|
||||
writer.Put((short)packetHeader.Code);
|
||||
writer.Put((short)packetHeader.BodyLength);
|
||||
writer.Put($"Echo seq:{seq}");
|
||||
// 순서보장 안함 HOL Blocking 제거
|
||||
peer.Send(writer, DeliveryMethod.ReliableUnordered);
|
||||
SentCount++;
|
||||
writer.Reset();
|
||||
}
|
||||
|
||||
public double AvgRttMs => RttCount > 0 ? TotalRttMs / RttCount : 0.0;
|
||||
|
||||
public void PollEvents()
|
||||
{
|
||||
manager.PollEvents();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
manager.Stop();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user