1일차 계획
1. 서버 - 클라이언트와 공유하는 공통 프로토콜에 대한 디렉토리 정리
2. 클라이언트와 서버 간 공통 프로토콜 정의
1. 디렉토리 구성
MCO_PARK
└ MCO_PARK.sln(솔루션)
└ CommonProtocol(공통 프로토콜)
└ 추가 예정
└ Server(서버)
└ 추가 예정
└ Client(클라이언트)
└ 추가 예정
2. 공통 프로토콜 정의
CommonProtocol
<MessageType.cs>
namespace CommonProtocol
{
// 클라이언트-서버 간 주고 받을 메시지 타입을 정의
public enum MessageType : uint
{
LOGIN_REQ = 1, // 로그인 요청
LOGIN_ACK, // 로그인 요청에 대한 회답
INPUT, // 클라이언트의 키 입력(좌우 및 점프)
STATE_SHORT, // 상태 전파(짧은 주기, 간단히)
STATE_LONG, // 상태 전파(긴 주기, 복잡하게)
STAR_GET, // 별 획득
DOOR_OPEN, // 문 열림
GAME_CLEAR, // 게임 클리어
ERROR // 에러
}
}
<Header.cs>
using System;
namespace CommonProtocol
{
// 헤더를 초기화 및 바이트를 담은 버퍼를 반환 받는 메서드 정의
public class Header
{
public uint MsgId { get; set; } // 메시지 ID(순서에 따라 증가)
public MessageType MsgType { get; set; } // 메시지 타입(구별요소)
public uint BodyLength { get; set; } // 바디 길이(실제 메시지 길이)
public Header() { }
public Header(byte[] bytes) // 각 필드 초기화
{
MsgId = BitConverter.ToUInt32(bytes, 0);
MsgType = (MessageType)BitConverter.ToUInt32(bytes, 4);
BodyLength = BitConverter.ToUInt32(bytes, 8);
}
public byte[] GetBytes() // 각 필드에 대한 바이트를 담은 버퍼를 반환
{
byte[] buf = new byte[12];
Array.Copy(BitConverter.GetBytes(MsgId), 0, buf, 0, 4);
Array.Copy(BitConverter.GetBytes((uint)MsgType), 0, buf, 4, 4);
Array.Copy(BitConverter.GetBytes(BodyLength), 0, buf, 8, 4);
return buf;
}
}
}
<Packet.cs>
using System.Text;
using System.Text.Json;
using System.Net.Sockets;
namespace CommonProtocol
{
// Header와 Body를 구분하여 Send 및 Read를 하는 유틸 메서드 정의
public static class Packet
{
// 전송을 위한 유틸 메서드
public static async Task SendAsync(NetworkStream stream, MessageType type, object bodyObj, uint msgId = 0)
{
// 바디 바이트를 먼저 생성
string json = JsonSerializer.Serialize(bodyObj);
byte[] bodyBytes = Encoding.UTF8.GetBytes(json);
// 구한 바디의 길이와 함께 헤더를 초기화
var header = new Header
{
MsgId = msgId,
MsgType = type,
BodyLength = (uint)bodyBytes.Length
};
// 헤더 바이트도 생성
byte[] headerBytes = header.GetBytes();
// 비동기적으로 헤더와 바디를 전송
await stream.WriteAsync(headerBytes, 0, headerBytes.Length);
await stream.WriteAsync(bodyBytes, 0, bodyBytes.Length);
}
// 수신을 위한 메서드
// 수신헬퍼 메서드(ReadExactAsync)를 호출 후 헤더와 바디로 파싱하여 반환함
public static async Task<(Header, string)> ReceiveAsync(NetworkStream stream)
{
byte[] headerBuf = new byte[12];
await ReadExactAsync(stream, headerBuf, 12);
var header = new Header(headerBuf);
byte[] bodyBuf = new byte[header.BodyLength];
await ReadExactAsync(stream, bodyBuf, (int)header.BodyLength);
string body = Encoding.UTF8.GetString(bodyBuf);
return (header, body);
}
// 정해진 크기(size)만큼 반복해서 실제 수신하는 헬퍼 메서드
public static async Task ReadExactAsync(NetworkStream stream, byte[] buf, int size)
{
int read = 0;
while (read < size)
{
int r = await stream.ReadAsync(buf, read, size - read);
if (r == 0) throw new Exception("연결 끊김");
read += r;
}
}
}
}
<PlayerState.cs>
namespace CommonProtocol
{
// 플레이어의 상태를 나타내는 클래스 정의
public class PlayerState
{
public int Id { get; set; } // ID(유저 실 구별요소)
public string Nickname { get; set; } = ""; // 닉네임
public float x { get; set; } // 유저의 x 좌표
public float y { get; set; } // 유저의 y 좌표
public bool HasStar { get; set; } // 유저의 별 보유여부
public bool IsCleared { get; set; } // 유저의 클리어 여부
}
}
<MapState.cs>
namespace CommonProtocol
{
// 맵 상태 나타내는 클래스 정의
public class MapState
{
public Door Door { get; set; } = new();
public List<Star> Stars { get; set; } = new();
}
// 문 상태
public class Door
{
public float x { get; set; } // 문의 x 좌표
public float y { get; set; } // 문의 y 좌표
public bool IsOpen { get; set; } // 문의 열림 상태
}
// 별 상태
public class Star
{
public int Id { get; set; } // 별의 ID(실 구별요소)
public float x { get; set; } // 별의 x 좌표
public float y { get; set; } // 별의 y 좌표
public bool IsTaken { get; set; } // 별이 획득됐는지 여부
public int? OwnerId { get; set; } // 별을 누가 가지고 있는지 여부
}
}
<KeyState.cs>
namespace CommonProtocol
{
// 유저가 누른 키의 상태를 나타내는 클래스 정의
public class KeyState
{
public bool Left { get; set; } // 왼쪽 키
public bool Right { get; set; } // 오른쪽 키
public bool Jump { get; set; } // 점프 키
}
}
<GameEvent.cs>
namespace CommonProtocol
{
// 별 획득, 문 열림, 게임 클리어에 대한 이벤트 클래스 정의
public abstract class GameEvent // 상속될 추상클래스
{
public string Type { get; set; } = "";
public int ServerTick { get; set; }
}
public class StarGetEvent : GameEvent // 별 획득에 따른 이벤트
{
public int PlayerId { get; set; }
public int StarId { get; set; }
public StarGetEvent(int tick, int pid, int sid)
{
Type = "STAR_GET";
ServerTick = tick;
PlayerId = pid;
StarId = sid;
}
}
public class DoorOpenEvent : GameEvent // 문 열림에 따른 이벤트
{
public int OpnerId { get; set; }
public DoorOpenEvent(int tick, int id)
{
Type = "DOOR_OPEN";
ServerTick = tick;
OpnerId = id;
}
}
public class GameClearEvent : GameEvent // 게임 클리어에 따른 이벤트
{
public GameClearEvent(int tick)
{
Type = "GAME_CLEAR";
ServerTick = tick;
}
}
}
Git 초기커밋

https://github.com/mmmmz986/MCO_PARK.git
내일 Packet 부분은 Body랑 Util로 쪼개야 할 듯
'LMS 7 > 개발일지' 카테고리의 다른 글
| 25.10.15 개발일지 / C# 명코파크 (3일차) (0) | 2025.11.13 |
|---|---|
| 25.10.14 개발일지 / C# 명코파크 (2일차) (0) | 2025.11.13 |
| 25.10.09 개발일지 / C# TRex - TCP IP 네트워크 구상 (0) | 2025.11.13 |
| 25.10.08 개발일지 / C# T-Rex Endless Runner - 3 / 주요 메서드 정리 및 영상 (0) | 2025.11.13 |
| 25.10.07 개발일지 / C# T-Rex Endless Runner - 2 / 이벤트, 필드, 메서드 등 멤버 정의 (0) | 2025.11.13 |