현재까지 구현된 코드
// ServerCore
// Session.cs
class Session
{
Socket _socket;
int _disconnected = 0;
Queue<byte[]> _sendQueue = new Queu<byte[]>();
bool _pending = false;
SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
SocketAsyncEventArgs _recvArgs = 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);
_sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
RegisterRecv();
}
public void Send(byte[] sendBuff)
{
lock(_lock)
{
_sendQueue.Enqueue(sendBuff);
if(_pending == false)
RegisterSend();
}
}
public void Disconnect()
{
if(Interlocked.Exchange(ref _disconnected, 1) == 1)
return;
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}
void RegisterSend()
{
_pending = true;
byte[] buff = _sendQueue.Dequeue();
_sendArgs.SetBuffer(buff, 0, buff.Length);
bool pending = _socket.SendAsync(_sendArgs));
OnSendCompleted(null, _sendArgs);
}
void RegisterRecv()
{
bool pending = _socket.ReceiveAsync(_recvArgs);
if(pending == false)
OnRecvComplete(null, _recvArgs);
}
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();
}
catch()
{
}
}
else
{
Disconnect();
}
}
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();
}
}
}
}
최적화가 필요한 부분이 있다
void RegisterSend()
{
_pending = true;
byte[] buff = _sendQueue.Dequeue();
_sendArgs.SetBuffer(buff, 0, buff.Length);
// SendAsync는 커널을 거드리는 함수이기에 속도가 느리다
bool pending = _socket.SendAsync(_sendArgs));
OnSendCompleted(null, _sendArgs);
}
List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();
// ...
void RegisterSend()
{
// byte[] buff = _sendQueue.Dequeue();
// _sendArgs.SetBuffer(buff, 0, buff.Length);
// 기존에는 SetBuffer로 버퍼를 지정해 보내줬지만
// 버퍼의 리스트로 보내는 방법이 있다
while(_sendQueue.Count > 0)
{
byte[] buff = _sendQueue.Dequeue();
_pendingList.Add(new ArraySegment<byte>(buff, 0, buff.Length));
// _sendArgs.BufferList.Add(new ArraySegment<byte>(buff, 0, buff.Length));
// 참고로 위와 같이 Add를 해서 보내면 안됨(MSDN에 안된다고 나옴, 왜그런지는 잘;; MS에 물어봐야할 듯)
/*
개선점1 : 여기서 패킷이 너무 많을경우 너무 오래 잡고 있을 수 있음(이후 개선)
*/
}
_sendArgs.BufferList = _pendingList;
// SendAsync는 커널을 거드리는 함수이기에 속도가 느리다
bool pending = _socket.SendAsync(_sendArgs);
if(pending == false)
OnSendCompleted(null, _sendArgs);
}
// ...
public void Send(byte[] sendBuff)
{
/*
개선점2 : send가 필요한 데이터를 즉시 send하면 너무 많은 send가 일어난다.
즉시 보내야할 데이터가 아닌경우 모아서 보내게 해야함
*/
lock(_lock)
{
_sendQueue.Enqueue(sendBuff);
if(_pendingList.Count == 0)
// 대기중인 List가 없기에 Send를 Regist
RegisterSend();
}
}
// ...
void OnSendComplete(object sender, SocketAsyncEventArgs args)
{
lock(_lock)
{
if(args.BytesTransferred > 0 && args.SocketError ==SocketError.Success)
{
try{
_sendArgs.BufferList = null;
_pendingList.Clear();
if(_sendQueue.Count > 0)
RegisterSend();
}
catch()
{
}
}
else
{
Disconnect();
}
}
}