36. 송신 백프레셔 부재: 무제한 송신 큐와 블로킹 send (C#)

난이도 중 해설 보기 →

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

결함 코드 · C#
// 시나리오: 게임 서버 — 세션 송신 경로(브로드캐스트/푸시)
//
// 서버는 존(zone) 안의 모든 세션에 상태 업데이트를 브로드캐스트한다.
// 각 세션은 자기 송신 큐를 갖고, 워커가 큐를 비우며 소켓으로 내보낸다.
// 느린(혹은 악의적으로 느린) 클라이언트가 데이터를 잘 받아가지 않을 수 있다.
//
// 요구사항:
//  - 한 느린 클라이언트가 서버 전체(메모리/이벤트 루프)를 위협하면 안 된다.
//  - 브로드캐스트를 호출하는 게임 로직 스레드가 소켓 I/O 때문에 막히면 안 된다.
//  - 필요 시 뒤처진 세션은 스로틀/드롭/연결종료로 격리한다.
//
// 아래 세션 송신 코드를 검토하라.

using System;
using System.Collections.Generic;
using System.Net.Sockets;

public sealed class Session
{
    private readonly Socket _socket;
    private readonly Queue<byte[]> _sendQueue = new();

    public Session(Socket socket) { _socket = socket; }

    // 게임 로직 스레드가 호출: 브로드캐스트할 프레임을 큐에 넣는다.
    public void Enqueue(byte[] frame)
    {
        lock (_sendQueue)
        {
            _sendQueue.Enqueue(frame);          // (A)
        }
        Flush();
    }

    // 큐를 소켓으로 밀어낸다.
    private void Flush()
    {
        while (true)
        {
            byte[] frame;
            lock (_sendQueue)
            {
                if (_sendQueue.Count == 0) return;
                frame = _sendQueue.Peek();
            }

            int sent = _socket.Send(frame, 0, frame.Length, SocketFlags.None); // (B)

            lock (_sendQueue)
            {
                _sendQueue.Dequeue();
            }
        }
    }
}

// 브로드캐스트: 게임 로직 스레드에서 존의 모든 세션에 푸시
public static class Zone
{
    public static void Broadcast(List<Session> sessions, byte[] frame)
    {
        foreach (var s in sessions)             // (C)
            s.Enqueue(frame);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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