C#/네트워크 관련

Socket 연결 실습 - 2

tmd1 2024. 10. 1. 22:21
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ServerCore
{
    internal class Program
    {

        static Listener _listener = new Listener();
        static void OnAcceptHandler(Socket clientSocket)
        {
            // 받는다
            byte[] recvBuff = new byte[1024];
            int recvBytes = clientSocket.Receive(recvBuff);
            string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
            Console.WriteLine($"[From Data] {recvData}");

            // 보낸다
            byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to MMORPG Server !");
            clientSocket.Send(sendBuff);

            // 끝내기
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
        }
        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)
            {
    
            }


        }
    }
}

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ServerCore
{
    internal class Listener
    {
        Socket _listenSocket;
        Action<Socket> _onAceeptHandler;

        public void init(IPEndPoint endPoint, Action<Socket> onAceeptHandler)
        {
            _listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _onAceeptHandler += onAceeptHandler;

            // 문지기 교육
            _listenSocket.Bind(endPoint);

            // 영업 시작
            // Backlog : 최대 대기 수
            _listenSocket.Listen(10);

           

            // callback 방식으로 동작시키기 위해 SocketAsyncEventArgs 객체 생성
            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            
             // args.Completed 이벤트에 OnAccpetComplete 메서드 등록
             // args.Completed는 EventHandler라는 delegate의 이벤트로
             // EventHandler는 object와 SocketAysncEventArgs를 매개변수로 받음
             // 따라서 똑같은 매개변수를 갖는 OnAccpetComplete 메서드를 등록 가능
             
             // 만약 비동기 Accpet가 완료된다면 신호를 보내 OnAccpetComplete 함수가
             // 실행되게 됨.
            args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAccpetComplete);
            
            // 직접 등록해줘서 펜딩이 일어나지 않는다면 if문에서 처리될거고
            // 펜딩이 일어난다면 바로 위에서 콜백방식으로 실행됨
            RegisterAccept(args);
        }

        void RegisterAccept(SocketAsyncEventArgs args)
        {
            // 재사용하기때문에 이전것이 남아있으면 안되므로 null로 초기화
            args.AcceptSocket = null;

            bool pending = _listenSocket.AcceptAsync(args);

            // 빨리 처리되어 펜딩이 일어나지 않는다면
            // 펜딩이 일어난다면 callback 방식으로 if문 내 구문이 실행되게됨
            if (pending == false)
                OnAccpetComplete(null, args);

        }

        void OnAccpetComplete(object obj, SocketAsyncEventArgs args)
        {

            // 에러없이 성공 되었다면
            if(args.SocketError == SocketError.Success)
            {
                // 클라이언트 소켓을 전달받는 부분과 동치
                // _OnAceeptHandler에 등록된 함수가 args.AcceptSocket을
                // 매개변수로 받아 실행된다.
                // 이떄 args.AccetSocket은 동기 연결 방식에서 Accpet()함수로
                // 만들어졌던 클라이언트소켓과 동일함
                _onAceeptHandler.Invoke(args.AcceptSocket);
            }
            else
                Console.WriteLine(args.SocketError.ToString());


            // 다시 연결을 비동기로 받아줌 -> 연결 반복
            RegisterAccept(args);

        }


    }
}