29. 아웃바운드 연결의 완료·실패·타임아웃 미처리 (서버-서버, C#)

난이도 중 해설 보기 →

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

결함 코드 · C#
// 시나리오: 게이트웨이 서버가 존/인증 서버로 나가는 아웃바운드 연결을 맺는다(서버-서버).
// 다운스트림이 잠깐 죽거나 방화벽에 막히면 connect 가 실패하거나 응답이 없을 수 있다.
// 연결이 되면 그 세션으로 RPC 를 흘려보낸다.
//
// 요구사항:
//   - Dial: 대상 주소로 비동기 연결을 시작하고, 연결이 "완료"되면 준비된 세션으로 등록한다.
//   - Send: 준비된 세션으로만 메시지를 보낸다.
//   - 다운스트림이 응답 없이 매달려도(black-hole) 연결 시도가 무한정 남아선 안 된다.
//
// 과제: (A)(B) 지점을 검토하라. 다운스트림이 죽거나 느릴 때 실패한/미완성 연결로
//       전송하거나 시도가 새어(leak) 쌓일 수 있다. 어떤 상황에서 그런지 밝히고 고쳐라.

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

public sealed class OutboundPool
{
    private readonly ConcurrentDictionary<string, Socket> _ready = new();

    public void Dial(string key, IPEndPoint remote)
    {
        var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        // 연결을 시작하고, 준비 맵에 넣는다.
        _ = sock.ConnectAsync(remote);     // (A) 완료를 기다리거나 성패를 관측하지 않음
        _ready[key] = sock;                // (B) 완료 전에 "준비됨"으로 등록, 타임아웃 없음
    }

    public bool Send(string key, byte[] frame)
    {
        if (!_ready.TryGetValue(key, out var sock)) return false;
        sock.Send(frame);                  // 준비된 세션으로 전송
        return true;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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