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++
// ============================================================================
// [코드리뷰 문제] C++ - 죽은 연결 감지: TCP KeepAlive 설정 vs 앱 레벨 Ping
// ----------------------------------------------------------------------------
// 시나리오:
// - 캐주얼 게임 로비 서버. 클라가 접속 후 매칭/채팅만 하는 동안에는
// 애플리케이션 트래픽이 거의 없는 "조용한 연결"이 길게 유지된다.
// - 모바일 환경 특성상 NAT/방화벽이 일정 시간 무트래픽 연결을 조용히
// 끊거나, 단말이 지하철·엘리베이터에서 갑자기 사라지는 일이 잦다.
// - 운영팀이 "유령 연결이 너무 오래 살아있다"고 보고했고, 담당자는
// OS의 TCP KeepAlive 를 켜서 죽은 연결을 OS가 알아서 정리하게 했다.
//
// 요구사항:
// - 단말이 사라진 죽은 연결(half-open 포함)은 수십 초~수 분 내에 감지·정리.
// - 살아있는 조용한 연결을 오인 끊김 시키지 않는다.
// - 연결이 죽었다고 판정되면 세션 정리 콜백이 정확히 한 번 실행돼야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
class LobbyConnection {
int fd_;
bool cleaned_ = false;
public:
explicit LobbyConnection(int fd) : fd_(fd) {
EnableKeepAlive(); // (A)
}
void EnableKeepAlive() {
// SO_KEEPALIVE 켜기
int on = 1;
setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); // (B)
// 원하던 동작: 10초 무트래픽이면 keepalive 프로브 시작,
// 5초 간격으로 재시도, 이 정도면 죽은 연결을 곧 OS가 끊어줄 것이다.
// (Linux 의 keepalive 파라미터를 setsockopt 로 설정)
int idle = 10; // (C) 의도: keepalive 시작까지 10초
int intvl = 5; // (C) 의도: 프로브 간격 5초
setsockopt(fd_, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
setsockopt(fd_, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl));
// (C) TCP_KEEPCNT(프로브 횟수)는 설정하지 않아 OS 전역값을 따른다.
}
// 평소엔 거의 호출되지 않는다(조용한 연결). 죽은 연결도
// 앱이 능동적으로 보내는 트래픽이 없으면 이 경로를 거의 안 탄다.
void OnAppDataReceived(const char* data, int len) {
// ... 매칭/채팅 처리
}
// OS keepalive 가 연결이 죽었다고 판단하면, 다음 번 수신 시도에서
// 에러가 올라오고 그때 세션을 정리하면 된다고 가정한다. (D)
void OnRecvError(int err) {
printf("recv error %d, cleaning up\n", err);
Cleanup();
}
void Cleanup() {
// 세션 정리, 매칭 큐에서 제거, 메모리 회수 등 ...
close(fd_);
}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.