26. 시퀀스 번호 랩어라운드와 시리얼 산술 (최신/과거 판정 붕괴)

난이도 상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 신뢰성 UDP(RUDP) 수신측의 시퀀스 번호 처리.
// 각 패킷에 16비트 시퀀스 번호(0~65535, 다 쓰면 0으로 순환)가 붙는다.
// 수신측은 (1) 받은 패킷이 지금까지 본 것보다 "더 최신"인지 판정해 상태를 갱신하고,
//          (2) 최근 윈도우로 중복/재전송(리플레이)을 걸러낸다.
//
// 요구사항:
//  - 시퀀스가 65535 다음 0 으로 되돌아가도(wrap) "최신/과거" 판정이 정확해야 한다.
//  - 지연·재정렬 도착과 중복을 올바르게 구분해야 한다.
//  - 세션은 장시간 유지되므로 시퀀스는 한 바퀴 이상 돈다.
//
// 과제: (A)(B) 를 중심으로, 시퀀스가 경계(65535→0)를 넘을 때 어떤 오작동이
//       나는지(구체적 값으로)와 수정안을 제시하라.

using System.Collections.Generic;

public sealed class SequenceReceiver
{
    private const int WindowSize = 256;

    private ushort _lastSeq;          // 지금까지 본 가장 최신 시퀀스
    private bool _hasAny;
    private readonly HashSet<ushort> _recent = new();   // 최근 본 시퀀스(중복 차단)

    // 더 최신이면 처리(true), 과거 윈도우 밖이거나 중복이면 버림(false).
    public bool Accept(ushort seq)
    {
        if (!_hasAny) { _hasAny = true; _lastSeq = seq; Remember(seq); return true; }

        if (seq > _lastSeq)                       // (A) "이게 더 최신인가?"
        {
            _lastSeq = seq;
            if (!_recent.Add(seq)) return false;  // 이미 본 것이면 중복
            Trim();
            return true;
        }

        // 과거 쪽: 최근 윈도우 안의 재정렬 도착만 받아들인다.
        if (_lastSeq - seq <= WindowSize)         // (B) "윈도우 안의 과거인가?"
        {
            if (!_recent.Add(seq)) return false;
            Trim();
            return true;
        }

        return false;   // 너무 오래된 것으로 보고 폐기
    }

    private void Remember(ushort seq) { _recent.Add(seq); Trim(); }

    private void Trim()
    {
        // 윈도우 크기 유지(상세 생략): 오래된 시퀀스를 _recent 에서 제거.
        if (_recent.Count > WindowSize * 2) _recent.Clear();
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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