Listener

Jaemyeong Lee·2025년 3월 10일

게임 서버1

목록 보기
143/220

Listener의 책임과 경계

한 줄 정의

Listener는 "접속 수락 파이프라인(AcceptEx)을 유지"하는 서버 전용 IocpObject입니다.

책임/비책임

구분Listener가 한다Listener가 하지 않는다
네트워크listen 소켓 관리, AcceptEx 선등록/재등록게임 규칙 처리
세션접속 완료된 소켓을 Session으로 연결플레이어 상태 갱신
운영accept 파이프라인 깊이 유지DB 호출/비즈니스 로직

구조

class Listener : public IocpObject
{
public:
    HANDLE GetHandle() override;
    void Dispatch(IocpEvent* event, int32 numOfBytes) override;
    bool StartAccept(const ServerServiceRef& service);
};

StartAccept 부트스트랩

초기화 순서

순서작업핵심 포인트
1listen 소켓 생성Overlapped 소켓으로 생성
2소켓 옵션 설정재시작 정책(SO_REUSEADDR/SO_EXCLUSIVEADDRUSE)
3Bind + Listen접속 수신 가능 상태 진입
4IOCP 등록Listener를 Completion Key로 연결
5AcceptEx 선등록acceptCount만큼 미리 낚싯대 던짐

예시 코드

bool Listener::StartAccept(const ServerServiceRef& service)
{
    _service = service;
    _listenSocket = SocketUtils::CreateSocket();
    if (_listenSocket == INVALID_SOCKET)
        return false;

    SocketUtils::SetReuseAddress(_listenSocket, true);
    SocketUtils::Bind(_listenSocket, _service->GetNetAddress());
    SocketUtils::Listen(_listenSocket);

    if (!GIocpCore.Register(shared_from_this()))
        return false;

    for (int32 i = 0; i < _acceptCount; ++i)
    {
        AcceptEvent* ev = PopAcceptEventFromPool(); // 혹은 생성
        ev->Init();
        ev->owner = shared_from_this();
        if (!RegisterAccept(ev))
            return false;
    }
    return true;
}

acceptCount의 의미

  • 선등록 수가 너무 작으면 접속 피크에서 지연/드랍이 늘어납니다.
  • 너무 크면 이벤트/세션 임시 자원 사용량이 증가합니다.
  • 시작값은 워커 수의 2~4배 수준으로 두고 지표로 조정하는 방식이 실무에서 안전합니다.

RegisterAccept (AcceptEx 등록 단계)

AcceptEx 핵심 인자

인자의미
listen socket접속을 받는 소켓
accept socket미리 생성한 클라이언트 소켓
output buffer초기 수신 데이터 + local/remote 주소 저장 버퍼
OVERLAPPED*완료 시 되돌아올 Accept 이벤트 컨텍스트

주소 버퍼 크기 공식은 보통 (주소구조체 크기 + 16) * 2 + 초기수신크기를 사용합니다.

반환값 해석

결과해석대응
TRUE즉시 완료곧바로 완료 처리 가능
FALSE + WSA_IO_PENDING정상 대기완료 통지 기다림
그 외 오류등록 실패소켓 정리 후 재시도/로그

예시 코드

bool Listener::RegisterAccept(AcceptEvent* ev)
{
    ev->Init();
    ev->session = _service->CreateSession(); // accept용 소켓 보유

    DWORD bytes = 0;
    BOOL ok = SocketUtils::AcceptEx(
        _listenSocket,
        ev->session->GetSocket(),
        ev->addressBuffer,
        0,                       // 초기 수신 데이터 길이
        kAddressBytes, kAddressBytes,
        &bytes,
        &ev->overlapped);

    if (ok == TRUE)
        return true;

    int32 err = WSAGetLastError();
    return (err == WSA_IO_PENDING);
}

ProcessAccept (완료 처리 단계)

호출 경로

