6. C# 죽은 연결 감지: TCP KeepAlive 설정 vs 앱 레벨 Ping

난이도 하 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 죽은 연결 감지: TCP KeepAlive 설정 vs 앱 레벨 Ping
// ----------------------------------------------------------------------------
// 시나리오:
//   - 캐주얼 게임 로비 서버. 클라가 접속 후 매칭/채팅만 하는 동안에는
//     애플리케이션 트래픽이 거의 없는 "조용한 연결"이 길게 유지된다.
//   - 모바일 환경 특성상 NAT/방화벽이 일정 시간 무트래픽 연결을 조용히
//     끊거나, 단말이 지하철·엘리베이터에서 갑자기 사라지는 일이 잦다.
//   - 운영팀이 "유령 연결이 너무 오래 살아있다"고 보고했고, 담당자는
//     OS의 TCP KeepAlive 를 켜서 죽은 연결을 OS가 알아서 정리하게 했다.
//
// 요구사항:
//   - 단말이 사라진 죽은 연결(half-open 포함)은 수십 초~수 분 내에 감지·정리.
//   - 살아있는 조용한 연결을 오인 끊김 시키지 않는다.
//   - 연결이 죽었다고 판정되면 세션 정리 콜백이 정확히 한 번 실행돼야 한다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
//   수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;

public sealed class LobbyConnection
{
    private readonly Socket _socket;

    public LobbyConnection(Socket socket)
    {
        _socket = socket;
        EnableKeepAlive();   // (A)
    }

    private void EnableKeepAlive()
    {
        // SO_KEEPALIVE 켜기
        _socket.SetSocketOption(SocketOptionLevel.Socket,
                                SocketOptionName.KeepAlive, true);   // (B)

        // 원하던 동작: 10초 무트래픽이면 keepalive 프로브 시작,
        // 5초 간격으로 재시도, 이 정도면 죽은 연결을 곧 OS가 끊어줄 것이다.
        // (Windows 의 keepalive 파라미터를 IOControl 로 설정)
        uint onoff   = 1;
        uint timeMs  = 10_000;   // (C) 의도: keepalive 시작까지 10초
        uint intvlMs = 5_000;    // (C) 의도: 프로브 간격 5초

        byte[] inValue = new byte[12];
        BitConverter.GetBytes(onoff).CopyTo(inValue, 0);
        BitConverter.GetBytes(timeMs).CopyTo(inValue, 4);
        BitConverter.GetBytes(intvlMs).CopyTo(inValue, 8);

        _socket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
    }

    // 평소엔 거의 호출되지 않는다(조용한 연결). 죽은 연결도
    // 앱이 능동적으로 보내는 트래픽이 없으면 이 경로를 거의 안 탄다.
    public void OnAppDataReceived(byte[] data)
    {
        // ... 매칭/채팅 처리
    }

    // OS keepalive 가 연결이 죽었다고 판단하면, 다음 번 수신 시도에서
    // 에러가 올라오고 그때 세션을 정리하면 된다고 가정한다. (D)
    public void OnRecvError(SocketException ex)
    {
        Console.WriteLine($"recv error {ex.SocketErrorCode}, cleaning up");
        Cleanup();
    }

    public void Cleanup()
    {
        // 세션 정리, 매칭 큐에서 제거, 메모리 회수 등 ...
        _socket.Close();
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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