37. 재접속 재바인딩 시 이전 연결 미정리로 인한 유령 연결 (C#)

난이도 상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 접속 이탈-재접속(reconnect) 처리
//
// 모바일 클라이언트는 네트워크가 자주 끊긴다. 우리는 세션을 즉시 파기하지 않고
// 짧은 유예 동안 유지하다가, 클라이언트가 reconnectToken 으로 다시 붙으면 기존
// 세션에 새 연결을 "재바인딩"해 진행 중이던 상태(전투/파티/버프)를 이어준다.
//
// 요구사항:
//  - 하나의 논리 세션에는 항상 최대 1개의 활성 물리 연결만 매여 있어야 한다.
//  - 재바인딩되면 이전 연결로는 더 이상 이 세션에 데이터가 흐르면 안 된다.
//  - 각 연결은 독립된 수신 루프를 돌리며, 받은 패킷을 바인딩된 세션에 전달한다.
//  - 재접속 요청은 이전 연결의 수신 루프가 아직 살아있는 동안에도 들어올 수 있다.
//
// 아래 매니저의 (A)(B) 지점을 검토하라.

using System.Collections.Generic;

public sealed class Connection
{
    public readonly int SocketId;
    public Session BoundSession;
    public bool Closed;
    public Connection(int id) { SocketId = id; }

    public void Send(byte[] data) { /* 소켓으로 송신 */ }
    public void Close() { Closed = true; /* 소켓 닫기 */ }

    // 각 연결의 수신 스레드 루프가 패킷마다 이걸 호출한다.
    public void OnPacket(byte[] data)
    {
        BoundSession?.Handle(data);   // (B)
    }
}

public sealed class Session
{
    public readonly string PlayerId;
    public Connection Conn;
    public Session(string playerId) { PlayerId = playerId; }
    public void Handle(byte[] data) { /* 게임 로직 적용 */ }
}

public sealed class SessionManager
{
    private readonly Dictionary<string, Session> _byToken = new();

    // 재접속: 새 연결 newConn 이 token 으로 기존 세션에 다시 붙는다.
    public bool Reconnect(string token, Connection newConn)
    {
        if (!_byToken.TryGetValue(token, out var session))
            return false;

        // 기존 세션에 새 연결을 매단다.
        session.Conn = newConn;          // (A)
        newConn.BoundSession = session;
        return true;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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