7. 바이너리 직렬화의 엔디안/정렬/레이아웃 가정
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 바이너리 직렬화의 엔디안/정렬/레이아웃 가정
// ----------------------------------------------------------------------------
// 시나리오:
// - 서버는 x86-64 리눅스(.NET), 클라는 Windows/x86-64 와 일부 모바일(ARM, Unity IL2CPP)이다.
// - 스냅샷 패킷 S_EntityState 를 "구조체를 통째로 바이트로 복사"해서 보낸다(속도 위해).
// - 와이어 포맷은 사실상 "구조체의 메모리 레이아웃(필드 순서/패딩)"이 그대로 정의가 된 상태다.
// - 최근 모바일(ARM) 빌드에서 좌표가 깨지고, 가끔 패킷 크기가 안 맞는다는
// 리포트가 들어왔다.
//
// 요구사항:
// - 모든 플랫폼/런타임에서 동일한 바이트 시퀀스로 직렬화되어야 한다.
// - 와이어 포맷은 머신/런타임 독립적으로 명세 가능해야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 플랫폼 간 불일치가 나는지 설명하고,
// 수정안과 더 나은 직렬화 설계를 제시하라.
// ============================================================================
using System;
using System.Runtime.InteropServices;
// (A) 와이어로 그대로 나가는 구조체. 명시적 레이아웃/팩 지정 없음.
[StructLayout(LayoutKind.Sequential)]
public struct S_EntityState
{
public byte type; // 엔티티 종류
public uint entityId;
public float posX; // 위치
public float posY;
public ushort hp;
public ulong flags; // 상태 비트
}
public class StateSerializer
{
// (B) 구조체를 통째로 바이트 버퍼로 복사 (Marshal 사용)
public byte[] Serialize(S_EntityState s)
{
int size = Marshal.SizeOf<S_EntityState>();
byte[] outBuf = new byte[size];
IntPtr p = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(s, p, false);
Marshal.Copy(p, outBuf, 0, size);
Marshal.FreeHGlobal(p);
return outBuf;
}
// (C) 수신: 바이트 버퍼를 구조체로 되돌린다
public S_EntityState Deserialize(byte[] buf, int len)
{
int size = Marshal.SizeOf<S_EntityState>();
IntPtr p = Marshal.AllocHGlobal(size);
Marshal.Copy(buf, 0, p, size); // len 무시
S_EntityState s = Marshal.PtrToStructure<S_EntityState>(p);
Marshal.FreeHGlobal(p);
return s;
}
}
// ---------------------------------------------------------------------------
// 멀티바이트 정수를 직접 쓰는 헬퍼도 한 군데 있는데, 여기도 가정이 있다.
// ---------------------------------------------------------------------------
public class IntWriter
{
// (D) 32비트 정수를 버퍼에 쓴다 — 호스트 바이트 순서 그대로
public void WriteU32(byte[] buf, int offset, uint v)
{
byte[] tmp = BitConverter.GetBytes(v); // 호스트 엔디안
Array.Copy(tmp, 0, buf, offset, 4);
}
public uint ReadU32(byte[] buf, int offset)
{
return BitConverter.ToUInt32(buf, offset); // 호스트 엔디안
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 바이너리 직렬화의 엔디안/정렬 가정
// ----------------------------------------------------------------------------
// 시나리오:
// - 서버는 x86-64 리눅스, 클라는 Windows/x86-64 와 일부 모바일(ARM)이다.
// - 스냅샷 패킷 S_EntityState 를 "구조체를 통째로 memcpy" 해서 보낸다(속도 위해).
// - 와이어 포맷은 사실상 "서버 머신의 메모리 레이아웃"이 그대로 정의가 된 상태다.
// - 최근 모바일(ARM) 빌드에서 좌표가 깨지고, 가끔 패킷 크기가 안 맞는다는
// 리포트가 들어왔다.
//
// 요구사항:
// - 모든 플랫폼/컴파일러에서 동일한 바이트 시퀀스로 직렬화되어야 한다.
// - 와이어 포맷은 머신/컴파일러 독립적으로 명세 가능해야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 플랫폼 간 불일치가 나는지 설명하고,
// 수정안과 더 나은 직렬화 설계를 제시하라.
// ============================================================================
#include <cstdint>
#include <cstring>
#include <vector>
// (A) 와이어로 그대로 나가는 구조체. pack 지정 없음.
struct S_EntityState {
uint8_t type; // 엔티티 종류
uint32_t entityId;
float posX; // 위치
float posY;
uint16_t hp;
uint64_t flags; // 상태 비트
};
class StateSerializer {
public:
// (B) 구조체를 통째로 바이트 버퍼로 복사
std::vector<uint8_t> Serialize(const S_EntityState& s)
{
std::vector<uint8_t> out(sizeof(S_EntityState));
std::memcpy(out.data(), &s, sizeof(S_EntityState));
return out;
}
// (C) 수신: 바이트 버퍼를 구조체로 되돌린다
S_EntityState Deserialize(const uint8_t* buf, size_t len)
{
S_EntityState s;
std::memcpy(&s, buf, sizeof(S_EntityState));
return s;
}
};
// ---------------------------------------------------------------------------
// 멀티바이트 정수를 직접 쓰는 헬퍼도 한 군데 있는데, 여기도 가정이 있다.
// ---------------------------------------------------------------------------
class IntWriter {
public:
// (D) 32비트 정수를 버퍼에 쓴다 — 호스트 메모리 순서 그대로
void WriteU32(uint8_t* buf, uint32_t v)
{
*reinterpret_cast<uint32_t*>(buf) = v;
}
uint32_t ReadU32(const uint8_t* buf)
{
return *reinterpret_cast<const uint32_t*>(buf);
}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.