본문 바로가기
카테고리 없음

네트워크프로그래밍 - Slice메서드 , 가변적인(String)크기의 데이터 직렬화 및 패킷에 넣기

by 코딩하는 돼징 2023. 8. 11.
반응형

1. Slice

Slice메서드를 사용해서 Span<byte>의 일부를 추출 할 수 있다. 이를 통해 불필요한 Offset계산을 줄일 수 있다.

public Span<T> Slice(int start, int length);

01 기존 코드

Offset 계싼이 반복해서 나타나며 각 Write작업마다 계산을 수동으로 진행한다.

success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset + count, openSegment.Count - count), this.packetId);

02 수정한 코드

Slice를 사용하여 반복적인 Offset계산을 줄이고 더 가독성이 높아졌다.

success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.packetId);

03 수정된 Write

Span<byte> s = new Span<byte>(segement.Array, segement.Offset, segement.Count);
count += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(count,s.Length-count), this.packetId);
count += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.playerId);
count += sizeof(long);
success &= BitConverter.TryWriteBytes(s, count);

04 수정된 Read

ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(openSegment.Array, openSegment.Offset, openSegment.Count);

count += 2;
count += 2;

this.playerId = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
count += 8;

2. String 직렬화해서 패킷에 넣는 법

01 name설정

class PlayerInfoReq : Packet
{
    public long playerId;
    public string name;
}

 public override void OnConnected(EndPoint endPoint)
{
    PlayerInfoReq packet = new PlayerInfoReq() { playerId = 1001, name = "piggy" };
}

02 Write에서 string

string을 통으로 byte배열로 만드는 것 보다 2byte로 string의 크기가 얼마인지 string의 length를 보낸 다음에 해당하는 크기의 데이터를 이어서 보내는게 좋다.


this.name에 보내고 싶은 데이터가 들어있는데 byte배열로 바꿔야한다.

문자열의 길이와 바이트 배열로 변환된 데이터의 길이가 다를 수 있다.예를 들어 name = "piggy"의 문자열의 길이는 5이다. 하지만 byte배열로 변환하면 5byte가 아니라 10byte가 된다.

이렇게 되는 이유는 UTF-16인코딩으로 변환될 경우 모든 문자가 2byte로 인코딩되기 때문이다.

 

UnicodeEncoding.GetByteCount

지정된 문자열을 Unicode인코딩을 사용하여 byte배열로 변환할 때 필요한 바이트 수를 반환

public override int GetByteCount(string s);

매개변수

s : 인코딩할 문자열

반환

인코딩된 문자열의 바이트 수


ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this.name);

1) len 집어 넣기

nameLen변수를 s에 넣기

success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
count += sizeof(ushort);

2) 해당된 데이터 넣기

string을 Byte배열로

Array.Copy(Encoding.Unicode.GetBytes(this.name), 0, segement.Array, count, nameLen);
count += nameLen;

03. Read에서 string

byte를 string으로

ushort nameLen = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
count += sizeof(ushort);
this.name = Encoding.Unicode.GetString(s.Slice(count, nameLen));

개선하기

01 수정 전 코드

Encoding.Unicode.GetByteCount을 사용하여 문자열의 전체길이를 계산한다.

public virtual int GetByteCount(string s);

ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this.name);
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
count += sizeof(ushort);

Array.Copy를 사용하여 데이터를 작성하기 전에 count를 증가시킨다.

Array.Copy(Encoding.Unicode.GetBytes(this.name), 0, segement.Array, count, nameLen);
count += nameLen;

02 수정 후 코드

바이트 배열에 직전 변환된 데이터를 기록한다. name의 길이만큼만 바이트 배열로 변환되어 segment에 바로 작성한다. 

Encoding.Unicode.GetBytes(this.name, 0, this.name.Length, segement.Array, segement.Offset + count);

GetBytes

지정된 문자열의 일부를 바이트 배열로 변환하여 저장하는 메서드

public virtual int GetBytes (string s, int charIndex, int charCount, byte[] bytes, int byteIndex);

매개변수

s : 변환할 문자열

charIndex : 문자열에서 변환을 시작할 문자의 인덱스

charCount : 변환할 문자의 수

bytes : 변환된 바이트를 저장할 대상 바이트 배열

byteIndex : 바이트 배열에서 변환된 데이터를 저장할 시작 위치

반환

변환된 바이트 수


count는 따로 수정하지 않는다.

count += nameLen;
success &= BitConverter.TryWriteBytes(s, count);

 

 

 

 

 

 

 

 

 

참고 :  본 내용은 MMORPG  PART4 강의를 수강하여 작성하였습니다.

https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4

 

 

 

 

 

 

 

 

 

 

 

반응형

댓글