12. 게이트웨이의 존 서버 메시지 포워딩 (프레이밍/버전/멀티플렉싱) · C#
난이도 최상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 게이트웨이의 존 서버 메시지 포워딩 (프레이밍/버전/멀티플렉싱, 종합/최상)
// ----------------------------------------------------------------------------
// 시나리오 (서버-서버):
// - 게이트웨이가 수많은 클라 연결을 받아, 메시지를 뒤단의 존(zone) 서버로 포워딩한다.
// - 여러 클라 세션의 메시지가 게이트웨이↔존 사이의 "하나의 공유 TCP 연결"로 멀티플렉싱
// 되어 흐른다(존 서버는 라우팅 헤더의 sessionId 로 어떤 클라인지 구분).
// - 와이어 포맷:
// 클라→게이트웨이 한 프레임: [u32 len][u16 msgType][payload]
// (len = msgType(2) + payload 바이트 수)
// 게이트웨이→존 한 프레임: [u32 zlen][u64 sessionId][u16 zoneId][u16 msgType][payload]
// (zlen = 그 뒤 전체 바이트 수)
// - 모든 정수는 네트워크 바이트 순서(빅엔디안)로 보낸다는 것이 프로토콜 약속이다.
// - 게이트웨이는 여러 IO 워커 스레드를 쓴다. 서로 다른 세션의 ForwardToZone 이
// 서로 다른 스레드에서 동시에 호출될 수 있다.
// - 게이트웨이와 존 서버는 서로 다른 시점에 배포되어 프로토콜 버전이 다를 수 있다
// (롤링 배포 중 구·신 버전 공존). msgType 번호 체계가 버전 간 다를 수 있다.
//
// 요구사항:
// - 존 서버가 프레임 경계를 정확히 복원할 수 있어야 한다(길이 프리픽스 정확).
// - 라우팅 헤더/페이로드의 바이트 순서가 프로토콜 약속과 일치해야 한다.
// - 버전이 다른 존으로 보낼 때 msgType/스키마가 호환되게 변환되어야 한다.
// - 공유 연결로의 멀티플렉싱이 프레임을 섞지 않아야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 어떤 경우에 존 서버가 디싱크/오라우팅/오디스패치
// 되는지 설명하고, 수정안과 더 나은 설계를 제시하라. (먼저 직접 리뷰 후 answer.md 와 대조)
// ============================================================================
using System;
using System.Net;
// 게이트웨이 워커 스레드들이 공유하는 존 서버 연결.
public class ZoneLink
{
public void RawSend(byte[] data, int length) { /* NetworkStream.Write(...) (생략) */ }
}
public class Gateway
{
private readonly ZoneLink _zone;
public Gateway(ZoneLink zone) { _zone = zone; }
// 클라에서 수신·재조립이 끝난 "완성된 한 프레임"을 존으로 포워딩한다.
// clientFrame = [u32 len][u16 msgType][payload], frameLen = 4 + len.
public void ForwardToZone(ulong sessionId, ushort zoneId, byte[] clientFrame, int frameLen)
{
// 클라 프레임 파싱
uint len = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(clientFrame, 0));
ushort msgType = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(clientFrame, 4));
int payloadOffset = 6;
uint payloadLen = len - 2; // msgType 2바이트를 뺀 나머지
// 존 프레임 구성: [u32 zlen][u64 sessionId][u16 zoneId][u16 msgType][payload]
byte[] outBuf = new byte[4 + 8 + 2 + 2 + (int)payloadLen];
int off = 0;
// (A) 길이 프리픽스: 클라 프레임의 len 을 그대로 사용
WriteU32BE(outBuf, off, len); off += 4;
// (B) 라우팅 헤더: sessionId/zoneId 를 그대로 복사
BitConverter.GetBytes(sessionId).CopyTo(outBuf, off); off += 8;
BitConverter.GetBytes(zoneId).CopyTo(outBuf, off); off += 2;
// (C) msgType: 버전 변환 없이 그대로 전달
WriteU16BE(outBuf, off, msgType); off += 2;
// payload 복사
Array.Copy(clientFrame, payloadOffset, outBuf, off, (int)payloadLen); off += (int)payloadLen;
// (D) 공유 존 연결로 송신
_zone.RawSend(outBuf, outBuf.Length);
}
private static void WriteU32BE(byte[] b, int o, uint v)
{
b[o] = (byte)(v >> 24); b[o + 1] = (byte)(v >> 16);
b[o + 2] = (byte)(v >> 8); b[o + 3] = (byte)v;
}
private static void WriteU16BE(byte[] b, int o, ushort v)
{
b[o] = (byte)(v >> 8); b[o + 1] = (byte)v;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 게이트웨이의 존 서버 메시지 포워딩 (프레이밍/버전/멀티플렉싱, 종합/최상)
// ----------------------------------------------------------------------------
// 시나리오 (서버-서버):
// - 게이트웨이가 수많은 클라 연결을 받아, 메시지를 뒤단의 존(zone) 서버로 포워딩한다.
// - 여러 클라 세션의 메시지가 게이트웨이↔존 사이의 "하나의 공유 TCP 연결"로 멀티플렉싱
// 되어 흐른다(존 서버는 라우팅 헤더의 sessionId 로 어떤 클라인지 구분).
// - 와이어 포맷:
// 클라→게이트웨이 한 프레임: [u32 len][u16 msgType][payload]
// (len = msgType(2) + payload 바이트 수)
// 게이트웨이→존 한 프레임: [u32 zlen][u64 sessionId][u16 zoneId][u16 msgType][payload]
// (zlen = 그 뒤 전체 바이트 수)
// - 모든 정수는 네트워크 바이트 순서(빅엔디안)로 보낸다는 것이 프로토콜 약속이다.
// - 게이트웨이는 여러 IO 워커 스레드를 쓴다. 서로 다른 세션의 ForwardToZone 이
// 서로 다른 스레드에서 동시에 호출될 수 있다.
// - 게이트웨이와 존 서버는 서로 다른 시점에 배포되어 프로토콜 버전이 다를 수 있다
// (롤링 배포 중 구·신 버전 공존). msgType 번호 체계가 버전 간 다를 수 있다.
//
// 요구사항:
// - 존 서버가 프레임 경계를 정확히 복원할 수 있어야 한다(길이 프리픽스 정확).
// - 라우팅 헤더/페이로드의 바이트 순서가 프로토콜 약속과 일치해야 한다.
// - 버전이 다른 존으로 보낼 때 msgType/스키마가 호환되게 변환되어야 한다.
// - 공유 연결로의 멀티플렉싱이 프레임을 섞지 않아야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 어떤 경우에 존 서버가 디싱크/오라우팅/오디스패치
// 되는지 설명하고, 수정안과 더 나은 설계를 제시하라. (먼저 직접 리뷰 후 answer.md 와 대조)
// ============================================================================
#include <cstdint>
#include <cstring>
#include <vector>
#include <arpa/inet.h> // htonl/ntohl/htons/ntohs
// 게이트웨이 워커 스레드들이 공유하는 존 서버 연결.
struct ZoneLink {
int fd;
void RawSend(const uint8_t* /*data*/, size_t /*n*/) { /* write(fd, ...) (생략) */ }
};
class Gateway {
public:
explicit Gateway(ZoneLink* zone) : zone_(zone) {}
// 클라에서 수신·재조립이 끝난 "완성된 한 프레임"을 존으로 포워딩한다.
// clientFrame = [u32 len][u16 msgType][payload], frameLen = 4 + len.
void ForwardToZone(uint64_t sessionId, uint16_t zoneId,
const uint8_t* clientFrame, size_t frameLen)
{
// 클라 프레임 파싱
uint32_t len;
std::memcpy(&len, clientFrame, 4);
len = ntohl(len);
uint16_t msgType;
std::memcpy(&msgType, clientFrame + 4, 2);
msgType = ntohs(msgType);
const uint8_t* payload = clientFrame + 6;
uint32_t payloadLen = len - 2; // msgType 2바이트를 뺀 나머지
// 존 프레임 구성: [u32 zlen][u64 sessionId][u16 zoneId][u16 msgType][payload]
std::vector<uint8_t> out;
out.resize(4 + 8 + 2 + 2 + payloadLen);
size_t off = 0;
// (A) 길이 프리픽스: 클라 프레임의 len 을 그대로 사용
uint32_t zlen = htonl(len);
std::memcpy(out.data() + off, &zlen, 4); off += 4;
// (B) 라우팅 헤더: sessionId/zoneId 를 그대로 복사
std::memcpy(out.data() + off, &sessionId, 8); off += 8;
std::memcpy(out.data() + off, &zoneId, 2); off += 2;
// (C) msgType: 버전 변환 없이 그대로 전달
uint16_t mt = htons(msgType);
std::memcpy(out.data() + off, &mt, 2); off += 2;
// payload 복사
std::memcpy(out.data() + off, payload, payloadLen); off += payloadLen;
// (D) 공유 존 연결로 송신
zone_->RawSend(out.data(), out.size());
}
private:
ZoneLink* zone_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.