21. 재접속 시 기존 세션 재바인딩과 옛 연결의 늦은 콜백/패킷

난이도 상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 재접속 시 기존 세션 재바인딩과 옛 연결의 늦은 콜백/패킷
// ----------------------------------------------------------------------------
// 시나리오 (세션/접속 · 서버-클라):
//   - 플레이어가 끊겼다가 재접속하면, 서버는 인게임 상태(HP, 위치 등)를 보존하기 위해
//     "기존 세션 객체"를 찾아 새 연결로 재바인딩(reattach)한다.
//   - 그런데 옛 연결의 이벤트는 늦게 도착할 수 있다: 옛 소켓의 종료 콜백(OnDisconnect)이나
//     아직 처리 중이던 패킷(OnPacket)이 재접속 "이후에" 도착할 수 있다(서로 다른 IO 스레드).
//
// 요구사항:
//   - 재접속으로 세션이 새 연결에 바인딩된 뒤에는, "옛 연결"에서 온 종료/패킷이 새 연결의
//     세션을 종료시키거나 상태를 바꾸면 안 된다(현재 바인딩된 연결의 이벤트만 유효).
//   - 어떤 시점에도 한 계정의 살아있는 세션은 정확히 하나여야 하고, 정상 접속 중인
//     플레이어가 옛 연결 정리 때문에 강제 종료되면 안 된다.
//
// 과제:
//   이 세션 매니저의 잠재적 문제를 모두 찾고, 재접속 직후 옛 연결의 OnDisconnect/OnPacket 이
//   도착하면 어떤 일이 벌어지는지 설명하고, 수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

using System.Collections.Generic;

public class Session
{
    public long   AccountId;
    public object Connection;   // 현재 바인딩된 연결
    public int    Hp = 100;
    public bool   Alive = true;
}

public class SessionManager
{
    private readonly Dictionary<long, Session> _byAccount = new();

    public Session OnConnect(long accountId, object newConn)
    {
        if (_byAccount.TryGetValue(accountId, out var s))
        {
            s.Connection = newConn;          // (A)
            s.Alive = true;
            return s;
        }
        s = new Session { AccountId = accountId, Connection = newConn };
        _byAccount[accountId] = s;
        return s;
    }

    public void OnDisconnect(long accountId, object conn)
    {
        if (_byAccount.TryGetValue(accountId, out var s))
        {
            s.Alive = false;                 // (B)
            _byAccount.Remove(accountId);    // (C)
        }
    }

    public void OnPacket(long accountId, object conn, int damage)
    {
        if (_byAccount.TryGetValue(accountId, out var s))
        {
            s.Hp -= damage;                  // (D)
        }
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.