코드
ServerCore
Session.cs
namespace ServerCore
{
public abstract class PacketSession : Session
{
public static readonly int HeaderSize = 2;
public sealed override int OnRecv(ArraySegment<byte> buffer)
{
int processLen = 0;
while (true)
{
if (buffer.Count < HeaderSize)
break;
ushort dataSize = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
if (buffer.Count < dataSize)
break;
OnRecvPacket(new ArraySegment<byte>(buffer.Array, buffer.Offset, dataSize));
processLen += dataSize;
buffer = new ArraySegment<byte>(buffer.Array, buffer.Offset + dataSize, buffer.Count - dataSize);
}
return processLen;
}
public abstract void OnRecvPacket(ArraySegment<byte> buffer);
}
public abstract class Session
{
Socket _socket;
int _disconnected = 0;
RecvBuffer _recvBuffer = new RecvBuffer(1024);
object _lock = new object();
Queue<ArraySegment<byte>> _sendQueue = new Queue<ArraySegment<byte>>();
List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();
SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
SocketAsyncEventArgs _recvArgs = new SocketAsyncEventArgs();
public abstract void OnConnected(EndPoint endPoint);
public abstract int OnRecv(ArraySegment<byte> buffer);
public abstract void OnSend(int numOfBytes);
public abstract void OnDisconnected(EndPoint endPoint);
public void Start(Socket socket)
{
_socket = socket;
_recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvCompleted);
_sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
RegisterRecv();
}
public void Send(ArraySegment<byte> sendBuff)
{
lock (_lock)
{
_sendQueue.Enqueue(sendBuff);
if (_pendingList.Count == 0)
RegisterSend();
}
}
public void Disconnect()
{
if (Interlocked.Exchange(ref _disconnected, 1) == 1)
return;
OnDisconnected(_socket.RemoteEndPoint);
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}
#region 네트워크 통신
void RegisterSend()
{
while (_sendQueue.Count > 0)
{
ArraySegment<byte> buff = _sendQueue.Dequeue();
_pendingList.Add(buff);
}
_sendArgs.BufferList = _pendingList;
bool pending = _socket.SendAsync(_sendArgs);
if (pending == false)
OnSendCompleted(null, _sendArgs);
}
void OnSendCompleted(object sender, SocketAsyncEventArgs args)
{
lock (_lock)
{
if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success)
{
try
{
_sendArgs.BufferList = null;
_pendingList.Clear();
OnSend(_sendArgs.BytesTransferred);
if (_sendQueue.Count > 0)
RegisterSend();
}
catch (Exception e)
{
Console.WriteLine($"OnSendCompleted Failed {e}");
}
}
else
{
Disconnect();
}
}
}
void RegisterRecv()
{
_recvBuffer.Clean();
ArraySegment<byte> segment = _recvBuffer.WriteSegment;
_recvArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
bool pending = _socket.ReceiveAsync(_recvArgs);
if (pending == false)
OnRecvCompleted(null, _recvArgs);
}
void OnRecvCompleted(object sender, SocketAsyncEventArgs args)
{
if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success)
{
try
{
if (_recvBuffer.OnWrite(args.BytesTransferred) == false)
{
Disconnect();
return;
}
int processLen = OnRecv(_recvBuffer.ReadSegment);
if (processLen < 0 || _recvBuffer.DataSize < processLen)
{
Disconnect();
return;
}
if (_recvBuffer.OnRead(processLen) == false)
{
Disconnect();
return;
}
RegisterRecv();
}
catch (Exception e)
{
Console.WriteLine($"OnRecvCompleted Failed {e}");
}
}
else
{
Disconnect();
}
}
#endregion
}
}
Server
ClientSession.cs
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 string name;
public struct SkillInfo
{
public int id;
public short level;
public float duration;
public bool Write(Span<byte> s, ref ushort count)
{
bool success = true;
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.id);
count += sizeof(int);
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.level);
count += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.duration);
count += sizeof(float);
return success;
}
public void Read(ReadOnlySpan<byte> s, ref ushort count)
{
id = BitConverter.ToInt32(s.Slice(count, s.Length - count));
count += sizeof(int);
level = BitConverter.ToInt16(s.Slice(count, s.Length - count));
count += sizeof(short);
duration = BitConverter.ToSingle(s.Slice(count, s.Length - count));
count += sizeof(float);
}
}
public List<SkillInfo> skills = new List<SkillInfo>();
public PlayerInfoReq()
{
this.playerId = (ushort)PacketID.PlayerInfoReq;
}
public override void Read(ArraySegment<byte> segment)
{
ushort pos = 0;
ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(segment.Array, segment.Offset, segment.Count);
pos += sizeof(ushort);
pos += sizeof(ushort);
this.playerId = BitConverter.ToInt64(s.Slice(pos, s.Length - pos));
pos += sizeof(long);
ushort nameLen = BitConverter.ToUInt16(s.Slice(pos, s.Length - pos));
pos += sizeof(ushort);
this.name = Encoding.Unicode.GetString(s.Slice(pos, nameLen));
pos += nameLen;
skills.Clear();
ushort skillLen = BitConverter.ToUInt16(s.Slice(pos, s.Length - pos));
pos += sizeof(ushort);
for (int i = 0; i < skillLen; i++)
{
SkillInfo skill = new SkillInfo();
skill.Read(s, ref pos);
skills.Add(skill);
}
}
public override ArraySegment<byte> Write()
{
ArraySegment<byte> segment = SendBufferHelper.Open(4096);
ushort size = 0;
bool success = true;
Span<byte> s = new Span<byte>(segment.Array, segment.Offset, segment.Count);
size += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), this.packetId);
size += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), this.playerId);
size += sizeof(long);
ushort nameLen = (ushort)Encoding.Unicode.GetBytes(this.name, 0, this.name.Length, segment.Array, segment.Offset + size + sizeof(ushort));
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), nameLen);
size += sizeof(ushort);
size += nameLen;
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), (ushort)skills.Count);
size += sizeof(ushort);
foreach (SkillInfo skill in skills)
success &= skill.Write(s, ref size);
success &= BitConverter.TryWriteBytes(s, size);
if (success == false)
return null;
return SendBufferHelper.Close(size);
}
}
public enum PacketID
{
PlayerInfoReq = 1,
PlayerInfoOk = 2,
}
class ClientSession : PacketSession
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
Thread.Sleep(5000);
Disconnect();
}
public override void OnRecvPacket(ArraySegment<byte> buffer)
{
int pos = 0;
ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
pos += 2;
ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + pos);
pos += 2;
switch ((PacketID)id)
{
case PacketID.PlayerInfoReq:
{
PlayerInfoReq p = new PlayerInfoReq();
p.Read(buffer);
Console.WriteLine($"PlayerInfoReq : {p.playerId} {p.name}");
foreach(PlayerInfoReq.SkillInfo skill in p.skills)
Console.WriteLine($"Skill({skill.id})({skill.level})({skill.duration})");
}
break;
case PacketID.PlayerInfoOk:
{
int hp = BitConverter.ToInt32(buffer.Array, buffer.Offset + pos);
pos += 4;
int attack = BitConverter.ToInt32(buffer.Array, buffer.Offset + pos);
pos += 4;
}
break;
default:
break;
}
Console.WriteLine($"RecvPacketId: {id}, Size {size}");
}
public void Handle_PlayerInfoOk(ArraySegment<byte> buffer)
{
}
public override void OnDisconnected(EndPoint endPoint)
{
Console.WriteLine($"OnDisconnected : {endPoint}");
}
public override void OnSend(int numOfBytes)
{
Console.WriteLine($"Transferred bytes: {numOfBytes}");
}
}
}
DummyClient
ServerSession.cs
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 string name;
public struct SkillInfo
{
public int id;
public short level;
public float duration;
public bool Write(Span<byte> s, ref ushort count)
{
bool success = true;
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length-count), this.id);
count += sizeof(int);
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.level);
count += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.duration);
count += sizeof(float);
return success;
}
public void Read(ReadOnlySpan<byte> s, ref ushort count)
{
id = BitConverter.ToInt32(s.Slice(count, s.Length - count));
count += sizeof(int);
level = BitConverter.ToInt16(s.Slice(count, s.Length - count));
count += sizeof(short);
duration = BitConverter.ToSingle(s.Slice(count, s.Length - count));
count += sizeof(float);
}
}
public List<SkillInfo> skills = new List<SkillInfo>();
public PlayerInfoReq()
{
this.playerId = (ushort)PacketID.PlayerInfoReq;
}
public override void Read(ArraySegment<byte> segment)
{
ushort pos = 0;
ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(segment.Array, segment.Offset, segment.Count);
pos += sizeof(ushort);
pos += sizeof(ushort);
this.playerId = BitConverter.ToInt64(s.Slice(pos, s.Length - pos));
pos += sizeof(long);
ushort nameLen = BitConverter.ToUInt16(s.Slice(pos, s.Length - pos));
pos += sizeof(ushort);
this.name = Encoding.Unicode.GetString(s.Slice(pos, nameLen));
pos += nameLen;
skills.Clear();
ushort skillLen = BitConverter.ToUInt16(s.Slice(pos, s.Length - pos));
pos += sizeof(ushort);
for(int i=0;i <skillLen; i++)
{
SkillInfo skill = new SkillInfo();
skill.Read(s, ref pos);
skills.Add(skill);
}
}
public override ArraySegment<byte> Write()
{
ArraySegment<byte> segment = SendBufferHelper.Open(4096);
ushort size = 0;
bool success = true;
Span<byte> s = new Span<byte>(segment.Array, segment.Offset, segment.Count);
size += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), this.packetId);
size += sizeof(ushort);
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), this.playerId);
size += sizeof(long);
ushort nameLen = (ushort)Encoding.Unicode.GetBytes(this.name, 0, this.name.Length, segment.Array, segment.Offset + size + sizeof(ushort));
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), nameLen);
size += sizeof(ushort);
size += nameLen;
success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), (ushort)skills.Count);
size += sizeof(ushort);
foreach (SkillInfo skill in skills)
success &= skill.Write(s, ref size);
success &= BitConverter.TryWriteBytes(s, size);
if (success == false)
return null;
return SendBufferHelper.Close(size);
}
}
public enum PacketID
{
PlayerInfoReq = 1,
PlayerInfoOk = 2,
}
class ServerSession : Session
{
static unsafe void ToBytes(byte[] array, int offset, ulong value)
{
fixed (byte* ptr = &array[offset])
*(ulong*)ptr = value;
}
static unsafe void ToBytes<T>(byte[] array, int offset, T value) where T : unmanaged
{
fixed (byte* ptr = &array[offset])
*(T*)ptr = value;
}
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
PlayerInfoReq packet = new PlayerInfoReq() { packetId = 1, playerId = 1001, name = "STREVELUN" };
packet.skills.Add(new PlayerInfoReq.SkillInfo() { id=101, level = 1,duration=3.0f });
packet.skills.Add(new PlayerInfoReq.SkillInfo() { id=1013153, level = 2,duration=4.0f });
packet.skills.Add(new PlayerInfoReq.SkillInfo() { id=1016, level = 3,duration=5.0f });
packet.skills.Add(new PlayerInfoReq.SkillInfo() { id=10111, level = 7,duration=6.6f });
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}");
}
}
}