public void Start(Socket socket)
{
_socket = socket;
_recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvCompleted);
_recvArgs.SetBuffer(new byte[1024], 0, 1024);
RegisterRecv();
_sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
_sendArgs.BufferList = _pendingList;
// bufferlist에 직접적으로 Add를 사용하여 추가할 시 에러 발생
}
listner가 보내준 소켓을 저장하여 사용
receiver가 사용할 이벤트 변수와
sender가 사용할 이벤트 변수를 나눠서 사용
void RegisterRecv()
{
_recvBuffer.Clean();
ArraySegment<byte> segment = _recvBuffer.ReceiveSegment;
_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 (0 < args.BytesTransferred && args.SocketError == SocketError.Success)
{
try
{
if (_recvBuffer.OnWrite(args.BytesTransferred) == false)
{
Disconnect();
return;
}
int processLength = OnReceive(_recvBuffer.DataSegment);
if (processLength < 0 || _recvBuffer.DataSize < processLength)
{
Disconnect();
return;
}
if (_recvBuffer.OnRead(processLength) == false)
{
Disconnect();
return;
}
RegisterRecv();
}
catch (Exception e)
{
Console.WriteLine($"OnRecvCompleted error: {e.Message}");
}
}
else
{
Disconnect();
}
}
receiver는 lisnter와 마찬가지로
ReceiveAsync를 실행 후 이벤트가 완료되거나 pending되지 않을 경우
OnRecvCompleted 콜백 함수가 실행되도록 함
void RegisterSend()
{
while (_sendQueue.Count > 0)
{
_pendingList.Add(_sendQueue.Dequeue());
}
_sendArgs.BufferList = _pendingList;
bool pending = _socket.SendAsync(_sendArgs);
if (pending == false)
{
OnSendCompleted(null, _sendArgs);
}
}
void OnSendCompleted(object sender, SocketAsyncEventArgs args)
{
lock (_lock)
{
if (0 < args.BytesTransferred && args.SocketError == SocketError.Success)
{
try
{
_pendingList.Clear();
OnSend(args.BytesTransferred);
if (0 < _sendQueue.Count)
{
RegisterSend();
}
}
catch (Exception e)
{
Console.WriteLine($"OnSendCompleted error: {e.Message}");
}
}
else
{
Disconnect();
}
}
}
public void Send(ArraySegment<byte> segment)
{
lock (_lock)
{
_sendQueue.Enqueue(segment);
if (_pendingList.Count == 0)
{
RegisterSend();
}
}
}
패킷을 보내고 받는 함수는 커널을 통해야 하기 때문에 비교적 느린 편이다.
그렇기 때문에 한번 send할 때 패킷을 모아 보내는 것이 유리하다.
_pendingList는 send를 직접적으로 호출하는 스레드와
send가 완료되고 비동기적으로 불린 스레드가 동시에 접근할 수 있기 때문에 lock을 사용
class GameSession : Session
{
public override void OnConnected(EndPoint endPoint)
{
Packet packet = new Packet();
packet.size = 4;
packet.packetID = 7;
Console.WriteLine($"OnConnected : {endPoint}");
for (int i = 0; i < 5; i++)
{
byte[] buffer1 = BitConverter.GetBytes(packet.size);
byte[] buffer2 = BitConverter.GetBytes(packet.packetID);
ArraySegment<byte> openSegment = SendBufferHelper.Open(4096);
Array.Copy(buffer1, 0, openSegment.Array, openSegment.Offset, buffer1.Length);
Array.Copy(buffer2, 0, openSegment.Array, openSegment.Offset + buffer1.Length, buffer2.Length);
ArraySegment<byte> segment = SendBufferHelper.Close(packet.size);
Send(segment);
}
}
public override void OnDisconnected(EndPoint endPoint)
{
Console.WriteLine($"Disconnected: {endPoint}");
}
public override int OnReceive(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($"transfered byte {numOfBytes}");
}
}
서버 코어에서 구현한 session을 상속받아 이벤트 함수들을 구현하여 사용
session에서 이벤트 핸들러들을 받아서 해당 이벤트를 호출하는 것도 가능