32. 수신 프레임 조립 버퍼의 최대 크기 상한 부재

난이도 중 해설 보기 →

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

결함 코드 · C#
// 시나리오 (서버-클라): TCP 는 스트림이라 한 번의 Read 로 완전한 프레임이
// 도착한다는 보장이 없다. 이 서버는 세션마다 수신 버퍼를 두고, Read 로 들어온
// 바이트를 계속 이어붙이다가(누적) 길이 프리픽스로 표시된 만큼 바이트가
// 모이면 하나의 프레임으로 꺼내 처리한다.
//
// 정상 클라이언트는 항상 작고 완결된 프레임을 보내지만, 네트워크 상황이나
// 악의적/오작동 클라이언트는 프레임의 나머지 부분을 아주 느리게 보내거나
// 아예 보내지 않을 수 있다. 그 사이에도 서버는 이미 도착한 바이트를 세션의
// 누적 버퍼에 계속 붙잡아 둔다.
//
// 요구사항:
//   - 정상적인 크기의 프레임은 지금처럼 정상 조립되어야 한다.
//   - 한 세션이 프레임을 끝까지 보내지 않거나 비정상적으로 큰 길이를
//     주장하더라도, 그 세션 하나 때문에 서버 전체 메모리가 고갈되어서는
//     안 된다.
//
// 과제: (A)(B) 지점을 검토해 위 요구사항이 깨지는 경로를 찾고 수정하라.

using System;
using System.Collections.Generic;

public sealed class SessionRecvBuffer
{
    private readonly List<byte> _accum = new();

    // 소켓에서 새로 읽은 바이트 조각을 누적 버퍼에 이어붙인다.
    public void OnBytesReceived(byte[] chunk, int length)
    {
        // (A) 이미 누적된 바이트 크기와 무관하게 항상 이어붙인다.
        for (int i = 0; i < length; i++)
            _accum.Add(chunk[i]);
    }

    // 누적 버퍼에서 완성된 프레임을 꺼낼 수 있으면 꺼낸다.
    // 프레임 포맷: [4바이트 길이 프리픽스][길이만큼의 페이로드]
    public bool TryExtractFrame(out byte[] payload)
    {
        payload = null;
        if (_accum.Count < 4)
            return false;

        int declaredLen = BitConverter.ToInt32(new[] { _accum[0], _accum[1], _accum[2], _accum[3] }, 0);

        // (B) declaredLen 이 비정상적으로 큰 값이어도 그대로 받아들이고,
        //     그만큼 모일 때까지 계속 기다린다.
        if (_accum.Count < 4 + declaredLen)
            return false; // 아직 다 도착하지 않음 — 더 기다린다.

        payload = new byte[declaredLen];
        for (int i = 0; i < declaredLen; i++)
            payload[i] = _accum[4 + i];

        _accum.RemoveRange(0, 4 + declaredLen);
        return true;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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