-
스파르타) The Last Rollback (D-34, Node.js 게임 서버 최종 프로젝트) - Protobuf (3)TIL-sparta 2024. 7. 25. 01:41
> 과제 진행 간 완료한 사항 및 문제점과 해결 과정을 정리해보았다.
학습 키워드: Protobuf, Node.js, TCP, socket1. 완료한 과제 진행 사항
1) 프로토콜 버퍼 구조 개선 (oneof):
- 클라이언트 Protobuf 역직렬화가 정상적으로 되지 않던 문제를 개선하기 위해 oneof 구조로 변경 작업
- 상세 변경 내용은 위 PR 및 연결된 Issue 참고
2. 과제 진행 간 문제점
1) 클라이언트 쪽 protobuf 메세지가 역직렬화 되지 않는 문제 (해결):
syntax = 'proto3'; message Foo { string message = 1; } message Bar { uint32 id = 1; bytes payload = 2; }
Protobuf로 직렬화된 데이터를 Unity에서 역직렬화할 때 위와같은 에러가 출력됐다. 명확한 원인을 알지 못해서 한참을 헤맸는데, 튜터님과 상의해본 결과 서버 측 직렬화 구조의 문제라는 결론이 나왔다. 이해를 돕기위해 기존의 축약된 구조를 만들어봤다. Foo라는 패킷이 Bar 패킷의 payload 에 담기게 되는 구조인데, 이 과정에서 Foo를 한 번 protobuf로 직렬화 하여 payload에 담고, 다시 Bar를 직렬화한 뒤 header를 붙여 전송하는 방식이었다. 지난 프로젝트 중에서는 Unity 클라이언트가 ProtoContract를 사용하여 두 번 직렬화 한 뒤 Node.js 서버의 protobufjs 라이브러리를 통해 역직렬화를 두 번 하는 구조로 작성 했었고, 아무런 문제 없이 동작했는데, 이번에 protoc로 컴파일한 cs 파일을 사용하는 Unity 클라이언트에서는 서버 측에서 두 번 직렬화 한 것을 역직렬화하지 못하고 Figure 2와 같은 에러를 출력한 것이다.
서버 측에서 직렬화를 두 번 진행하는 구조인 것이 역직렬화 시 payload의 직렬화된 버퍼를 잘못된 태그로 인식하는 것이 아닐까 생각되어서, 이전 프로젝트와 동일하게 oneof를 사용하여 한 번씩만 직렬화 하는 구조로 변경해보니 해결됐다.
// protocol.proto, 변경된 메세지 구조 ... message ResponsePacket { uint32 code = 1; string message = 2; uint64 timestamp = 3; uint32 payloadType = 4; oneof payload { S_Enter sEnter = 5; S_Spawn sSpawn = 6; S_Despawn sDespawn = 7; S_Move sMove = 8; ...
// ClientPacketManager.cs, 완성된 버퍼를 역직렬화하여 핸들러로 전달 public void OnRecvPacket(PacketSession session, ArraySegment<byte> buffer) { ResponsePacket resPkt = new ResponsePacket(); resPkt.MergeFrom(buffer.Array, buffer.Offset + 5, buffer.Count - 5); Action<PacketSession, ResponsePacket> action = null; if (_handler.TryGetValue((ushort) resPkt.PayloadType, out action)) action.Invoke(session, resPkt); }
// ServerSession.cs, 패킷 전송 위치 public void Send(IMessage packet) { RequestPacket reqPkt = packet as RequestPacket; ushort reqSize = (ushort) reqPkt.CalculateSize(); // byte[] bytes = new byte[reqSize + 5]; // 크기(4바이트) + 아이디(1바이트) + 데이터 크기 Array.Copy(BitConverter.GetBytes(reqSize + 5), 0, bytes, 0, sizeof(int)); // 데이터 크기 (4바이트) bytes[4] = (byte) reqPkt.PayloadType; // 프로토콜의 아이디 (1바이트) Array.Copy(reqPkt.ToByteArray(), 0, bytes, 5, reqSize); // 전달하려는 데이터 Send(new ArraySegment<byte>(bytes)); }
# 서버 측 변경점은 PR을 참고
+ 24-08-28 추가: https://protobuf.dev/programming-guides/encoding/ tag 검색하여 참고
--
REFERENCES:> 프로젝트 repo
728x90'TIL-sparta' 카테고리의 다른 글
스파르타) The Last Rollback (D-32, Node.js 게임 서버 최종 프로젝트) (0) 2024.07.26 스파르타) The Last Rollback (D-33, Node.js 게임 서버 최종 프로젝트) (0) 2024.07.25 스파르타) The Last Rollback (D-35, Node.js 게임 서버 최종 프로젝트) - Protobuf (2) (0) 2024.07.23 스파르타) The Last Rollback (D-36, Node.js 게임 서버 최종 프로젝트) - Protobuf (0) 2024.07.22 [Docker] Docker Desktop 설치 (Windows 11) (0) 2024.07.21