Listener 구현
이전에 예시로 만든 소켓 프로그래밍 구조는 문제점이 있다. 그것은 바로 블로킹 방식을 사용해서 받아올 것이 없을 때, 코드가 멈출 수 있는 것이다. 그래서 비동기 방식으로 수정하는 보완이 필요하다.
우선 ServerCore에 Listener class를 추가하자.
Listener
using System.Net;
using System.Net.Sockets;
namespace ServerCore
{
class Listener
{
private Socket _listenSocket;
private Action<Socket> _onAcceptHandler;
public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
{
_listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_onAcceptHandler += onAcceptHandler;
// Bind (문지기 교육)
_listenSocket.Bind(endPoint);
// Listen (영업 시작)
// backlog : 최대 대기 수
_listenSocket.Listen(10);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
RegisterAccept(args);
}
void RegisterAccept(SocketAsyncEventArgs args)
{
// 이벤트를 재사용 하기 때문에 null값 입력
args.AcceptSocket = null;
// 비동기 방식으로 프로그램이 종료되지 않도록 함
// 바로 접속하면 직접 OnAcceptCompleted 를 실행
bool pending = _listenSocket.AcceptAsync(args);
if (!pending)
{
OnAcceptCompleted(null,args);
}
}
// 멀티쓰레드로 실행되는 부분, 공유 자원을 사용하는 경우, 멀티쓰레드 동기화 문제를 반드시 해결해야 됨
void OnAcceptCompleted(object sender,SocketAsyncEventArgs args)
{
if (args.SocketError == SocketError.Success)
{
// Socket clientSocket = _listener.Accept(); 와 같은 역할
_onAcceptHandler.Invoke(args.AcceptSocket);
}
else
{
Console.WriteLine(args.SocketError.ToString());
}
// 모든 처리 후, 다시 RegisterAccept 실행
RegisterAccept(args);
}
}
}
Main
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ServerCore
{
class Program
{
private static Listener _listener = new Listener();
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 YoungJune Server!");
clientSocket.Send(sendBuff);
// 퇴장
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
static void Main(string[] args)
{
//DNS (Domain Name System)
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)
{
}
}
}
}
'서버 > 서버 이론' 카테고리의 다른 글
Session 구현 (0) | 2025.04.13 |
---|---|
소켓 프로그래밍 기초 (0) | 2025.04.09 |
네트워크 기초 이론 (0) | 2025.04.07 |
Thread Local Storage (TLS) (0) | 2025.04.06 |
Lock 구현 (0) | 2025.03.29 |