1. C# Heartbeat / 유휴 타임아웃 세션 정리
난이도 하 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - Heartbeat / 유휴 타임아웃 세션 정리
// ----------------------------------------------------------------------------
// 시나리오:
// - 실시간 게임 서버. 모든 세션은 주기적으로 Ping 패킷을 보낸다.
// - 일정 시간(IdleTimeout) 동안 Ping이 없으면 "죽은 연결"로 보고 끊는다.
// - 별도 스레드(타이머)가 1초마다 전체 세션을 훑어 만료된 세션을 끊는다.
// - 네트워크 수신 스레드는 Ping을 받으면 LastPing 시각을 갱신한다.
//
// 요구사항:
// - 살아있는 정상 유저를 절대 오인 끊김(false disconnect) 시키면 안 된다.
// - 진짜 죽은 연결은 IdleTimeout 안에 정리되어야 한다.
// - 수신 스레드와 타이머 스레드가 동시에 같은 세션을 만진다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
using System.Threading;
public class Session
{
public int Id;
public bool Connected = true;
// (A) 마지막 Ping을 받은 시각. 수신 스레드가 쓰고 타이머 스레드가 읽는다.
public DateTime LastPing = DateTime.Now;
public void Close(string reason)
{
Connected = false;
// 소켓 닫기 등 ... (생략)
Console.WriteLine($"[Session {Id}] closed: {reason}");
}
}
public class HeartbeatManager
{
private readonly Dictionary<int, Session> _sessions = new Dictionary<int, Session>();
private readonly TimeSpan _idleTimeout = TimeSpan.FromSeconds(30);
private Timer _timer;
public void Start()
{
// 1초마다 SweepDeadSessions 호출
_timer = new Timer(_ => SweepDeadSessions(), null, 1000, 1000);
}
public void AddSession(Session s)
{
_sessions[s.Id] = s; // (B)
}
// 네트워크 수신 스레드에서 Ping 패킷 수신 시 호출
public void OnPingReceived(int sessionId)
{
if (_sessions.TryGetValue(sessionId, out var s))
{
s.LastPing = DateTime.Now; // (A) 시각 갱신
}
}
// (C) 타이머 스레드: 만료 세션을 찾아 끊는다
private void SweepDeadSessions()
{
var now = DateTime.Now;
foreach (var kv in _sessions)
{
var s = kv.Value;
if (now - s.LastPing > _idleTimeout)
{
s.Close("idle timeout");
_sessions.Remove(kv.Key); // (D)
}
}
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - Heartbeat / 유휴 타임아웃 세션 정리
// ----------------------------------------------------------------------------
// 시나리오:
// - 실시간 게임 서버. 모든 세션은 주기적으로 Ping 패킷을 보낸다.
// - 일정 시간(IdleTimeout) 동안 Ping이 없으면 "죽은 연결"로 보고 끊는다.
// - 별도 스레드(타이머)가 1초마다 전체 세션을 훑어 만료된 세션을 끊는다.
// - 네트워크 수신 스레드는 Ping을 받으면 LastPing 시각을 갱신한다.
//
// 요구사항:
// - 살아있는 정상 유저를 절대 오인 끊김(false disconnect) 시키면 안 된다.
// - 진짜 죽은 연결은 IdleTimeout 안에 정리되어야 한다.
// - 수신 스레드와 타이머 스레드가 동시에 같은 세션을 만진다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <unordered_map>
#include <chrono>
#include <thread>
#include <cstdint>
#include <cstdio>
class Session {
public:
int id;
bool connected = true;
// (A) 마지막 Ping을 받은 시각. 수신 스레드가 쓰고 타이머 스레드가 읽는다.
std::chrono::system_clock::time_point lastPing = std::chrono::system_clock::now();
void Close(const char* reason) {
connected = false;
// 소켓 닫기 등 ... (생략)
printf("[Session %d] closed: %s\n", id, reason);
}
};
class HeartbeatManager {
std::unordered_map<int, Session*> sessions_;
std::chrono::seconds idleTimeout_{30};
std::thread timer_;
bool running_ = true;
public:
void Start() {
// 1초마다 SweepDeadSessions 호출
timer_ = std::thread([this] {
while (running_) {
std::this_thread::sleep_for(std::chrono::seconds(1));
SweepDeadSessions();
}
});
}
void AddSession(Session* s) {
sessions_[s->id] = s; // (B)
}
// 네트워크 수신 스레드에서 Ping 패킷 수신 시 호출
void OnPingReceived(int sessionId) {
auto it = sessions_.find(sessionId);
if (it != sessions_.end()) {
it->second->lastPing = std::chrono::system_clock::now(); // (A) 시각 갱신
}
}
// (C) 타이머 스레드: 만료 세션을 찾아 끊는다
void SweepDeadSessions() {
auto now = std::chrono::system_clock::now();
for (auto it = sessions_.begin(); it != sessions_.end(); ++it) {
Session* s = it->second;
if (now - s->lastPing > idleTimeout_) {
s->Close("idle timeout");
sessions_.erase(it); // (D) 순회 중 erase
delete s; // (E)
}
}
}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.