Session 구현

Jaemyeong Lee·2025년 3월 23일

게임 서버1

목록 보기
147/220

Register vs Process: 구현의 뼈대

두 단계의 역할

구분RegisterProcess
의미비동기 I/O 요청 등록완료된 I/O 결과 처리
시점"지금 일 시켜둠""나중에 완료 알림 받음"
실행 주체호출한 스레드IOCP 워커 스레드

쌍으로 동작

  • RegisterConnect <-> ProcessConnect
  • RegisterRecv <-> ProcessRecv
  • RegisterSend <-> ProcessSend
  • RegisterDisconnect <-> ProcessDisconnect

핵심 규칙

  • Register 없이 Process는 오지 않습니다.
  • Process가 끝난 뒤 다음 Register를 누락하면 파이프라인이 멈춥니다.

Recv와 Send의 본질적 차이

비교

항목RecvSend
트리거 주체상대가 보낼 때내가 보내고 싶을 때
일반 패턴세션당 1개의 outstanding Recv송신 큐 기반 다건 송신
재등록ProcessRecv 끝에서 필수큐 잔량/전송 상태에 따라 등록
동기화상대적으로 단순멀티스레드 경쟁 관리 필수

Recv 1개 정책이 흔한 이유

  • 완료 순서/버퍼 관리가 단순해집니다.
  • 디버깅이 쉬워지고 레이스 가능성이 줄어듭니다.
  • 대부분의 게임 서버에서 처리량 대비 안정성이 좋습니다.

Send는 큐 중심으로 설계

  • 여러 스레드가 동시에 Send를 호출할 수 있으므로 세션별 송신 큐가 필요합니다.
  • "전송 중 플래그"로 중복 RegisterSend를 막는 패턴이 실무 표준입니다.

Session 콜백 계약(Contract)

콜백 포인트

함수의미
OnConnected연결 성립 알림
OnRecv수신 데이터 처리 요청
OnSend송신 완료 알림
OnDisconnected연결 종료 알림

OnRecv 반환 규칙

  • 반환값은 "소비한 바이트 수"입니다.
  • 반드시 0 <= processedLen <= dataSize를 만족해야 합니다.
  • 이 규칙이 깨지면 ReceiveBuffer 커서가 망가져 패킷 파싱이 무너집니다.

스레드 경계

  • 콜백은 보통 워커 스레드에서 호출됩니다.
  • 무거운 게임 로직은 JobQueue로 넘겨 워커 점유 시간을 짧게 유지해야 합니다.

ProcessRecv 구현 핵심

표준 순서

1) OnWrite(numBytes)            // 수신된 바이트 반영
2) dataSize 계산
3) OnRecv(readPtr, dataSize)    // 컨텐츠가 소비 길이 반환
4) OnRead(processedLen)         // 소비 반영
5) Clean()                      // 커서 정리/압축
6) RegisterRecv()               // 다음 수신 재등록

종료/오류 분기

  • numBytes == 0이면 원격 종료로 보고 Disconnect 경로로 전환합니다.
  • 실패 완료(GQCS FALSE)도 정리 경로로 안전하게 흘려야 합니다.

안전 검증 코드

int32 processed = OnRecv(buffer, dataSize);
if (processed < 0 || processed > dataSize)
{
    // 프로토콜/파서 오류로 간주하고 종료
    Disconnect(L"invalid processed length");
    return;
}

ProcessSend 구현 핵심

흔한 패턴

  1. 송신 완료 바이트만큼 큐/버퍼에서 소모
  2. 잔여 데이터가 있으면 다음 RegisterSend
  3. 잔여가 없으면 "전송 중 플래그" 해제

부분 전송 대응

  • 한 번의 Send 완료가 요청한 전체 바이트를 보장하지 않습니다.
  • 잔여 길이를 계산해 이어서 등록해야 합니다.

중복 등록 방지

if (!sending.exchange(true)) {
    RegisterSend();
}
  • 이미 전송 중이면 큐 적재만 하고 등록은 기존 루프에 맡깁니다.

Disconnect 구현 원칙

idempotent 보장

  • Disconnect()는 여러 번 호출돼도 한 번만 의미 있게 동작해야 합니다.
  • 상태 전이를 Connected -> Disconnecting -> Disconnected로 고정하면 안전합니다.

순서

  1. 신규 Register 차단
  2. 소켓 shutdown/close
  3. 늦게 오는 완료 처리
  4. OnDisconnected 1회 호출

재진입 주의

  • OnDisconnected에서 다시 Disconnect()를 호출해도 문제 없게 설계해야 합니다.

자주 나는 버그와 점검표

버그증상원인
Recv 재등록 누락어느 순간부터 수신 정지ProcessRecv 말미 Register 빠짐
Send 중복 등록패킷 순서 꼬임/중복 전송전송 중 플래그 부재
잘못된 processedLen패킷 파싱 붕괴OnRecv 계약 위반
종료 중 콜백 레이스간헐 크래시상태 전이/수명 정책 미흡

운영 지표

  • 세션당 outstanding Recv 수
  • 세션별 송신 큐 길이
  • 초당 Disconnect 사유 코드 분포
  • OnRecv 파싱 실패 횟수

강의 시 유의사항

강조 포인트

  • Session 구현의 본질은 "성공 경로"보다 "실패/종료 경로를 끊기지 않게 잇는 것"입니다.
  • Recv는 재등록 루프, Send는 큐 루프라는 차이를 분명히 가르치세요.
  • Part 9(ReceiveBuffer)와 연결해 OnRecv 반환 계약을 반드시 강조하세요.

자주 하는 오해

오해바로잡기
Process 함수는 완료 알림만 처리하면 된다재등록/정리/상태 전이까지 포함해야 한다
Send는 호출할 때마다 바로 Register하면 된다중복 등록 방지 플래그/큐 설계가 필요하다
OnRecv가 아무 값이나 반환해도 된다커서 일관성 규칙을 깨면 즉시 장애로 이어진다

체크 질문 (스스로 답해보기)

  • numBytes == 0 수신 시 Session은 어떤 상태 전이와 정리 순서를 따라야 하는가?
  • Send 큐에서 중복 Register를 막지 않으면 어떤 문제(순서/성능)가 생기는가?
  • OnRecvdataSize보다 큰 값을 반환했을 때 왜 즉시 종료해야 하는가?

profile
李家네_공부방

0개의 댓글