패킷 직렬화
지난 시간에는 유니코드와 인코딩의 기본적인 개념과 UTF-8, 16의 개념 및 차이점에 대해서 알아보았다. 이러한 지식을 바탕으로 오늘 배울 문자열 처리를 잘 학습해보자. 🧐
🚩 코드개선
Session Write
기존 코드는 아래 이미지와 같이 new Span을 사용하여 packetId, playerId 값을 세그먼트에 넘겨주었다. 이 부분을 보다 가독성 좋고 효율적으로 변경할 수 있다.
바로 매번 Span 변수를 사용하는 것이 아닌 Span 변수를 한번만 호출하고 선언하여 세그먼트의 값을 받아와 Span의 Offset 값만 변경하며 필요한 값들을 byte 타입으로 변경시켜 줄것이다.
변경된 코드를 보면 Span<byte> span을 선언하여 사용한다. 또한 기존 코드에서는 각 패킷의 사이즈를 하드코딩(2, 2, 8)하여 넣어주었으나, sizeof를 활용하여 숫자로 사용하는 것이 아닌 사이즈 크기에 따라 처리하도록 변경해준다. 마지막으로 Slice를 이용하여 필요한 부분을 잘라서, 자른 부분에 byte를 넣어주는 형태로 바꿔준다. Slice는 해당 배열의 시작 위치 부터, 사이즈만큼 복사하여 뱉어준다. 따라서 기존에 선언한 span 배열에는 아무런 영향을 끼치지 않는다.
Session Read
Wirte하는 부분을 위와 같이 변경하였으므로 Read 해주는 부분에서도 Span을 이용하여 값을 받아와 역직렬화를 진행해 줄 것이다.
해당 메서드 내부에서는 ReadOnlySpan을 사용하는데, 이는 말 그대로 읽기 전용으로 사용되는 것이다. Write와 마찬가지로 Slice를 사용하여 필요한 영역만 잘라 이를 복사하여 ToInt64로 변환하기만 하면 된다.
🎷 String Packet 처리
String Packet을 처리하기 전 개선하는 방법에 대해서 알아보았다. 이제 진짜로 String 타입의 패킷들은 어떻게 직렬화(Serialization)를 처리하는지 학습할 것이다.
String 타입이 다른 타입과 다른 이유는 타입의 크기가 정해져있지 않기 때문에 패킷을 전송할 때 필요한 공간이 얼마인지 모르기 때문에 다른 타입과 다르다.
UTF-8을 사용해야 할까? 16을 사용해야 할까?
지난 시간에서 학습한 내용을 바탕으로 둘은 약간의 차이점을 가지고 있다. 그러나 C#에서는 기본적으로 char와 string을 UTF-16으로 사용하기 때문에 구태여 UTF-8로 재 변환하여 사용하는 것이 아닌 UTF-16을 사용한다.
🧞♀️ String Packet 처리 방법
String Packet은 위에서 언급한 것과 같이 사이즈 예측이 어렵기 때문에 다음과 같이 두 단계로 구분하여 처리한다.
1. String Packet의 사이즈 먼저 확인하기.
2. 내부 데이터 값 전송하기
Session Write 수정
Encoding.Unicode.GetbyteCount 메서드를 활용한다면 string으로 선언한 name 변수의 사이즈를 불러올 수 있다.
위 이미지와 같은 방법으로 구했다면, 일전에 사용했던 Array.Copy를 이용하여 byte 타입의 name을 세그먼트로 부터 구한 길이를 넘겨주면 된다.
Session Read 수정
Read에서는 Write와는 반대로 길이를 내부 데이터 값 가져와 확인하고, 해당 길이만큼 GetString 메서드를 이용해 데이터를 읽어주면 된다.
이 후, playerName 값에 임의의 값을 넣어서 실행해보면 정상적으로 string 데이터를 받아오는 것을 확인할 수 있다. 👍
🚩 멈추지 않는 코드개선...
위에서 Write를 할 때에 일전에 배웠던 것처럼 Array.Copy를 이용하여 불필요하게 새로운 배열을 만들어 준다. 하지만 이를 GetBytes 메서드의 다른 오버라이딩 방법을 통해 개선할 수 있다.
Encoding.Unicode.GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex)
// s의 값을 charIndex~charCount 만큼을 byte 타입으로 캐스팅하고 이를 bytes의 byteIndex 넣어주고, 몇 바이트를 사용했는지 int 값으로 뱉어준다.
따라서 이를 다시 활용한다면 this.name은 0에서부터 this.name.length 만큼을 세그먼트에 복사하여 담아준다.
위와 같이 적용한다면 기존에 사용하던 방식과 달리 GetBytes로 값을 먼저 담아주고, 크기 값을 정리하는 형태로 변경된다. 😎
'공부 > 인프런 - Rookiss' 카테고리의 다른 글
Part 4-4-6. 패킷 직렬화 : Packet Generator #1 (1) | 2023.12.08 |
---|---|
Part 4-4-5. 패킷 직렬화 : Serialization #4 (1) | 2023.12.07 |
Part 4-4-3. 패킷 직렬화 : UTF-8 vs UTF-16 (1) | 2023.12.06 |
Part 4-4-2. 패킷 직렬화 : Serialization #2 (0) | 2023.12.04 |
Part 4-4-1. 패킷 직렬화 : Serialization #1 (1) | 2023.11.30 |