37. 협상된 세션 버전을 무시한 고정 레이아웃 파싱 (C#)

난이도 중 해설 보기 →

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

결함 코드 · C#
// 시나리오: 프로토콜 버전 협상 후, 세션별 버전에 맞춘 패킷 파싱
//
// 핸드셰이크에서 클라이언트와 서버는 사용할 프로토콜 버전을 협상하고, 그 결과를
// 세션에 저장한다(Session.NegotiatedVersion). 이동 패킷 레이아웃은 버전에 따라 다르다:
//   v1 레이아웃: [seq:int32][tickMs:int32]                     (8바이트)
//   v2 레이아웃: [seq:int32][tickMs:int32][inputFlags:int32]   (12바이트, v2에서 필드 추가)
//
// 한 수신 버퍼에는 같은 연결에서 온 여러 패킷이 연이어 담길 수 있고, 파서는 오프셋을
// 전진시키며 순서대로 읽는다.
//
// 요구사항:
//  - 각 세션의 협상된 버전에 맞는 레이아웃으로 파싱해야 한다(v1 클라는 inputFlags 없음).
//  - 파서가 소비하는 바이트 수는 그 패킷의 실제 크기와 정확히 일치해야 한다
//    (그래야 다음 패킷 경계가 맞는다).
//
// 아래 파서의 (A)(B) 지점을 검토하라.

using System;

public sealed class Session
{
    public int NegotiatedVersion;   // 핸드셰이크에서 정해져 저장됨 (1 또는 2)
}

public struct MoveInput { public int Seq; public int TickMs; public int InputFlags; }

public sealed class MovePacketParser
{
    // buf[pos..] 에서 이동 패킷 하나를 읽고, pos 를 소비한 만큼 전진시킨다.
    public MoveInput Parse(Session session, byte[] buf, ref int pos)
    {
        var m = new MoveInput
        {
            Seq     = BitConverter.ToInt32(buf, pos),
            TickMs  = BitConverter.ToInt32(buf, pos + 4),
            InputFlags = BitConverter.ToInt32(buf, pos + 8),   // (A)
        };
        pos += 12;                                             // (B)
        return m;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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