30. 순서 보장 채널 재정렬 버퍼: 무제한 버퍼링과 갭 정지 (C#)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// [시나리오] 순서 보장 채널의 수신 재정렬 버퍼 (서버-클라 / RUDP)
// 신뢰성·순서 보장 채널은 각 프레임에 시퀀스 번호(seq)를 붙여 보낸다.
// 네트워크가 순서를 뒤섞으므로, 수신 측은 아직 차례가 아닌 프레임을
// "재정렬 버퍼"에 담아 두었다가, 기대하는 seq(NextExpected)가 채워지면
// 그 지점부터 연속된 프레임을 순서대로 상위(App)로 전달한다.
//
// [요구사항]
// - 상위에는 항상 빈틈 없는 오름차순 seq 로 전달한다.
// - 악의적/비정상 피어가 보낸 트래픽으로 서버 메모리가 무한정 커지면 안 된다.
// - 한 프레임의 유실이 채널을 영구히 멈추게 해서는 안 된다.
//
// [과제] OrderedChannel.OnReceive 를 검토하고, 순서 밖 프레임이 계속 들어오거나
// 중간 한 프레임이 오래 오지 않을 때 어떤 문제가 생기는지 원인과 수정안을
// 제시하라. 표시된 (A)(B) 지점에 주목하라.
using System;
using System.Collections.Generic;
interface IAppSink { void Deliver(int seq, byte[] payload); }
class OrderedChannel
{
private int _nextExpected; // 다음에 전달할 seq
private readonly Dictionary<int, byte[]> _reorder = new(); // seq -> payload
private readonly IAppSink _app;
public OrderedChannel(IAppSink app) { _app = app; }
// 수신 스레드에서 프레임 하나가 도착할 때마다 호출된다.
public void OnReceive(int seq, byte[] payload)
{
if (seq < _nextExpected)
return; // 이미 전달한 과거 seq → 폐기
// (A)
_reorder[seq] = payload; // 아직 차례가 아니면 버퍼에 보관
// (B)
while (_reorder.TryGetValue(_nextExpected, out byte[] next))
{
_app.Deliver(_nextExpected, next);
_reorder.Remove(_nextExpected);
_nextExpected++;
}
}
} 결함 코드 · C++
// [시나리오] 순서 보장 채널의 수신 재정렬 버퍼 (서버-클라 / RUDP)
// 신뢰성·순서 보장 채널은 각 프레임에 시퀀스 번호(seq)를 붙여 보낸다.
// 네트워크가 순서를 뒤섞으므로, 수신 측은 아직 차례가 아닌 프레임을
// "재정렬 버퍼"에 담아 두었다가, 기대하는 seq(nextExpected)가 채워지면
// 그 지점부터 연속된 프레임을 순서대로 상위(App)로 전달한다.
//
// [요구사항]
// - 상위에는 항상 빈틈 없는 오름차순 seq 로 전달한다.
// - 악의적/비정상 피어가 보낸 트래픽으로 서버 메모리가 무한정 커지면 안 된다.
// - 한 프레임의 유실이 채널을 영구히 멈추게 해서는 안 된다.
//
// [과제] OrderedChannel::onReceive 를 검토하고, 순서 밖 프레임이 계속 들어오거나
// 중간 한 프레임이 오래 오지 않을 때 어떤 문제가 생기는지 원인과 수정안을
// 제시하라. 표시된 (A)(B) 지점에 주목하라.
#include <map>
#include <vector>
#include <cstdint>
class OrderedChannel {
uint32_t nextExpected_ = 0; // 다음에 전달할 seq
std::map<uint32_t, std::vector<uint8_t>> reorder_; // seq -> payload
void deliver(uint32_t /*seq*/, const std::vector<uint8_t>& /*payload*/) { /* 상위 전달 */ }
public:
// 수신 스레드에서 프레임 하나가 도착할 때마다 호출된다.
void onReceive(uint32_t seq, std::vector<uint8_t> payload) {
if (seq < nextExpected_)
return; // 이미 전달한 과거 seq → 폐기
// (A)
reorder_[seq] = std::move(payload); // 아직 차례가 아니면 버퍼에 보관
// (B)
auto it = reorder_.find(nextExpected_);
while (it != reorder_.end()) {
deliver(nextExpected_, it->second);
reorder_.erase(it);
++nextExpected_;
it = reorder_.find(nextExpected_);
}
}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.