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
댓글