45. 배치 메시지 스트림의 미지(unknown) 타입 처리 (C#)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# — 배치 메시지 스트림의 미지(unknown) 타입 처리
// ----------------------------------------------------------------------------
// 시나리오:
// 클라이언트는 한 번의 전송에 여러 논리 메시지를 배치(batch)로 묶어 보낸다.
// 각 메시지는 [type:u16][len:u16][payload:len] 형식이다. 프로토콜은 버전업으로
// 새 메시지 타입이 추가될 수 있으므로(전방 호환), 서버는 자신이 모르는 타입을
// 만나도 그 메시지를 건너뛰고 나머지를 계속 처리해야 한다.
//
// 요구사항:
// - 알려진 타입은 처리하고, 모르는 타입은 len 만큼 정확히 건너뛴다.
// - 손상/악성 입력(선언 len 이 남은 바이트보다 큼 등)에도 버퍼 밖을 읽지 않는다.
// - 하나의 메시지 처리가 어긋나도 이후 메시지 경계가 밀리면 안 된다.
//
// 과제: (A) (B) 지점의 동작을 설명하고 수정안을 제시하라.
// ============================================================================
using System;
public sealed class BatchDecoder
{
private const int HeaderSize = 4; // type(2) + len(2)
private const int KnownPayloadSize = 8; // 현재 서버가 아는 타입들의 payload 크기
public void Decode(ReadOnlySpan<byte> buf)
{
int off = 0;
while (off + HeaderSize <= buf.Length)
{
ushort type = (ushort)(buf[off] | (buf[off + 1] << 8));
ushort len = (ushort)(buf[off + 2] | (buf[off + 3] << 8));
off += HeaderSize;
var payload = buf.Slice(off, len); // (B)
switch (type)
{
case 1: HandleMove(payload); break;
case 2: HandleChat(payload); break;
default: break; // 미지 타입: 무시
}
off += KnownPayloadSize; // (A)
}
}
private void HandleMove(ReadOnlySpan<byte> p) { /* ... */ }
private void HandleChat(ReadOnlySpan<byte> p) { /* ... */ }
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ — 배치 메시지 스트림의 미지(unknown) 타입 처리
// ----------------------------------------------------------------------------
// 시나리오:
// 클라이언트가 한 전송에 여러 메시지를 배치로 묶어 보낸다. 각 메시지는
// [type:u16][len:u16][payload:len](리틀엔디언) 형식이다. 프로토콜 버전업으로
// 새 타입이 추가될 수 있어(전방 호환) 서버는 모르는 타입도 건너뛰고 계속 처리한다.
//
// 요구사항:
// - 알려진 타입은 처리, 모르는 타입은 len 만큼 정확히 건너뛴다.
// - 손상/악성 입력(선언 len 이 남은 바이트 초과 등)에도 버퍼 밖 접근 금지.
// - 한 메시지가 어긋나도 이후 경계가 밀리면 안 된다.
//
// 과제: (A) (B) 지점의 동작을 설명하고 수정안을 제시하라.
// ============================================================================
#include <cstdint>
#include <cstring>
#include <vector>
class BatchDecoder {
public:
static constexpr int kHeaderSize = 4; // type(2) + len(2)
static constexpr int kKnownPayloadSize = 8; // 서버가 아는 타입 payload 크기
void decode(const uint8_t* buf, size_t size) {
size_t off = 0;
while (off + kHeaderSize <= size) {
uint16_t type = static_cast<uint16_t>(buf[off] | (buf[off + 1] << 8));
uint16_t len = static_cast<uint16_t>(buf[off + 2] | (buf[off + 3] << 8));
off += kHeaderSize;
const uint8_t* payload = buf + off; // (B)
switch (type) {
case 1: handleMove(payload, len); break;
case 2: handleChat(payload, len); break;
default: break; // 미지 타입: 무시
}
off += kKnownPayloadSize; // (A)
}
}
private:
void handleMove(const uint8_t* p, uint16_t len) { (void)p; (void)len; }
void handleChat(const uint8_t* p, uint16_t len) { (void)p; (void)len; }
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.