3. 접속 핸드셰이크에서의 프로토콜 버전 협상

난이도 상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 접속 핸드셰이크에서의 프로토콜 버전 협상
// ----------------------------------------------------------------------------
// 시나리오:
//   - 클라이언트가 접속하면 가장 먼저 C_Handshake 를 보낸다.
//   - 서버는 자신이 지원하는 프로토콜 버전과 비교해 접속을 수락/거부한다.
//   - 운영 중에는 서버가 먼저 신버전으로 배포되고(부분 롤아웃, 카나리),
//     구버전 클라와 신버전 클라가 동시에 접속한다.
//   - 빌드 번호(BuildNumber)는 핫픽스마다 올라가지만, 프로토콜 호환성은
//     ProtocolVersion 으로만 판단해야 한다(같은 프로토콜이면 빌드가 달라도 호환).
//
//   - 와이어 포맷: [ushort protocolVersion][uint buildNumber][ushort nameLen][name...]
//     모든 정수는 little-endian 으로 합의되어 있다.
//
// 요구사항:
//   - 호환되는 클라만 입장시키고, 호환 불가 클라에는 "업데이트 필요" 사유를
//     명확히 알려준 뒤 정상적으로 끊어야 한다.
//   - 핸드셰이크는 인증 전 단계이므로, 어떤 입력이 와도 서버가 죽으면 안 된다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 발생하는지 설명하고,
//   수정안과 더 나은 버전 협상 설계를 제시하라.
// ============================================================================

using System;
using System.Text;

// 서버가 지원하는 프로토콜 버전 (이 빌드 기준)
public static class ServerInfo
{
    public const ushort ProtocolVersion = 7;
    public const uint   ExpectedBuild   = 10420;
}

public class Session
{
    public bool authorized = false;
    public ushort clientProtocol = 0;
    public string clientName;
    public void SendDisconnect(string reason) { /* 사유 전송 후 종료 */ }
    public void Close() { /* ... */ }
}

public class HandshakeHandler
{
    // recvBuf[0..recvLen) 에 들어온 핸드셰이크 패킷(헤더+가변이름)
    public void OnHandshake(Session s, byte[] recvBuf, int recvLen)
    {
        // (A) 받은 버퍼에서 필드를 순서대로 읽는다
        ushort protocolVersion = BitConverter.ToUInt16(recvBuf, 0);
        uint   buildNumber     = BitConverter.ToUInt32(recvBuf, 2);
        ushort nameLen         = BitConverter.ToUInt16(recvBuf, 6);

        s.clientProtocol = protocolVersion;

        // (B) 버전 호환성 판단
        if (protocolVersion != ServerInfo.ProtocolVersion)
        {
            // 빌드가 다르면 일단 거부
            if (buildNumber != ServerInfo.ExpectedBuild)
            {
                s.SendDisconnect("Version mismatch");
                s.Close();
                return;
            }
        }

        // (C) 클라이언트 이름 읽기 (가변 길이)
        s.clientName = Encoding.UTF8.GetString(recvBuf, 8, nameLen);

        // (D) 여기까지 왔으면 호환되는 것으로 간주
        s.authorized = true;
        OnHandshakeComplete(s);
    }

    private void OnHandshakeComplete(Session s) { /* 로비 입장 등 */ }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.