23. 세션 객체 풀 재사용 시 상태 누수 (권한 승계 · 신원 혼동 · 패킷 오배달)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 세션 객체 풀(object pool) 재사용 시 상태 정리
// ----------------------------------------------------------------------------
// 시나리오 (세션/접속 · 서버-클라):
// - accept 가 잦은 서버라 매 연결마다 Session 을 새로 할당하지 않고 오브젝트 풀에서
// 빌려 쓰고(Rent), 연결이 끊기면 풀로 돌려준다(Return).
// - Session 은 인증 여부/플레이어 식별/수신 누적 버퍼/송신 대기 큐를 들고 있다.
// - Return 된 세션은 곧 다른 새 연결이 Rent 해서 재사용한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 인증된 플레이어 P 의 연결이 끊겨 세션이 풀로
// 돌아간 뒤 새 연결 Q 가 그 세션을 재사용할 때 Q 가 어떤 상태에서 시작하는지 설명하라.
// 비동기 수신/송신이 아직 끝나지 않은 채 Return 되면 무슨 일이 생기는지도 논하라.
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
public class Session
{
public Socket Socket;
public bool Authenticated; // 인증 통과 여부
public long PlayerId; // 인증된 플레이어
public int RecvLen; // 수신 누적 바이트 수
public byte[] RecvBuf = new byte[4096];
public Queue<byte[]> SendQueue = new();
public void Init(Socket s) // (A) 풀에서 꺼내 새 연결에 바인딩
{
Socket = s;
RecvLen = 0;
// 그 외 필드는 이전 사용분이 그대로 남아 있다.
}
}
public class SessionPool
{
private readonly ConcurrentQueue<Session> _pool = new();
public Session Rent(Socket s)
{
if (!_pool.TryDequeue(out var ses))
ses = new Session();
ses.Init(s); // (B)
return ses;
}
public void Return(Session ses)
{
_pool.Enqueue(ses); // (C) 그대로 반납
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 세션 객체 풀(object pool) 재사용 시 상태 정리
// ----------------------------------------------------------------------------
// 시나리오 (세션/접속 · 서버-클라):
// - accept 가 잦은 서버라 매 연결마다 Session 을 new 하지 않고 오브젝트 풀에서 빌려 쓰고
// (rent), 연결이 끊기면 풀로 돌려준다(release).
// - Session 은 인증 여부/플레이어 식별/수신 누적 버퍼/송신 대기 큐를 들고 있다.
// - release 된 세션은 곧 다른 새 연결이 rent 해서 재사용한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 인증된 플레이어 P 의 연결이 끊겨 세션이 풀로
// 돌아간 뒤 새 연결 Q 가 그 세션을 재사용할 때 Q 가 어떤 상태에서 시작하는지 설명하라.
// 비동기 수신/송신(완료 핸들러)이 아직 끝나지 않은 채 release 되면 무슨 일이 생기는지도
// 논하라. 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <vector>
#include <deque>
#include <mutex>
struct Session {
int fd = -1;
bool authenticated = false; // 인증 통과 여부
int64_t playerId = 0; // 인증된 플레이어
size_t recvLen = 0; // 수신 누적 바이트
uint8_t recvBuf[4096];
std::deque<std::vector<uint8_t>> sendQueue;
void init(int newFd) { // (A) 풀에서 꺼내 새 연결에 바인딩
fd = newFd;
recvLen = 0;
// 그 외 필드는 이전 사용분이 그대로 남아 있다.
}
};
class SessionPool {
public:
Session* rent(int fd) {
std::lock_guard<std::mutex> lk(mtx_);
Session* s;
if (!free_.empty()) { s = free_.back(); free_.pop_back(); }
else { s = new Session(); }
s->init(fd); // (B)
return s;
}
void release(Session* s) {
std::lock_guard<std::mutex> lk(mtx_);
free_.push_back(s); // (C) 그대로 반납
}
private:
std::mutex mtx_;
std::vector<Session*> free_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.