- 최초 작성: 2026-02-26 최종 업데이트: 2026-03-02
- 기준 리포지토리:
NetworkModuleTest - 분석 기준:
ServerEngine,TestServer,DBServer실제 구현 코드 - 목적: 네트워크 처리, 비동기 처리, DB 처리의 현재 구조와 데이터 흐름을 코드 기준으로 정리
- 비동기 고도화: KeyedDispatcher(직렬화), TimerQueue(타이머), AsyncScope(협력 취소), Send 백프레셔, NetworkEventBus(이벤트 버스) 추가
- AsyncScope 풀 재사용 버그 수정: 세션 풀 재사용 시
mCancelled=true잔존 문제 →AsyncScope::Reset()추가로 해결 - Linux Docker 테스트: Ubuntu 22.04 컨테이너에서 epoll + io_uring 양 백엔드 통합 테스트 PASS
- Core/Memory 버퍼 모듈: 플랫폼 독립
IBufferPool+StandardBufferPool/RIOBufferPool/IOUringBufferPool - 성능: Windows 1000 클라이언트 PASS, Server WS 143.6 MB (최적화 후)
| Diagram | File |
|---|---|
| Architecture Overview | assets/01-architecture-overview.svg |
| Session UML | assets/02-session-uml.svg |
| Client Lifecycle Sequence | assets/03-client-lifecycle-sequence.svg |
| Async DB Flow Sequence | assets/04-async-db-flow-sequence.svg |
| Graceful Shutdown Sequence | assets/05-graceful-shutdown-sequence.svg |
| DB Reconnect Sequence | assets/06-db-reconnect-sequence.svg |
이 프로젝트는 INetworkEngine(상위 추상화)와 AsyncIOProvider(하위 플랫폼 백엔드)로 분리된 2계층 구조를 사용한다.
- 네트워크 처리: 플랫폼별 엔진(Windows/Linux/macOS)이 수락/완료 이벤트를 처리하고, 공통 로직은
BaseNetworkEngine이 담당 - 비동기 처리: I/O 워커 스레드 + 로직 스레드풀 + DB 전용 작업큐(
DBTaskQueue,OrderedTaskQueue)로 역할 분리 - DB 처리:
IDatabase추상화 위에SQLite/Mock/ODBC구현을 두고, TestServer는 비동기 큐 기반으로 DB 작업을 오프로딩
핵심 특징:
- Windows: RIO 우선, 실패 시 IOCP 폴백
- Linux: io_uring 우선, 실패 시 epoll 폴백
- macOS: kqueue
- TestServer의 접속/종료 DB 기록은 이벤트 핸들러 ->
DBTaskQueue경로로 비동기 처리 - 종료 시
DBTaskQueue드레인 -> DB 해제 -> 네트워크 종료 순서로 graceful shutdown 수행
Server/ServerEngine/Network/Core/NetworkEngine.hServer/ServerEngine/Network/Core/BaseNetworkEngine.hServer/ServerEngine/Network/Core/BaseNetworkEngine.cppServer/ServerEngine/Network/Core/NetworkEngineFactory.cppServer/ServerEngine/Network/Core/AsyncIOProvider.hServer/ServerEngine/Network/Core/AsyncIOProvider.cppServer/ServerEngine/Network/Platforms/WindowsNetworkEngine.cppServer/ServerEngine/Network/Platforms/LinuxNetworkEngine.cppServer/ServerEngine/Network/Platforms/macOSNetworkEngine.cppServer/ServerEngine/Network/Core/Session.hServer/ServerEngine/Network/Core/Session.cppServer/ServerEngine/Network/Core/SessionManager.cpp
Server/TestServer/src/TestServer.cppServer/TestServer/src/ClientSession.cppServer/TestServer/src/ClientPacketHandler.cppServer/TestServer/src/DBTaskQueue.cppServer/TestServer/src/DBServerPacketHandler.cppServer/TestServer/main.cpp
Server/DBServer/src/TestDBServer.cppServer/DBServer/src/ServerPacketHandler.cppServer/DBServer/src/OrderedTaskQueue.cppServer/DBServer/src/ServerLatencyManager.cppServer/DBServer/main.cpp
INetworkEngine가 외부 API를 정의하고(Initialize/Start/Stop/SendData/CloseConnection), BaseNetworkEngine이 공통 동작을 구현한다.
- 공통: 세션 조회/제거, 이벤트 콜백 등록, 통계 집계
- 플랫폼 특화: 소켓 생성, accept 루프, completion 처리
코드 포인트:
- 인터페이스:
Server/ServerEngine/Network/Core/NetworkEngine.h:72 - 공통 구현:
Server/ServerEngine/Network/Core/BaseNetworkEngine.cpp:28
엔진 팩토리(CreateNetworkEngine("auto"))와 AsyncIO 팩토리가 폴백 체인을 제공한다.
- Windows: RIO -> IOCP
Server/ServerEngine/Network/Core/NetworkEngineFactory.cpp:24Server/ServerEngine/Network/Core/AsyncIOProvider.cpp:74
- Linux: io_uring -> epoll
Server/ServerEngine/Network/Core/NetworkEngineFactory.cpp:58Server/ServerEngine/Network/Core/AsyncIOProvider.cpp:104
- macOS: kqueue
Server/ServerEngine/Network/Core/NetworkEngineFactory.cpp:105
플랫폼 엔진 AcceptLoop() 공통 흐름:
accept()로 소켓 수락SessionManager::CreateSession()로 세션 생성AsyncIOProvider::AssociateSocket()으로 백엔드 연동- 로직 스레드풀에서
OnConnected+Connected 이벤트실행 - 첫
Recv등록
Windows 기준 코드:
- accept 루프:
Server/ServerEngine/Network/Platforms/WindowsNetworkEngine.cpp:128 - 소켓 associate:
.../WindowsNetworkEngine.cpp:167 - 연결 이벤트 비동기 실행:
.../WindowsNetworkEngine.cpp:188
수신 완료 처리(ProcessCompletions)에서 Recv 완료를 BaseNetworkEngine::ProcessRecvCompletion()으로 전달한다.
- 데이터는 로직 스레드풀로 전달된 후
Session::ProcessRawRecv()실행 ProcessRawRecv()는 TCP 스트림을PacketHeader(size,id)기준으로 재조립- 유효하지 않은 크기/오버플로우 탐지 시 세션 종료
코드 포인트:
- completion 처리:
Server/ServerEngine/Network/Platforms/WindowsNetworkEngine.cpp:233 - 공통 recv 완료:
Server/ServerEngine/Network/Core/BaseNetworkEngine.cpp:255 - TCP 재조립:
Server/ServerEngine/Network/Core/Session.cpp:445
송신 경로는 플랫폼별로 나뉜다.
- Windows + RIO:
Session::Send()에서 providerSendAsync()직행 - 그 외(WSASend/epoll/kqueue):
mSendQueue에 enqueue 후PostSend()로 비동기 송신
Session은 mSendQueueSize(atomic)와 mIsSending(CAS)로 락 경쟁을 줄이고 중복 송신을 방지한다.
코드 포인트:
- 송신 진입:
Server/ServerEngine/Network/Core/Session.cpp:155 - 큐 플러시/CAS:
.../Session.cpp:248 - 실제 PostSend:
.../Session.cpp:261
OnDisconnected는 CloseConnection() 경로와 recv 오류 경로 모두 로직 스레드풀로 전달되어 실행된다. 즉, 콜백 호출 스레드가 일관되게 유지된다.
코드 포인트:
CloseConnection경로:Server/ServerEngine/Network/Core/BaseNetworkEngine.cpp:155- recv 오류 경로:
.../BaseNetworkEngine.cpp:264
ThreadPool은 내부적으로SafeQueue를 사용Submit()시 큐가 가득 차면 작업 드롭 후 경고 로그- 워커에서 예외를 삼켜 프로세스 전체 실패를 방지
코드 포인트:
Server/ServerEngine/Utils/ThreadPool.h:22Server/ServerEngine/Utils/SafeQueue.h:18
현재 기본 경로에서 TestServer는 연결 이벤트를 직접 받아 DBTaskQueue에 작업을 enqueue한다.
OnClientConnectionEstablished->RecordConnectTimeOnClientConnectionClosed->RecordDisconnectTimeStop()-> active session snapshot ->RecordDisconnectTime
코드 포인트:
- 이벤트 핸들러:
Server/TestServer/src/TestServer.cpp:449,:472 - 큐 enqueue / 라우팅:
Server/TestServer/src/DBTaskQueue.cpp:347,:385 - 워커 실행:
.../DBTaskQueue.cpp:536
중요 설계:
- 현재 런타임 설정은
Initialize(1, ...) - 구현 자체는 워커별 독립 큐 +
sessionId % workerCount라우팅으로 같은 세션 순서를 보장
코드 포인트:
- 워커 구조 / 라우팅:
Server/TestServer/include/DBTaskQueue.h:70,:154 - TestServer 현재 설정값:
Server/TestServer/src/TestServer.cpp:131
DBTaskQueue는 WAL(db_tasks.wal)로 크래시 복구를 지원한다.
- enqueue 전
P|...(pending) 기록 - 성공 처리 후
D|seq기록 - 재시작 시 WAL(+백업
.bak)을 병합 파싱해 미완료 작업만 재큐잉
코드 포인트:
- pending/done 기록:
Server/TestServer/src/DBTaskQueue.cpp:538,:574 - 복구 로직:
.../DBTaskQueue.cpp:597
TestDBServer는 serverId 단위 순서 보장을 위해 OrderedTaskQueue를 사용한다.
- 내부적으로
KeyedDispatcher사용 - 같은 key(serverId)는 같은 worker queue로 라우팅
- worker FIFO로 key 단위 순서 보장
코드 포인트:
- facade:
Server/DBServer/src/OrderedTaskQueue.cpp:29 - keyed dispatch:
.../OrderedTaskQueue.cpp:109 - dispatcher 구현:
Server/ServerEngine/Concurrency/KeyedDispatcher.h:30
Windows 경로에서 TestServer는 DB 연결 끊김 시 재연결 스레드를 실행한다.
- 기본 지수 백오프: 1s -> 2s -> 4s ... max 30s
- 예외:
WSAECONNREFUSED(10061)은 1초 고정 간격 재시도 condition_variable로Stop()시 즉시 깨어나도록 설계
코드 포인트:
- 재연결 루프:
Server/TestServer/src/TestServer.cpp:583 - 에러 구분:
.../TestServer.cpp:629
DB 레이어는 IDatabase / IStatement 인터페이스 기반으로 구현체를 교체 가능하게 설계되어 있다.
- 구현체:
MockDatabase,SQLiteDatabase,ODBCDatabase,OLEDBDatabase - 생성:
DatabaseFactory::CreateDatabase()
코드 포인트:
- 인터페이스:
Server/ServerEngine/Interfaces/IDatabase.h:29 - 팩토리:
Server/ServerEngine/Database/DatabaseFactory.cpp:19
TestServer::Initialize()에서 로컬 DB를 선택한다.
dbConnectionString비어있음:MockDatabase- 값 있음:
SQLiteDatabase+ 해당 파일 경로
선택된 DB 인스턴스를 DBTaskQueue에 주입하고, 큐에서 아래 테이블을 보장한다.
SessionConnectLogSessionDisconnectLogPlayerData
코드 포인트:
- DB 선택/주입:
Server/TestServer/src/TestServer.cpp:81 - 큐 테이블 보장:
Server/TestServer/src/DBTaskQueue.cpp:73
TestDBServer는 ServerPacketHandler + ServerLatencyManager + OrderedTaskQueue 조합으로 동작한다.
ServerPingReq수신 시 RTT 계산 후RecordLatencyDBSavePingTimeReq수신 시SavePingTime실행 후 응답 패킷 전송
코드 포인트:
- 핸들러 라우팅:
Server/DBServer/src/ServerPacketHandler.cpp:40 - RTT 기록:
.../ServerPacketHandler.cpp:116 - ping 저장:
.../ServerPacketHandler.cpp:196
현재 상태(중요):
TestDBServer경로에는ServerLatencyManager::SetDatabase()주입 코드가 없음ServerLatencyManager::ExecuteQuery()는 DB 미주입 시 true 반환(log-only)
즉, 현재 기본 실행 경로에서는 메모리 통계/로그 중심이며, 영구 저장은 DB 주입이 연결되어야 활성화된다.
코드 포인트:
- TestDBServer 초기화:
Server/DBServer/src/TestDBServer.cpp:68 - DB 미주입 시 log-only:
Server/DBServer/src/ServerLatencyManager.cpp:331
DBServer.cpp는 데드 코드로 확인되어 삭제되었습니다. 유일한 실행 경로는 TestDBServer입니다.
- 클라이언트 TCP 접속
- 엔진 accept 후 Session 생성/associate
SessionManager::CreateSession()이Core::Session을 생성/초기화SetSessionConfigurator()가 recv 콜백을 부착NetworkEvent::Connected->OnClientConnectionEstablished()-> DBTaskQueue enqueue- 클라이언트
SessionConnectReq처리 후SessionConnectRes반환 - 주기적 PingReq/PongRes
관련 코드:
- accept / CreateSession:
Server/ServerEngine/Network/Platforms/WindowsNetworkEngine.cpp:128,:175 - session configurator:
Server/ServerEngine/Network/Core/SessionManager.h:48 - 접속 기록 enqueue:
Server/TestServer/src/TestServer.cpp:449 - 핸드셰이크 처리:
Server/TestServer/src/ClientPacketHandler.cpp:102
- TestServer가
ConnectToDBServer()로 별도 소켓 연결(Windows) TimerQueue::ScheduleRepeat()가SendDBPing()을 주기 호출- TestDBServer가
ServerPongRes응답 - 주기적으로
PKT_DBSavePingTimeReq전송/응답 - 끊김 시
DBReconnectLoop재시도
관련 코드:
- 연결/스레드 시작:
Server/TestServer/src/TestServer.cpp:336 - ping 스케줄링:
.../TestServer.cpp:430 - DBServer 패킷 처리:
Server/DBServer/src/ServerPacketHandler.cpp:116
Stop()에서 아래 순서를 지킨다.
- DB 재연결 루프 깨움 및 join
- 연결 중 세션의 disconnect 기록 enqueue
DBTaskQueue->Shutdown()으로 큐 드레인- 로컬 DB disconnect
- DB 서버 소켓 disconnect
- 클라이언트 엔진 stop
코드 포인트:
- 전체 순서:
Server/TestServer/src/TestServer.cpp:165
- 네트워크 엔진 stop
- OrderedTaskQueue shutdown(남은 작업 처리)
- ServerLatencyManager shutdown
코드 포인트:
Server/DBServer/src/TestDBServer.cpp:160
- 네트워크/로직/DB 비동기 경로 분리 명확
- 플랫폼 폴백 체인과 공통 추상화 정리됨
DBTaskQueueWAL 복구로 종료/크래시 안정성 강화- 재연결 정책(ECONNREFUSED 분리)으로 운영 복원력 확보
TestServer의 DB 서버 소켓 경로는 Windows 전용 (#ifdef _WIN32)TestDBServer기본 경로는 현재 DB 주입 없음 -> 영구 DB 저장은 비활성(log-only)- TestDBServer 기본 포트(코드 기본 8001)와 실행 스크립트 포트(문서/스크립트에서 8002 사용 가능) 혼동 위험
TestDBServer에도 설정 기반 DB 주입(SetDatabase) 경로를 main 옵션으로 연결- TestServer의 DB 서버 연결 경로를 플랫폼 공통 소켓/AsyncIO 경로로 통합
운영 문서에 "기본 실행 경로(TestDBServer) vs 실험 경로(DBServer.cpp)"를 명시(DBServer.cpp 제거 완료)- 포트 기본값(8001/8002) 정책을 코드/스크립트/문서에서 단일화
Server/ServerEngine/Network/Core/BaseNetworkEngine.cppServer/ServerEngine/Network/Core/Session.cppServer/ServerEngine/Network/Platforms/WindowsNetworkEngine.cppServer/ServerEngine/Network/Platforms/LinuxNetworkEngine.cppServer/TestServer/src/TestServer.cppServer/TestServer/src/ClientSession.cppServer/TestServer/src/DBTaskQueue.cppServer/TestServer/src/ClientPacketHandler.cppServer/DBServer/src/TestDBServer.cppServer/DBServer/src/ServerPacketHandler.cppServer/DBServer/src/OrderedTaskQueue.cppServer/DBServer/src/ServerLatencyManager.cpp