서버는 클라이언트로부터 패킷을 받으면 거짓말을 하고 있다 가정하며 코드를 구현해야 한다.
지난 글(Serialization [1])에 이어서 자동화에 더 가깝게 수정하고, 패킷 사이즈를 검사할 것이다.
💻 코드
Packet
Packet
의 쓰기(Write)
, 읽기(Read)
를 추가하고 PlayerInfoReq
를 생성하여 상속 받아준다.
기존에 Client
와 Server
가 송수신하던 코드를 PlayerInfoReq
에 옮겨주었다.
패킷을 받았을 때 사이즈를 변조하여 보내면 그대로 받는 문제점이 있었다.
이를 해결하기 위해 Read
에서 new ReadOnlySpan<byte>()
을 사용해 범위를 지정하여 받도록 수정되었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public abstract class Packet
{
public ushort size;
public ushort packetId;
public abstract ArraySegment<byte> Write();
public abstract void Read(ArraySegment<byte> s);
}
class PlayerInfoReq : Packet
{
public long playerId;
public PlayerInfoReq()
{
this.packetId = (ushort)PacketID.PlayerInfoReq;
}
public override void Read(ArraySegment<byte> s)
{
ushort count = 0;
//ushort size = BitConverter.ToUInt16(s.Array, s.Offset);
count += 2;
//ushort id = BitConverter.ToUInt16(s.Array, s.Offset + count);
count += 2;
// long playerId = BitConverter.ToInt64(s.Array, s.Offset + count);
this.playerId = BitConverter.ToInt64(new ReadOnlySpan<byte>(s.Array, s.Offset + count, s.Count - count));
count += 8;
}
public override ArraySegment<byte> Write()
{
// 4096 버퍼 사용하기 (이름이 길어 segment -> s)
ArraySegment<byte> s = SendBufferHelper.Open(4096);
bool success = true;
ushort count = 0;
// size 공간 +
count += 2;
// packetId 공간 +
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.packetId);
count += 2;
// playerId 공간 +
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.playerId);
count += 8;
// 총 사이즈는 다 넣어야 알 수 있기 때문에 마지막에 넣기
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset, s.Count), count);
if (success == false)
return null;
// 얼마나 썼는지 알려주기
return SendBufferHelper.Close(count);
}
}
public enum PacketID
{
PlayerInfoReq = 1,
PlayerInfoOk = 2,
}
Client
[ Source Code (Click) ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
namespace DummyClient
{
public abstract class Packet
{
public ushort size;
public ushort packetId;
public abstract ArraySegment<byte> Write();
public abstract void Read(ArraySegment<byte> s);
}
class PlayerInfoReq : Packet
{
public long playerId;
public PlayerInfoReq()
{
this.packetId = (ushort)PacketID.PlayerInfoReq;
}
public override void Read(ArraySegment<byte> s)
{
ushort count = 0;
//ushort size = BitConverter.ToUInt16(s.Array, s.Offset);
count += 2;
//ushort id = BitConverter.ToUInt16(s.Array, s.Offset + count);
count += 2;
this.playerId = BitConverter.ToInt64(new ReadOnlySpan<byte>(s.Array, s.Offset + count, s.Count - count));
count += 8;
}
public override ArraySegment<byte> Write()
{
// 4096 버퍼 사용하기 (이름이 길어 segment -> s)
ArraySegment<byte> s = SendBufferHelper.Open(4096);
bool success = true;
ushort count = 0;
// size 공간 +
count += 2;
// packetId 공간 +
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.packetId);
count += 2;
// playerId 공간 +
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.playerId);
count += 8;
// 총 사이즈는 다 넣어야 알 수 있기 때문에 마지막에 넣기
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset, s.Count), count);
if (success == false)
return null;
// 얼마나 썼는지 알려주기
return SendBufferHelper.Close(count);
}
}
public enum PacketID
{
PlayerInfoReq = 1,
PlayerInfoOk = 2,
}
class ServerSession : Session
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
PlayerInfoReq packet = new PlayerInfoReq(){ playerId = 1001 };
// 보낸다
//for (int i = 0; i < 5; i++)
{
ArraySegment<byte> s = packet.Write();
if (s != null)
Send(s);
}
}
public override void OnDisConnected(EndPoint endPoint)
{
Console.WriteLine($"OnDisConnected : {endPoint}");
}
public override int OnRecv(ArraySegment<byte> buffer)
{
string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
Console.WriteLine($"[From Server] {recvData}");
return buffer.Count;
}
public override void OnSend(int numOfBytes)
{
Console.WriteLine($"Transferred Bytes : {numOfBytes}");
}
}
}
Server
[ Source Code (Click) ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
namespace Server
{
public abstract class Packet
{
public ushort size;
public ushort packetId;
public abstract ArraySegment<byte> Write();
public abstract void Read(ArraySegment<byte> s);
}
class PlayerInfoReq : Packet
{
public long playerId;
public PlayerInfoReq()
{
this.packetId = (ushort)PacketID.PlayerInfoReq;
}
public override void Read(ArraySegment<byte> s)
{
ushort count = 0;
//ushort size = BitConverter.ToUInt16(s.Array, s.Offset);
count += 2;
//ushort id = BitConverter.ToUInt16(s.Array, s.Offset + count);
count += 2;
this.playerId = BitConverter.ToInt64(new ReadOnlySpan<byte>(s.Array, s.Offset + count, s.Count - count));
count += 8;
}
public override ArraySegment<byte> Write()
{
// 4096 버퍼 사용하기 (이름이 길어 segment -> s)
ArraySegment<byte> s = SendBufferHelper.Open(4096);
bool success = true;
ushort count = 0;
// size 공간 +
count += 2;
// packetId 공간 +
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.packetId);
count += 2;
// playerId 공간 +
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.playerId);
count += 8;
// 총 사이즈는 다 넣어야 알 수 있기 때문에 마지막에 넣기
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset, s.Count), count);
if (success == false)
return null;
// 얼마나 썼는지 알려주기
return SendBufferHelper.Close(count);
}
}
public enum PacketID
{
PlayerInfoReq = 1,
PlayerInfoOk = 2,
}
class ClientSession : PacketSession
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
/*Packet packet = new Packet() { size = 100, packetId = 10 };
// 4096 버퍼 사용하기
ArraySegment<byte> openSegment = SendBufferHelper.Open(4096);
// 데이터 바이트 형식으로 가져오기
byte[] hpBuffer = BitConverter.GetBytes(packet.size);
byte[] atkBuffer = BitConverter.GetBytes(packet.packetId);
// sendBuffer에 복사.
Array.Copy(hpBuffer, 0, openSegment.Array, openSegment.Offset, hpBuffer.Length);
Array.Copy(atkBuffer, 0, openSegment.Array, openSegment.Offset + hpBuffer.Length, atkBuffer.Length);
// 얼마나 썼는지 알려주기
ArraySegment<byte> sendBuff = SendBufferHelper.Close(hpBuffer.Length + atkBuffer.Length);
Send(sendBuff);*/
Thread.Sleep(3000);
Disconnect();
}
public override void OnRecvPacket(ArraySegment<byte> buffer)
{
int count = 0;
ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
count += 2;
ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count);
count += 2;
switch ((PacketID)id)
{
case PacketID.PlayerInfoReq:
{
PlayerInfoReq p = new PlayerInfoReq();
p.Read(buffer);
Console.WriteLine($"PlayerInfoReq : {p.playerId}, size : {size}, count : {count}");
}
break;
}
}
public override void OnDisConnected(EndPoint endPoint)
{
Console.WriteLine($"OnDisConnected : {endPoint}");
}
public override void OnSend(int numOfBytes)
{
Console.WriteLine($"Transferred Bytes : {numOfBytes}");
}
}
}