feat : 서버 로직 성능개선 / 결과 정리
This commit is contained in:
@@ -9,7 +9,7 @@ class EcoClientTester
|
|||||||
public static string SERVER_IP = "localhost";
|
public static string SERVER_IP = "localhost";
|
||||||
public static int SERVER_PORT = 9500;
|
public static int SERVER_PORT = 9500;
|
||||||
public static readonly string CONNECTION_KEY = "test";
|
public static readonly string CONNECTION_KEY = "test";
|
||||||
public static int CLIENT_COUNT = 100;
|
public static int CLIENT_COUNT = 50;
|
||||||
|
|
||||||
private async Task StartEchoDummyTest()
|
private async Task StartEchoDummyTest()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using LiteNetLib;
|
||||||
using MMOserver.Game.Channel.Maps;
|
using MMOserver.Game.Channel.Maps;
|
||||||
|
|
||||||
namespace MMOserver.Game.Channel;
|
namespace MMOserver.Game.Channel;
|
||||||
@@ -10,6 +11,9 @@ public class Channel
|
|||||||
// 채널 내 유저 상태 (hashKey → Player)
|
// 채널 내 유저 상태 (hashKey → Player)
|
||||||
private Dictionary<long, Player> connectUsers = new Dictionary<long, Player>();
|
private Dictionary<long, Player> connectUsers = new Dictionary<long, Player>();
|
||||||
|
|
||||||
|
// 채널 내 유저 NetPeer (hashKey → NetPeer) — BroadcastToChannel 교차 조회 제거용
|
||||||
|
private Dictionary<long, NetPeer> connectPeers = new Dictionary<long, NetPeer>();
|
||||||
|
|
||||||
public int ChannelId
|
public int ChannelId
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -33,19 +37,27 @@ public class Channel
|
|||||||
ChannelId = channelId;
|
ChannelId = channelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddUser(long userId, Player player)
|
public void AddUser(long userId, Player player, NetPeer peer)
|
||||||
{
|
{
|
||||||
connectUsers[userId] = player;
|
connectUsers[userId] = player;
|
||||||
|
connectPeers[userId] = peer;
|
||||||
UserCount++;
|
UserCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveUser(long userId)
|
public void RemoveUser(long userId)
|
||||||
{
|
{
|
||||||
connectUsers.Remove(userId);
|
connectUsers.Remove(userId);
|
||||||
|
connectPeers.Remove(userId);
|
||||||
UserCount--;
|
UserCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 채널 내 모든 유저의 hashKey 반환
|
// 재연결(WiFi→LTE 등) 시 동일 유저의 peer 교체
|
||||||
|
public void UpdatePeer(long userId, NetPeer peer)
|
||||||
|
{
|
||||||
|
connectPeers[userId] = peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 채널 내 모든 유저의 hashKey 반환 (채널 입장 시 기존 플레이어 목록 조회용)
|
||||||
public IEnumerable<long> GetConnectUsers()
|
public IEnumerable<long> GetConnectUsers()
|
||||||
{
|
{
|
||||||
return connectUsers.Keys;
|
return connectUsers.Keys;
|
||||||
@@ -57,6 +69,12 @@ public class Channel
|
|||||||
return connectUsers.Values;
|
return connectUsers.Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 채널 내 모든 NetPeer 반환 — BroadcastToChannel 전용 (sessions 교차 조회 불필요)
|
||||||
|
public IEnumerable<NetPeer> GetConnectPeers()
|
||||||
|
{
|
||||||
|
return connectPeers.Values;
|
||||||
|
}
|
||||||
|
|
||||||
// 특정 유저의 Player 반환
|
// 특정 유저의 Player 반환
|
||||||
public Player? GetPlayer(long userId)
|
public Player? GetPlayer(long userId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace MMOserver.Game.Channel;
|
using LiteNetLib;
|
||||||
|
|
||||||
|
namespace MMOserver.Game.Channel;
|
||||||
|
|
||||||
public class ChannelManager
|
public class ChannelManager
|
||||||
{
|
{
|
||||||
@@ -37,12 +39,12 @@ public class ChannelManager
|
|||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddUser(int channelId, long userId, Player player)
|
public void AddUser(int channelId, long userId, Player player, NetPeer peer)
|
||||||
{
|
{
|
||||||
// 유저 추가
|
// 유저 추가
|
||||||
connectUsers.Add(userId, channelId);
|
connectUsers.Add(userId, channelId);
|
||||||
// 채널에 유저 추가
|
// 채널에 유저 + peer 추가
|
||||||
channels[channelId].AddUser(userId, player);
|
channels[channelId].AddUser(userId, player, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemoveUser(long userId)
|
public bool RemoveUser(long userId)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class GameServer : ServerBase
|
|||||||
{
|
{
|
||||||
// 더미 클라다.
|
// 더미 클라다.
|
||||||
ChannelManager cm = ChannelManager.Instance;
|
ChannelManager cm = ChannelManager.Instance;
|
||||||
cm.AddUser(1, hashKey, new Player());
|
cm.AddUser(1, hashKey, new Player(), peer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -134,7 +134,8 @@ public class GameServer : ServerBase
|
|||||||
|
|
||||||
if (channelId >= 0)
|
if (channelId >= 0)
|
||||||
{
|
{
|
||||||
// 재연결: 채널 유저 목록 전송 (채널 선택 스킵, 바로 마을로)
|
// 재연결: Channel 내 peer 참조 갱신 후 채널 유저 목록 전송
|
||||||
|
cm.GetChannel(channelId).UpdatePeer(hashKey, peer);
|
||||||
SendIntoChannelPacket(peer, hashKey);
|
SendIntoChannelPacket(peer, hashKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -263,16 +264,12 @@ public class GameServer : ServerBase
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// 특정 채널의 모든 유저에게 전송 (exclude 지정 시 해당 피어 제외)
|
// 특정 채널의 모든 유저에게 전송 (exclude 지정 시 해당 피어 제외)
|
||||||
|
// Channel이 NetPeer를 직접 보유하므로 sessions 교차 조회 없음
|
||||||
private void BroadcastToChannel(int channelId, byte[] data, NetPeer? exclude = null, DeliveryMethod method = DeliveryMethod.ReliableOrdered)
|
private void BroadcastToChannel(int channelId, byte[] data, NetPeer? exclude = null, DeliveryMethod method = DeliveryMethod.ReliableOrdered)
|
||||||
{
|
{
|
||||||
Channel.Channel channel = ChannelManager.Instance.GetChannel(channelId);
|
Channel.Channel channel = ChannelManager.Instance.GetChannel(channelId);
|
||||||
foreach (long userId in channel.GetConnectUsers())
|
foreach (NetPeer targetPeer in channel.GetConnectPeers())
|
||||||
{
|
{
|
||||||
if (!sessions.TryGetValue(userId, out NetPeer? targetPeer))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exclude != null && targetPeer.Id == exclude.Id)
|
if (exclude != null && targetPeer.Id == exclude.Id)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -330,7 +327,7 @@ public class GameServer : ServerBase
|
|||||||
PlayerId = (int)(hashKey & 0x7FFFFFFF),
|
PlayerId = (int)(hashKey & 0x7FFFFFFF),
|
||||||
};
|
};
|
||||||
|
|
||||||
cm.AddUser(packet.ChannelId, hashKey, newPlayer);
|
cm.AddUser(packet.ChannelId, hashKey, newPlayer, peer);
|
||||||
Log.Debug("[GameServer] INTO_CHANNEL HashKey={Key} ChannelId={ChannelId}", hashKey, packet.ChannelId);
|
Log.Debug("[GameServer] INTO_CHANNEL HashKey={Key} ChannelId={ChannelId}", hashKey, packet.ChannelId);
|
||||||
|
|
||||||
// 접속된 모든 유저 정보 전달
|
// 접속된 모든 유저 정보 전달
|
||||||
|
|||||||
@@ -277,9 +277,106 @@ Docker Compose (compose.yaml)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 문제점 - 해결
|
## 4. 버그 및 잠재적 이슈
|
||||||
|
|
||||||
### 4.1 해결된 문제
|
> 코드 정적 분석을 통해 발견된 버그 및 잠재적 문제점 목록입니다.
|
||||||
|
|
||||||
|
### 4.1 버그 목록
|
||||||
|
|
||||||
|
#### [B1] Engine Vector3 타입 불일치 (HIGH)
|
||||||
|
|
||||||
|
- **파일**: `MMOserver/Game/Engine/Vector3.cs` vs `MMOserver/Packet/PacketBody.cs`
|
||||||
|
- **문제**: 두 Vector3 클래스가 다른 타입으로 정의됨
|
||||||
|
- `Engine/Vector3.cs`: 좌표가 `int` 타입
|
||||||
|
- `Packet/PacketBody.cs` (PacketVector3): 좌표가 `float` 타입
|
||||||
|
- **영향**: 실제 코드에서는 Packet Vector3만 사용되고 Engine Vector3는 사용되지 않음. 향후 Engine Vector3를 사용하는 코드가 생기면 타입 캐스팅 오류 발생
|
||||||
|
- **권장 조치**: Engine Vector3 제거 또는 float 기반으로 통일
|
||||||
|
|
||||||
|
#### [B2] ChannelManager.AddUser() 스레드 안전성 문제 (MEDIUM)
|
||||||
|
|
||||||
|
- **파일**: `MMOserver/Game/Channel/ChannelManager.cs`
|
||||||
|
- **문제**: 비동기 인증 흐름(JWT 검증 → AddUser) 후 두 요청이 동시에 같은 userId로 `connectUsers.Add()`를 호출할 경우 `ArgumentException` 발생 가능
|
||||||
|
```csharp
|
||||||
|
public void AddUser(int channelId, long userId, Player player)
|
||||||
|
{
|
||||||
|
connectUsers.Add(userId, channelId); // 중복 키 시 예외 발생
|
||||||
|
channels[channelId].AddUser(userId, player);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **영향**: 동시 재접속 시나리오에서 서버 크래시 가능성
|
||||||
|
- **권장 조치**: `TryAdd()` 사용 또는 `lock` 추가
|
||||||
|
|
||||||
|
#### [B3] PlayerId 정수 오버플로우로 인한 중복 ID (LOW)
|
||||||
|
|
||||||
|
- **파일**: `MMOserver/Game/GameServer.cs`
|
||||||
|
- **문제**: `PlayerId = (int)(hashKey & 0x7FFFFFFF)` 계산으로 서로 다른 hashKey가 동일한 PlayerId를 생성할 수 있음
|
||||||
|
- 예: `hashKey=1` → PlayerId=1, `hashKey=2147483649` → PlayerId=1 (동일)
|
||||||
|
- **영향**: ID 기반 플레이어 조회 오작동
|
||||||
|
- **권장 조치**: PlayerId로 hashKey 원본(long) 사용
|
||||||
|
|
||||||
|
#### [B4] TokenHash 딕셔너리 무한 증가 (MEDIUM)
|
||||||
|
|
||||||
|
- **파일**: `ServerLib/Service/ServerBase.cs`
|
||||||
|
- **문제**: 토큰 만료 또는 로그아웃 시 `tokenHash` 딕셔너리에서 항목이 제거되지 않음. 장기 운영 시 모든 누적 토큰이 메모리에 남음
|
||||||
|
- **영향**: 장기 운영 서버에서 메모리 누수 (ex. 100만 로그인 이후 100만 항목 유지)
|
||||||
|
- **권장 조치**: 로그아웃/연결 해제 시 `tokenHash.Remove()` 호출, 또는 TTL 기반 만료 캐시 도입
|
||||||
|
|
||||||
|
#### [B5] 패킷 역직렬화 크기 검증 없음 (MEDIUM)
|
||||||
|
|
||||||
|
- **파일**: `ServerLib/Service/ServerBase.cs`
|
||||||
|
- **문제**: 패킷 페이로드가 `null`인지는 확인하지만, 역직렬화된 객체의 필드 크기는 검증하지 않음
|
||||||
|
- **영향**: 악의적으로 구성된 패킷(예: PlayerInfo 리스트 내 수만 개의 항목)이 메모리 과소비 유발 가능
|
||||||
|
- **권장 조치**: 역직렬화 전 페이로드 바이트 크기 상한 검증 추가
|
||||||
|
|
||||||
|
#### [B6] Echo 패킷에 레이트 리미팅 미적용 (MEDIUM)
|
||||||
|
|
||||||
|
- **파일**: `MMOserver/Game/GameServer.cs`
|
||||||
|
- **문제**: Echo 패킷 처리 경로가 세션 인증 및 레이트 리미팅 검사를 우회함
|
||||||
|
- **영향**: 인증 전 클라이언트가 Echo 패킷을 무제한으로 전송하여 서버 자원 소모 가능 (DoS)
|
||||||
|
- **권장 조치**: Echo 처리 경로에도 레이트 리미팅 적용
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 최적화 미비점
|
||||||
|
|
||||||
|
### 5.1 성능 병목
|
||||||
|
|
||||||
|
#### ~~[O1] BroadcastToChannel 내 반복적 딕셔너리 조회~~ ✅ 해결됨
|
||||||
|
|
||||||
|
- **해결**: `Channel`에 `Dictionary<long, NetPeer> connectPeers` 추가, `BroadcastToChannel`이 `GetConnectPeers()`를 직접 순회하도록 변경
|
||||||
|
- **변경 파일**: `Channel.cs`, `ChannelManager.cs`, `GameServer.cs`
|
||||||
|
- **효과**: 100명 채널 브로드캐스트 시 `sessions` 딕셔너리 교차 조회 100회 → 0회 제거
|
||||||
|
- **추가 처리**: 재연결(WiFi→LTE) 시 `Channel.UpdatePeer()`로 peer 참조 갱신
|
||||||
|
|
||||||
|
#### [O2] Transform 패킷 객체 풀링 미구현
|
||||||
|
|
||||||
|
- **파일**: `ServerLib/Packet/PacketSerializer.cs`
|
||||||
|
- **문제**: 매 패킷마다 새 객체 생성 및 Protobuf 리플렉션 수행. Transform 패킷은 초당 수백~수천 회 발생하는 고빈도 패킷
|
||||||
|
- **권장 조치**: `ObjectPool<T>` 도입으로 GC 압박 감소
|
||||||
|
|
||||||
|
#### [O3] Dictionary 열거 시 Enumerator 힙 할당
|
||||||
|
|
||||||
|
- **현황**: 모든 열거 메서드(`GetConnectUsers`, `GetPlayers`, `GetConnectPeers`)를 `IEnumerable<T>`로 통일
|
||||||
|
- **판단 근거**: 박싱 비용(수 나노초)이 실질적으로 무시 가능한 수준이며, `IEnumerable<T>`가 가독성과 인터페이스 일관성 면에서 유리
|
||||||
|
- **미적용 사유**: 가독성 > 마이크로 최적화 우선
|
||||||
|
|
||||||
|
#### [O4] 레이트 리미터 고정 윈도우 방식
|
||||||
|
|
||||||
|
- **파일**: `ServerLib/Service/Session.cs`
|
||||||
|
- **문제**: 현재 고정 윈도우(Fixed Window) 방식 사용. 윈도우 경계에서 최대 2배 패킷 폭주 허용 가능
|
||||||
|
- 예: 윈도우 끝 120개 + 새 윈도우 시작 120개 = 240개/초 순간 처리
|
||||||
|
- **권장 조치**: 슬라이딩 윈도우 또는 토큰 버킷 알고리즘으로 교체
|
||||||
|
|
||||||
|
#### [O5] 로깅 문자열 할당
|
||||||
|
|
||||||
|
- **현황**: 모든 패킷 처리마다 Serilog 메시지 템플릿 평가 발생
|
||||||
|
- **권장 조치**: `if (Log.IsEnabled(LogEventLevel.Debug))` 가드 추가로 Debug 레벨 불필요한 평가 방지
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 문제점 - 해결
|
||||||
|
|
||||||
|
### 6.1 해결된 문제
|
||||||
|
|
||||||
#### [P1] 이동 패킷 재전송 문제
|
#### [P1] 이동 패킷 재전송 문제
|
||||||
- **문제**: 이동(Transform) 패킷이 `ReliableOrdered`로 전송되어 패킷 손실 시 재전송 발생 → 위치 동기화 지연, Head-of-Line 블로킹
|
- **문제**: 이동(Transform) 패킷이 `ReliableOrdered`로 전송되어 패킷 손실 시 재전송 발생 → 위치 동기화 지연, Head-of-Line 블로킹
|
||||||
@@ -326,7 +423,7 @@ Docker Compose (compose.yaml)
|
|||||||
- `Program.cs`: CLI 인자 지원 (`stress -c 100 -d 60 --ip ...`) + 대화형 모드 3번 메뉴
|
- `Program.cs`: CLI 인자 지원 (`stress -c 100 -d 60 --ip ...`) + 대화형 모드 3번 메뉴
|
||||||
- **효과**: N명 동시접속 시나리오 자동 검증, 성능 병목 지점 파악
|
- **효과**: N명 동시접속 시나리오 자동 검증, 성능 병목 지점 파악
|
||||||
|
|
||||||
### 4.2 현재 남아 있는 문제
|
### 6.2 현재 남아 있는 문제
|
||||||
|
|
||||||
#### [I1] 서버 사이드 위치 검증 미구현
|
#### [I1] 서버 사이드 위치 검증 미구현
|
||||||
- **현상**: 클라이언트가 전송하는 위치를 서버에서 검증하지 않음
|
- **현상**: 클라이언트가 전송하는 위치를 서버에서 검증하지 않음
|
||||||
@@ -334,20 +431,20 @@ Docker Compose (compose.yaml)
|
|||||||
- **권장**: `MapBounds` 로직을 서버 사이드에 적용, 비정상 이동 감지 시 보정
|
- **권장**: `MapBounds` 로직을 서버 사이드에 적용, 비정상 이동 감지 시 보정
|
||||||
|
|
||||||
#### [I2] 플레이어 데이터 DB 연동 미완성
|
#### [I2] 플레이어 데이터 DB 연동 미완성
|
||||||
- **현상**: 플레이어 데이터가 하드코딩된 기본값 사용 (TODO 주석 존재)
|
- **현상**: 플레이어 데이터가 하드코딩된 기본값 사용 (GameServer.cs 내 TODO 주석 존재)
|
||||||
- **영향**: 캐릭터 정보 저장/불러오기 불가
|
- **영향**: 캐릭터 정보 저장/불러오기 불가, 레벨·닉네임·HP/MP 등 지속성 없음
|
||||||
- **권장**: Player 모델 DB 테이블 생성, 로그인 시 DB 조회
|
- **권장**: Player 모델 DB 테이블 생성, 로그인 시 DB 조회
|
||||||
|
|
||||||
#### [I3] NPC / 파티 시스템 미구현
|
#### [I3] NPC / 파티 시스템 미구현
|
||||||
- **현상**: 패킷 코드(TRANSFORM_NPC, ACTION_NPC, UPDATE_PARTY 등)는 정의되어 있으나 처리 로직 없음
|
- **현상**: 패킷 코드(`TRANSFORM_NPC`, `ACTION_NPC`, `UPDATE_PARTY` 등)는 정의되어 있으나 핸들러 없음
|
||||||
- **영향**: 게임 콘텐츠 부족
|
- **영향**: 수신 시 경고 로그만 출력되고 무시됨
|
||||||
- **권장**: NPC AI 시스템, 파티 매칭 로직 순차 구현
|
- **권장**: NPC AI 시스템, 파티 매칭 로직 순차 구현
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. 정리
|
## 7. 정리
|
||||||
|
|
||||||
### 5.1 아키텍처 요약
|
### 7.1 아키텍처 요약
|
||||||
|
|
||||||
```
|
```
|
||||||
┌──────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────┐
|
||||||
@@ -376,7 +473,7 @@ Docker Compose (compose.yaml)
|
|||||||
└──────────────────────────────┘
|
└──────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5.2 기술적 강점
|
### 7.2 기술적 강점
|
||||||
|
|
||||||
| 항목 | 내용 |
|
| 항목 | 내용 |
|
||||||
|------|------|
|
|------|------|
|
||||||
@@ -389,19 +486,21 @@ Docker Compose (compose.yaml)
|
|||||||
| 계층화된 DB 구조 | Handler-Service-Repository 패턴 |
|
| 계층화된 DB 구조 | Handler-Service-Repository 패턴 |
|
||||||
| Docker 배포 | 컨테이너화된 일관된 배포 환경 |
|
| Docker 배포 | 컨테이너화된 일관된 배포 환경 |
|
||||||
|
|
||||||
### 5.3 향후 작업 (Roadmap)
|
### 7.3 향후 작업 (Roadmap)
|
||||||
|
|
||||||
| 우선순위 | 작업 | 설명 |
|
| 우선순위 | 작업 | 설명 |
|
||||||
|---------|------|------|
|
|---------|------|------|
|
||||||
| **높음** | 플레이어 DB 연동 | 캐릭터 정보 저장/불러오기 완성 |
|
| **높음** | 플레이어 DB 연동 | 캐릭터 정보 저장/불러오기 완성 (I2) |
|
||||||
| **높음** | 서버 사이드 위치 검증 | 치트 방어를 위한 이동 유효성 검사 |
|
| **높음** | 버그 수정 | B2 스레드 안전성, B4 메모리 누수, B6 Echo 레이트 리미팅 |
|
||||||
| **중간** | NPC 시스템 | AI 기반 NPC 이동/전투 로직 |
|
| **높음** | 서버 사이드 위치 검증 | 치트 방어를 위한 이동 유효성 검사 (I1) |
|
||||||
| **낮음** | 파티 시스템 | 파티 생성/참여/매칭 |
|
| **중간** | NPC 시스템 | AI 기반 NPC 이동/전투 로직 (I3) |
|
||||||
|
| **중간** | 성능 최적화 | ~~O1 브로드캐스트 최적화 (완료)~~, O2 객체 풀링, O4 레이트 리미터 개선 |
|
||||||
|
| **낮음** | 파티 시스템 | 파티 생성/참여/매칭 (I3) |
|
||||||
| **낮음** | 모니터링 대시보드 | 서버 상태/접속자 수 실시간 모니터링 |
|
| **낮음** | 모니터링 대시보드 | 서버 상태/접속자 수 실시간 모니터링 |
|
||||||
|
|
||||||
> **완료된 항목**: 토큰 캐시 정리 (기 구현 확인), 패킷 레이트 리미팅 (적용 완료), 부하 테스트 도구 (구현 완료)
|
> **완료된 항목**: 패킷 레이트 리미팅 (적용 완료), 부하 테스트 도구 (구현 완료), 크래시 덤프 (적용 완료), O1 브로드캐스트 최적화 (적용 완료)
|
||||||
|
|
||||||
### 5.4 부하 테스트 도구 (StressTest)
|
### 7.4 부하 테스트 도구 (StressTest)
|
||||||
|
|
||||||
`ClientTester/EchoClientTester/StressTest/` 에 구현됨.
|
`ClientTester/EchoClientTester/StressTest/` 에 구현됨.
|
||||||
|
|
||||||
@@ -473,10 +572,14 @@ dotnet run -- stress \
|
|||||||
╚═══════════════════════════════════════════════╝
|
╚═══════════════════════════════════════════════╝
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5.5 최근 개발 이력
|
### 7.5 최근 개발 이력
|
||||||
|
|
||||||
| 커밋 | 내용 |
|
| 커밋 | 내용 |
|
||||||
|------|------|
|
|------|------|
|
||||||
|
| `85c3276` | 세션 끊길 때 같은 채널에 퇴장 메시지 전송 |
|
||||||
|
| `c27e846` | 채널 버그 수정 |
|
||||||
|
| `d4c5a70` | 채널 버그 수정 |
|
||||||
|
| `ea3f64a` | 스트레스 테스트 기능 추가 / 패킷 처리량 제한 / 프로젝트 상황 README 추가 |
|
||||||
| `2be1302` | 크래시 덤프 기능 추가 (Release: 힙 덤프) |
|
| `2be1302` | 크래시 덤프 기능 추가 (Release: 힙 덤프) |
|
||||||
| `42f0ef1` | 이동 패킷 Unreliable 전송으로 변경 |
|
| `42f0ef1` | 이동 패킷 Unreliable 전송으로 변경 |
|
||||||
| `bfa3394` | 토큰 → HashKey 생성 로직 구조 변경 |
|
| `bfa3394` | 토큰 → HashKey 생성 로직 구조 변경 |
|
||||||
@@ -485,5 +588,9 @@ dotnet run -- stress \
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> **작성일**: 2026-03-05
|
> **작성일**: 2026-03-06
|
||||||
> **프로젝트 상태**: 핵심 네트워크 인프라 완성, 게임 콘텐츠 확장 단계
|
> **프로젝트 상태**: 핵심 네트워크 인프라 완성, 게임 콘텐츠 확장 단계
|
||||||
|
>
|
||||||
|
> **구현 완료**: 네트워크 코어, 세션 관리, 채널 시스템, 인증, 레이트 리미팅, 크래시 덤프, 부하 테스트 도구
|
||||||
|
> **미구현**: 플레이어 DB 연동, NPC/파티 시스템, 서버 사이드 위치 검증, 전투/아이템 시스템
|
||||||
|
> **알려진 버그**: B1 Vector3 타입 불일치, B2 ChannelManager 스레드 안전성, B4 TokenHash 메모리 누수, B6 Echo DoS 취약점
|
||||||
Reference in New Issue
Block a user