blocking방식 이용한 Server코드
blocking방식에서 문지기와 관련된 코드 깔끔하게 정리하기
원래 코드
Socket listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
try
{
listenSocket.Bind(endPoint);
listenSocket.Listen(10);
while (true)
{
Console.WriteLine("Listening...");
Socket clientSocket = listenSocket.Accept();
}
고친 코드
static Listener _listener = new Listener();
public void Init(IPEndPoint endPoint)
{
_listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
// 문지기 교육
_listenSocket.Bind(endPoint);
// 영업 시작
// backing : 최대 대기 수
_listenSocket.Listen(10);
}
public Socket Accept()
{
return _listenSocket.Accept();
}
Socket _listenSocket;
try
{
_listener.Init(endPoint);
while (true)
{
Console.WriteLine("Listening...");
// 손님을 입장시킨다.
Socket clientSocket = _listener.Accept();
}
}
이렇게 코드를 고치면 코드의 가독성이 향상되고 재사용성 또한 확장된다.
_listenSocket을 멤버 변수로 사용함으로써 초기화와 수신 대기 작입이 한곳에 처리 된다. 또한 Accept메서드를 외부에서 호출할 수 있게 됨에 따라 다른 부분에서 _listernSocket의 수신된 데이터를 활용할 수 있다.
비동기(Non - blocking) 사용
( 맨 밑에 정리용으로 간단한 낚시예시로 정리해놓았어요 :) )
1. RegisterAccept
_listenOkcet으로부터 클라이언트의 연결 요청을 받기 위해 AcceptAsync을 호출하고 연결 요청이 완료되었을때에는 콜백 메서드인 OnAcceptCompleted가 호출된다.
void RegisterAccept(SocketAsyncEventArgs args)
{
bool pending = _listenSocket.AcceptAsync(args);
if (pending == false)
OnAceeptCompleted();
}
void OnAceeptCompleted()
{
}
pending == false인 경우
AcceptAsync메서드가 즉시 클라이언트 접속 요청이 와서 수락되었다면 pending이 false가 된다. 이는 즉시 OnAceeptCompleted를 호출한다.
pending == true인 경우
클라이언트 접속 요청이 아직 안온 것이므로 AcceptAysnc메서드를 통해 요청이 올때까지 기다린다. 그 후 OnAceeptCompleted메서드는 AcceptAysnc의 콜백으로 호출되어 연결 요청 완료처리를 수행한다.
2. 초기화 및 등록
public void Init(IPEndPoint endPoint)
{
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
}
void RegisterAccept(SocketAsyncEventArgs args)
{
bool pending = _listenSocket.AcceptAsync(args);
if (pending == false)
OnAceeptCompleted(null,args);
}
void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
}
01 SocketAsyncEventArgs 객체 만들기
SocketAsyncEventArgs 의 객체에는 비동기 소켓 작업에 대한 이벤트와 데이터를 저장하는데 사용된다.
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
02 args.Comleted이벤트에 대한 핸들러 등록
이 핸들러는 OnAccept메서드를 이벤트 핸들러로 사용하도록 한다. 이를 통해 클라이언트에 연결 요청이 들어온 후 accept한 경우 이 이벤트가 실행되도록 설정하는 역할이다.
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
03 최초 등록은 우리가 한번 해준다.
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAceeptCompleted);
RegisterAccept(args);
이상태에서 클라이언트의 connect 요청이 왔다고 하면 콜백 방식으로 onAcceptCompleted가 호출된다
3. OnAceeptCompleted
AcceptAsync메서드의 비동기 작업이 완료되었을때 호출되는 이벤트 핸들러이다.
매개 변수
sender : 이벤트를 발생시킨 객체
args : 비동기 작업에 대한 정보와 결과를 포함하는 SocketAsyncEventArgs객체
void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
if(args.SocketError == SocketError.Success)
{
//todo
_onAcceptHandler.Invoke(args.AcceptSocket);
}
else
Console.WriteLine(args.SocketError.ToString());
// 다음 연결을 요청
RegisterAccept(args);
}
args.SocketError를 확인하여 비동기 작업의 완료여부를 확인한다.
작업이 성공적으로 완료된 경우
args.SocketError가 SocketError.Success인 경우 _onAcceptHandler.Invoke(args.AcceptSocket)를 호출하여 클라이언트와의 통신을 위한 작업이 수행된다.
_onAcceptHandler
_onAcceptHandler는 클라이언트 연결을 처리하기 위한 핸들러이다.
Action<Socket> _onAcceptHandler;
public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
{
_onAcceptHandler += onAcceptHandler;
}
4. 재사용하는 하는 경우
SocketAsyncEventArgs는 재사용 가능한 객체이다. Accept작업이 완료되고 클라이언트와의 통신이 종료된 후에 해당 객체를 다시 사용하기 위해서는 이전에 연결된 클라이언트 소켓에 대한 참조를 제거해야한다.
void RegisterAccept(SocketAsyncEventArgs args)
{
// 재사용할을 위한 참조 제거
args.AcceptSocket = null;
bool pending = _listenSocket.AcceptAsync(args);
if (pending == false)
OnAceeptCompleted(null,args);
}
class Program
{
static Listener _listener = new Listener();
static void onAcceptHandler(Socket clientSocket)
{
try
{
// 생략
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
static void Main(string[] args)
{
// 생략
_listener.Init(endPoint,onAcceptHandler);
Console.WriteLine("Listening...");
while (true)
{
;
}
}
}
낚시로 간단 예시
01 낚시대를 처음 던진다.
public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
{
RegisterAccept(args);
}
02 낚시대를 던지자마자 생선이 물음( pending == false )
if (pending == false)
OnAcceptCompleted(null,args);
03 낚시대를 던지고 입질을 기다림 ( pending == true )
만약 물고기가 잡히면 작업이 완료되었다는 뜻이니까 args.Completed가 호출된다.
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
04 낚시대의 고기를 뺌
if(args.SocketError == SocketError.Success)
{
_onAcceptHandler.Invoke(args.AcceptSocket);
}
else
Console.WriteLine(args.SocketError.ToString());
05 낚시대를 다시 던짐
void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
RegisterAccept(args)
}
non - blocking code에 대해 더 알아보기
참고 : 본 내용은 MMORPG PART4 강의를 수강하여 작성하였습니다.
https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4
'cs공부 > 네트워크프로그래밍' 카테고리의 다른 글
네트워크 프로그래밍 - non-blocking code에 대해 더 알아보기 (0) | 2023.07.03 |
---|---|
네트워크프로그래밍 - blocking(send등), non-blocking(BeiginSend,SocketAsyncEventArgs등) (0) | 2023.06.21 |
네트워크 프로그래밍 - C# 간단한 blocking 사용한 Server 소켓프로그래밍 구현 (0) | 2023.06.19 |
네트워크 프로그래밍 - C# 간단한 Client 소켓프로그래밍 구현 (0) | 2023.06.19 |
네트워크프로그래밍 - 소켓 프로그래밍(클라이언트 관점, 서버관점) 과정 (0) | 2023.06.19 |
댓글