Recv까지 구현된 코드는 아래와 같다
// ServerCore
// Session.cs
class Session
{
Socket _socket;
int _disconnected = 0;
public void Start(Socket socket)
{
_socket = socket;
SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs();
recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvComplete);
recvArgs.SetBuffer(new byte[1024], 0/*시작인덱스*/, 1024);
RegisterRecv(recvArgs);
}
public void Send(byte[] sendBuff)
{
_socket.Send(sendBuff);
}
public void Disconnect()
{
if(Interlocked.Exchange(ref _disconnected, 1) == 1)
return;
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}
void RegisterRecv(SocketAsyncEventArgs args)
{
bool pending = _socket.ReceiveAsync(args);
if(pending == false)
OnRecvComplete(null, args);
}
void OnRecvComplete(object sender, SocketAsyncEventArgs args)
{
if(args.BytesTransferred > 0 && args.SocketError ==SocketError.Success)
{
try{
string recvData = Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred);
RegisterRecv(args);
}
catch()
{
}
}
else
{
Disconnect();
}
}
}
Send를 구현해보자
// ServerCore
// Session.cs
class Session
{
Socket _socket;
int _disconnected = 0;
// ...
/*
Recv와는 달리 Send는 호출되는 시점이 정해져있지 않다
(Recv의 경우 시작과 동시에 Start로 동작, Recv이후 다시 Register)
Send는 언제 Register하고 Send이후 언제다시 Register를 해야할까?
*/
public void Send(byte[] sendBuff)
{
// 문제 1. SocketAsyncEventArgs를 재사용할 수 없음
SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
sendArgs.SetBuffer(sendBuff, 0, sendBuff.Length);
RegisterSend(sendArgs);
}
// ...
void RegisterSend(SocketAsyncEventArgs args)
{
// 문제 2. Send이후 Register과정에서 SendAsync를 호출하는데
// 커널단에서 처리하는 함수라 속도가 느림
bool pending = _socket.SendAsync(args);
OnSendCompleted(null, args);
}
// ...
해결해보자
// ServerCore
// Session.cs
class Session
{
Socket _socket;
int _disconnected = 0;
// Queue에 보낼 데이터를 미리 담아두자
Queue<byte[]> _sendQueue = new Queu<byte[]>();
bool _pending = false;
SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
object _lock = new object();
// ...
public void Start(Socket socket)
{
_socket = socket;
SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs();
recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvComplete);
recvArgs.SetBuffer(new byte[1024], 0/*시작인덱스*/, 1024);
// Send 델리게이터를 시작시점에 연결
_sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
RegisterRecv(recvArgs);
}
public void Send(byte[] sendBuff)
{
lock(_lock)
{
_sendQueue.Enqueue(sendBuff);
if(_pending == false)
RegisterSend();
}
}
// ...
void RegisterSend()
{
_pending = true;
byte[] buff = _sendQueue.Dequeue();
_sendArgs.SetBuffer(buff, 0, buff.Length);
bool pending = _socket.SendAsync(_sendArgs));
OnSendCompleted(null, _sendArgs);
}
// ...
void OnSendComplete(object sender, SocketAsyncEventArgs args)
{
lock(_lock)
{
if(args.BytesTransferred > 0 && args.SocketError ==SocketError.Success)
{
try{
// SendQueue안에 남아있는지 확인
if(_sendQueue.Count > 0)
{
RegisterSend();
}
else
_pending = false;
}
catch()
{
}
}
else
{
Disconnect();
}
}
}