- 채널 입장 시 API 서버에서 플레이어 프로필 로드 (레벨/스탯/위치) - 채널 퇴장 시 위치/플레이타임 DB 저장 (SaveGameDataAsync) - Player.cs에 AttackPower/AttackRange/SprintMultiplier/Experience 필드 추가 - ToPlayerInfo에서 전투 스탯 매핑 추가 - Session에 ChannelJoinedAt 추가 (플레이타임 계산용) - PartyUpdateType에 INVITE/KICK 추가 - RequestPartyPacket에 TargetPlayerId 필드 추가 - GameServer에 INVITE/KICK 핸들러 구현 - Channel에 GetPeer() 메서드 추가 - RestApi에 GetPlayerProfileAsync/SaveGameDataAsync 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ClientTester - MMO 서버 테스트 도구
MMO Game Server의 네트워크 성능 검증 및 부하 테스트를 위한 클라이언트 시뮬레이터.
1. 환경 (라이브러리)
| 항목 | 버전 | 용도 |
|---|---|---|
| .NET | 9.0 | 런타임 |
| LiteNetLib | 2.0.2 | UDP 네트워크 통신 (서버와 동일) |
| protobuf-net | 3.2.56 | 패킷 직렬화/역직렬화 |
| Serilog | 4.3.1 | 구조적 로깅 |
| Serilog.Sinks.Console | 6.0.0 | 콘솔 출력 |
| Serilog.Sinks.File | 7.0.0 | 파일 로깅 |
| Microsoft.Diagnostics.NETCore.Client | 0.2.553101 | 크래시 힙 덤프 (Release만) |
빌드
cd ClientTester/EchoClientTester
dotnet build
2. 디렉토리 구조
ClientTester/
├── EchoClientTester/
│ ├── Program.cs # 진입점 (대화형 + CLI)
│ ├── EchoClientTester.csproj
│ ├── Vector3.cs # 위치 구조체
│ │
│ ├── DummyService/ # 이동 테스트
│ │ ├── DummyClientService.cs # 다중 더미 클라 관리
│ │ ├── DummyClients.cs # 개별 더미 클라 (이동 시뮬)
│ │ └── MapBounds.cs # 맵 경계 / 벽 충돌
│ │
│ ├── EchoDummyService/ # Echo RTT 테스트
│ │ ├── EchoDummyClientService.cs # 다중 에코 클라 관리
│ │ └── EchoDummyClients.cs # 개별 에코 클라 (RTT 측정)
│ │
│ ├── StressTest/ # 부하/스트레스 테스트
│ │ ├── StressTestService.cs # 오케스트레이터 (Ramp-up, 통계, CSV)
│ │ └── StressTestClient.cs # 통합 클라 (이동 + Echo 동시)
│ │
│ ├── Packet/ # 패킷 정의 (서버와 동일 프로토콜)
│ │ ├── PacketHeader.cs # PacketCode Enum
│ │ ├── PacketBody.cs # Protobuf 패킷 클래스
│ │ └── PacketSerializer.cs # 직렬화 유틸
│ │
│ └── Utils/
│ └── CrashDumpHandler.cs # 크래시 로그 + 힙 덤프
│
└── TempServer/ # 간이 에코 서버 (독립 테스트용)
└── Program.cs
3. 사용방법
3.1 대화형 모드
dotnet run
========== 더미 클라 테스터 ==========
1. 에코 서버
2. 더미 클라(이동만)
3. 스트레스 테스트 (부하)
====================================
1 / 2 / 3 :
| 모드 | 설명 |
|---|---|
| 1. 에코 서버 | Echo 패킷 송수신, RTT(Round-Trip Time) 측정 |
| 2. 더미 클라 | 이동(Transform) 패킷 전송, 패킷 손실률 측정 |
| 3. 스트레스 테스트 | Ramp-up + 이동 + Echo 동시, 퍼센타일 레이턴시, CSV 내보내기 |
3.2 CLI 모드 (스트레스 테스트)
# 기본 실행 (50명, 60초)
dotnet run -- stress
# 100명 클라이언트, 120초 테스트
dotnet run -- stress -c 100 -d 120
# 원격 서버 대상, 커스텀 설정
dotnet run -- stress -c 200 -d 60 -r 500 -b 20 --ip 192.168.0.10 --port 9500
# 도움말
dotnet run -- --help
CLI 옵션
| 옵션 | 축약 | 기본값 | 설명 |
|---|---|---|---|
--clients |
-c |
50 | 총 클라이언트 수 |
--duration |
-d |
60 | 테스트 지속 시간 (초, 0=무제한) |
--interval |
-i |
100 | 패킷 전송 주기 (ms) |
--ramp |
-r |
1000 | Ramp-up 간격 (ms) |
--batch |
-b |
10 | Ramp-up 당 추가 클라이언트 수 |
--ip |
localhost | 서버 IP 주소 | |
--port |
9500 | 서버 포트 |
3.3 출력 파일
| 경로 | 내용 |
|---|---|
logs2/log_{timestamp}.txt |
전체 실행 로그 |
results/stress_{N}clients_{timestamp}.csv |
스트레스 테스트 결과 (클라이언트별 통계) |
crashes/crash_{timestamp}.log |
크래시 스택 트레이스 |
crashes/crash_{timestamp}.dmp |
힙 덤프 (Release 빌드만) |
4. 파이프라인
4.1 에코 테스트 (모드 1)
[클라이언트 N개 동시 생성]
↓
[연결] UDP Connect → ServerBase.OnConnectionRequest()
↓
[PollLoop] 10ms 주기로 네트워크 이벤트 처리
[SendLoop] 100ms 주기로 Echo 패킷 전송
↓
[송신] EchoPacket { Str = "Echo seq:{N}" }
→ PacketSerializer.Serialize()
→ 4바이트 헤더(type + size) + Protobuf 페이로드
→ DeliveryMethod.ReliableUnordered
↓
[수신] 서버가 Echo 패킷 그대로 반환
→ seq 번호로 pendingPings 조회
→ RTT 계산: (수신시각 - 송신시각) / Stopwatch.Frequency
↓
[통계] 클라이언트별: AvgRTT, LastRTT, Sent, Recv, PacketLoss%
전체: 총 Sent/Recv, 평균 RTT
4.2 이동 테스트 (모드 2)
[클라이언트 N개 동시 생성]
↓
[연결 + 인증]
DummyAccTokenPacket { Token = clientId }
→ DeliveryMethod.ReliableOrdered
↓
[이동 시뮬레이션 - UpdateDummy()]
┌────────────────────────────────────────┐
│ 1. 델타 타임 계산 (Stopwatch) │
│ 2. 남은 거리 = 0 이면: │
│ - 벽 근처? → 반대 방향 회전 │
│ - 아니면 → 현재 방향 ±30도 랜덤 회전 │
│ - 목표 거리 = moveSpeed × (3~12초) │
│ 3. 이번 틱 이동 (moveSpeed × delta) │
│ 4. MapBounds.Clamp() → 벽 충돌 처리 │
└────────────────────────────────────────┘
↓
[송신] TransformPlayerPacket { PlayerId, Position(X,0,Z), RotY }
→ DeliveryMethod.Unreliable (손실 감수)
↓
[통계] 클라이언트별: Sent, Recv, PacketLoss%
4.3 스트레스 테스트 (모드 3 / CLI)
[설정 입력] CLI 인자 또는 대화형 입력
↓
[Ramp-up Loop]
┌────────────────────────────────────┐
│ rampInterval(1000ms)마다 │
│ clientsPerRamp(10)명씩 클라이언트 생성 │
│ → totalClients 도달 시 중단 │
└────────────────────────────────────┘
↓ (병렬 실행)
[PollLoop] 10ms 주기 네트워크 이벤트
[SendLoop] sendInterval(100ms) 주기
├─ UpdateAndSendTransform() (이동 - Unreliable)
└─ SendPing() (Echo - ReliableUnordered)
[StatsLoop] 10초마다 실시간 통계 출력
↓
[지속시간 만료 또는 Ctrl+C]
↓
[최종 리포트]
├─ 처리량: 총 Sent/Recv, 패킷/초
├─ 레이턴시: Min, Avg, P50, P95, P99, Max
├─ 최악 RTT 상위 5 클라이언트 상세
└─ CSV 파일 자동 저장
4.4 패킷 직렬화 파이프라인
객체 (e.g. TransformPlayerPacket)
↓
protobuf-net Serialize → byte[] payload
↓
PacketSerializer.Serialize()
↓
┌──────────────────────────────┐
│ [2B type] [2B size] [payload] │
│ ushort ushort byte[] │
└──────────────────────────────┘
↓
NetDataWriter.Put(data)
↓
peer.Send(writer, DeliveryMethod)
5. 테스트 모드별 기능 비교
| 기능 | 에코 (1) | 이동 (2) | 스트레스 (3) |
|---|---|---|---|
| 동시접속 클라이언트 | 10 (고정) | 10 (고정) | N (설정 가능) |
| 인증 패킷 전송 | X | O | O |
| 이동 시뮬레이션 | X | O | O |
| Echo RTT 측정 | O | X | O |
| 패킷 손실 추적 | O | O | O |
| 점진적 Ramp-up | X | X | O |
| 지속시간 제한 | 무제한 | 무제한 | 설정 가능 |
| 실시간 통계 | X | X | O (10초 주기) |
| 퍼센타일 레이턴시 | X (평균만) | X | O (P50/P95/P99) |
| CSV 내보내기 | X | X | O |
| CLI 인자 | X | X | O |
| 최악 클라이언트 분석 | X | X | O (상위 5) |
6. 구현 진행도
완료
| 항목 | 상태 | 설명 |
|---|---|---|
| Echo RTT 측정 | 완료 | seq 기반 ping/pong, Stopwatch 고정밀 타이머 |
| 더미 이동 시뮬레이션 | 완료 | 벽 충돌, 랜덤 방향 전환, 델타 타임 기반 이동 |
| 맵 경계 처리 | 완료 | MapBounds (Clamp, 벽 반대 방향 계산) |
| 패킷 손실 추적 | 완료 | LiteNetLib.NetStatistics 활용 |
| 크래시 덤프 | 완료 | Debug: .log / Release: .log + .dmp |
| 로깅 (콘솔+파일) | 완료 | Serilog 타임스탬프별 로그 파일 |
| Graceful Shutdown | 완료 | Ctrl+C → CancellationToken → 통계 출력 후 종료 |
| 스트레스 테스트 | 완료 | Ramp-up, 퍼센타일, CSV, CLI 인자 |
| CLI 인자 지원 | 완료 | stress -c N -d T --ip --port |
미구현 / 향후 작업
| 항목 | 상태 | 설명 |
|---|---|---|
| 채널 입장 시뮬레이션 | 미구현 | INTO_CHANNEL 패킷 전송 후 게임 루프 진입 |
| 전투 액션 시뮬레이션 | 미구현 | ACTION_PLAYER (공격/스킬) 패킷 전송 |
| 상태 변경 시뮬레이션 | 미구현 | STATE_PLAYER (HP/MP) 패킷 전송 |
| JWT 토큰 인증 테스트 | 미구현 | ACC_TOKEN 방식 인증 플로우 검증 |
| 서버 응답 검증 | 미구현 | 수신 패킷 내용 파싱 및 정합성 체크 |
| 테스트 시나리오 스크립트 | 미구현 | JSON/YAML 기반 테스트 시나리오 정의 |
| 그래프 출력 | 미구현 | RTT/처리량 시계열 차트 생성 |
7. 설정 참고
서버 연결 기본값
| 항목 | 값 |
|---|---|
| IP | localhost |
| Port | 9500 |
| Connection Key | test |
이동 시뮬레이션 파라미터
| 항목 | 값 | 설명 |
|---|---|---|
| moveSpeed | 3.5 | 이동 속도 (units/sec) |
| 맵 범위 | -50 ~ 50 (X, Z) | 100x100 영역 |
| 벽 감지 마진 | 0.5 | 벽 근처 판정 거리 |
| 목표 거리 | moveSpeed x 3~12초 | 직진 후 방향 전환 |
| 방향 전환 | 현재 ±30도 랜덤 | 벽 근처 시 반대 방향 강제 |
스트레스 테스트 기본값
| 항목 | 값 | 설명 |
|---|---|---|
| 클라이언트 수 | 50 | -c 옵션 |
| 지속 시간 | 60초 | -d 옵션 |
| 전송 주기 | 100ms | -i 옵션 |
| Ramp-up 간격 | 1000ms | -r 옵션 |
| Ramp-up 배치 | 10명 | -b 옵션 |
| 실시간 통계 주기 | 10초 | 고정 |
| Echo 최대 pending | 500 | 메모리 누수 방지 |