GQCS -> Listener::Dispatch -> ProcessAccept

처리 순서

순서작업이유
1SO_UPDATE_ACCEPT_CONTEXT 적용accept 소켓을 listen 컨텍스트와 연결
2원격 주소 추출세션 주소 정보 설정
3Session을 IOCP에 등록이후 Recv/Send 완료 수신 준비
4서비스에 세션 추가세션 라이프사이클 관리 시작
5ProcessConnect/RegisterRecv수신 파이프라인 시작
6RegisterAccept 재호출다음 접속을 위한 파이프라인 유지

실패 경로 규칙

  • 중간 단계 실패 시 해당 accept socket/session은 정리합니다.
  • 성공/실패와 무관하게 마지막에 Accept 재등록을 보장해야 파이프라인이 끊기지 않습니다.
  • 이 재등록을 finally 성격 코드로 고정하면 실수를 크게 줄일 수 있습니다.

주소 처리와 NetAddress 연동

주소 추출 방식

방식장점주의
GetAcceptExSockaddrsAcceptEx 버퍼에서 바로 추출 가능버퍼 크기/오프셋 규칙 정확히 필요
getpeername코드가 직관적SO_UPDATE_ACCEPT_CONTEXT 적용 후 사용 권장

NetAddress 반영

NetAddress addr = SocketUtils::GetRemoteAddress(ev->addressBuffer);
session->SetNetAddress(addr);

운영 관점

  • 접속 로그에 sessionId, ip, port, acceptLatency를 남기면 장애 분석이 쉬워집니다.

AcceptEx vs 일반 accept (엔진 관점)

항목일반 acceptAcceptEx
모델동기/논블로킹 기반 루프IOCP 완료 기반 비동기
소켓 생성accept가 반환미리 생성해 전달
완료 통지직접 호출 결과 확인Completion Port에서 수신
초기 데이터/주소별도 API 호출output buffer로 함께 처리 가능
대규모 서버 적합성제한적높음(IOCP 파이프라인과 궁합)

운영 튜닝 포인트

선등록 깊이 관리

  • acceptCount가 낮으면 접속 피크에서 큐 대기 증가
  • acceptCount가 높으면 메모리/이벤트 풀 사용량 증가

관측 지표

  • 초당 접속 수(accept completions/sec)
  • accept 등록 실패율
  • 접속 지연(accept 완료 -> session connect 완료)
  • 선등록 이벤트 잔량(현재 pending accept 수)

자주 발생하는 장애 패턴

패턴증상원인
재등록 누락일정 시점부터 신규 접속 정지ProcessAccept 후 재등록 빠짐
컨텍스트 미적용getpeername 실패/주소 이상SO_UPDATE_ACCEPT_CONTEXT 누락
풀 고갈접속 폭주 시 실패율 상승이벤트/세션 풀 크기 부족

강의 시 유의사항

강조 포인트

  • Listener는 "접속 파이프라인 관리자"이지 "게임 로직 실행기"가 아닙니다.
  • RegisterAccept -> ProcessAccept -> RegisterAccept 순환이 핵심입니다.
  • 재등록 누락은 실습에서 가장 먼저 확인해야 할 체크포인트입니다.

자주 하는 오해

오해바로잡기
접속 완료 후 등록은 끝났다Accept 파이프라인은 항상 일정 깊이로 유지해야 한다
WSA_IO_PENDING은 오류다정상 비동기 대기 상태다
Address 추출은 아무 때나 가능하다컨텍스트 업데이트/버퍼 규칙을 지켜야 안전하다

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

  • 왜 Listener는 성공/실패와 무관하게 Accept를 재등록해야 하는가?
  • acceptCount를 늘릴지 줄일지 어떤 지표로 판단할 것인가?
  • AcceptEx 완료 후 Session 수명과 이벤트 수명을 어떻게 연결해 안전하게 관리할 것인가?

profile
李家네_공부방

0개의 댓글