C#/네트워크
[c#][서버] Listener
goliot
2024. 6. 5. 16:51
반응형
주의할 점
- 소켓 통신에서, accept, receive, send같은 입출력 계열의 함수는 비동기, 논블로킹 계열 함수로 만들어야 함
비동기 -> Accept 분리하기
- Accept 요청과 완료를 분리하여 구현해야 함
using System.Net;
using System.Net.Sockets;
using System.Text;
class Listener
{
Socket _listenSocket;
Action<Socket> _onAcceptHandler;
public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
{
_listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_onAcceptHandler += onAcceptHandler; //연결 수락시 실행할 콜백 함수
//문지기 교육
_listenSocket.Bind(endPoint);
//영업 시작
_listenSocket.Listen(10);
SocketAsyncEventArgs args = new SocketAsyncEventArgs(); //한번 만들면 계속 재사용
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted); //완료되면, OnAcceptCompleted 실행
RegisterAccept(args);
}
//비동기에선 Accept 요청과 완료가 분리되어야 함
//Register 와 Completed가 뺑뺑이를 돌면서 계속 실행됨
void RegisterAccept(SocketAsyncEventArgs args)
{
args.AcceptSocket = null; //재사용이므로 초기화 시키고 사용
bool pending = _listenSocket.AcceptAsync(args); //비동기로 예약
if(pending == false) //완료가 된 상태, 바로 다음줄로 넘어가는 동안 완료가 된 것
{
OnAcceptCompleted(null, args);
}
}
void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
if(args.SocketError == SocketError.Success)
{
//TODO
_onAcceptHandler.Invoke(args.AcceptSocket);
}
else
{
Console.WriteLine(args.SocketError.ToString());
}
RegisterAccept(args);
//이번 꺼는 완료가 됐으므로, 다음 아이를 위해 재등록
}
}
//------------------------------------------------------------------------------------------
class Program
{
static Listener _listener = new Listener();
//이 함수가 위의 _onAcceptHandler에 등록될 것
static void OnAcceptHandler(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.ToString());
}
}
static void Main(string[] args)
{
//DNS 사용
string host = Dns.GetHostName();
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);
_listener.Init(endPoint, OnAcceptHandler);
Console.WriteLine("Listening...");
while (true)
{
}
}
}
반응형