🌞 Listener의 class화
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ServerCore
{
class Listener
{
Socket _listenerSocket;
public void Init(IPEndPoint endPoint)
{
//문지기
Socket listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
//문지기 교육
listenSocket.Bind(endPoint);
//영업시작
//backlog : 최대 대기수
listenSocket.Listen(10);
}
public Socket Accept()
{
return _listenerSocket.Accept();
}
}
}
🌞 블로킹 함수
이러한 함수들은 게임서버에서는 최대한 피해야 하는 함수들이다.
비동기식 함수가 필요하다!
🌞 비동기식
Accept가 동기식 메소드 였다면 AcceptAsync 는 비동기식 메소드이다. 이 메소드에 대해서 짧게 이야기 하면, AcceptAsync는 인수로 SocketAsyncEventArgs를 받는다. 이 인수는 Completed 라는 eventHandler를 가지고 있는데, 이 eventHandler는 AcceptAsync가 종료될 때 실행 된다.
AcceptAsync -> Sleep -> 다른 일 -> AccepAsync -> AcceptAsync 완료 -> SocketAsyncEventArgs.Completed 실행 -> AcceptAsync 다시실행
bool AccepAsync(SocketAsyncEventArgs)
return값은 bool로 만약에 운좋게 Accept가 바로 이루어진 경우에는 false값을, Accept가 이루어지지 않았으면 true를 return한다.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ServerCore
{
class Listener
{
Socket _listenerSocket;
Action<Socket> _onAcceptHandler;
public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
{
//문지기
_listenerSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_onAcceptHandler += onAcceptHandler;
//문지기 교육
_listenerSocket.Bind(endPoint);
//영업시작
//backlog : 최대 대기수
_listenerSocket.Listen(10);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
RegisterAccept(args);
}
void RegisterAccept(SocketAsyncEventArgs args)
{
args.AcceptSocket = null;
bool pending = _listenerSocket.AcceptAsync(args);
if (pending == false)
OnAcceptCompleted(null, args);
}
void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
if (args.SocketError == SocketError.Success)
{
_onAcceptHandler.Invoke(args.AcceptSocket);
}
else
Console.WriteLine(args.SocketError.ToString());
RegisterAccept(args);
}
public Socket Accept()
{
return _listenerSocket.Accept();
}
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ServerCore
{
class Program
{
static Listener _listener = new Listener();
static void OnAccetHandler(Socket clientSocket)
{
try
{
//받는다.
byte[] recvBuff = new byte[1024];
int recvBytes = clientSocket.Receive(recvBuff);
string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
Console.WriteLine($"[From Client] {recvData}");
//보낸다.
byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to MMORPG Server !");
clientSocket.Send(sendBuff);
//쫓아낸다.
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
static void Main(string[] args)
{
//DNS(Domain Name Service
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
_listener.Init(endPoint,OnAccetHandler);
while (true)
{
;
}
}
}
}
cf) Action의 그냥 실행과 Invoke 실행의 차이
Invoke 실행 : Action이 null 일때는 실행하지 않음
그냥 실행 : Action이 null인경우 NullReference 반환
cf2) Class 매개변수는 값을 복사하는 형식이 아닌 참조형이다.
args.AcceptSocket = null; 을 해주는 이유