서버/서버 이론

Connector 구현

잼잼재미 2025. 4. 20. 01:31

Connector  구현


Session을 수정했던 것 처럼, Connect 함수 부분도 블로킹 방식에서 비동기 함수로 수정해줘야 한다. ServerCore에 Connector 클래스를 추가하자. Listener을 구현할 때와 유사한 구조로 진행한다.

 

 

 

Server에 Connector가 필요한 이유?

Server는 대기 상태에서 Client의 연결을 기다리는 역할을 하는데 왜 Connector가 필요할까? Server와 Client의 연결 뿐만 아니라, Server와 Server 끼리 통신을 할 때도, 한쪽은 Listener, 다른 한쪽은 Connector를 사용해서 통신한다.

 

 

ServerCore 셋팅

이제 ServerCore에 추가했던 class들은 모두 Client에서 사용할 수 있어야 한다. 그렇다고 DummyClient 부분에도 똑같은 class들을 복사해서 추가하는 것은 비효율적이다. 그래서 다음과 같이 셋팅하자.

 

 

 

출력 유형을 콘솔 애플리케이션에서 클래스 라이브러리로 변경한다. 이제 라이브러리 형식으로만 사용되기 때문에 독립적으로 실행할 수 없다. 

 

 

 

마찬가지로 DummyClient에서도 똑같이 ServerCore를 참조해준다.

 

 

 

그리고 ServerCore Program에 있는 내용을 모두 복사해서 Server Program으로 이동한다. using도 모두 복사해주고, using ServerCore; 를 추가해준다. 그래도 에러가 뜬다면 ServerCore에서 구현했던 class들 (Connector, Listener, Session)을 private으로 설정되어 있기 때문이다. public으로 수정한다.

 

 

 

ServerCore의 Program은 더 이상 사용하지 않기 때문에 삭제 해주자.

 

 

 

다음과 같이 여러 시작 프로젝트 설정을 다시 해준다.

 

 

코드 작성 및 수정


Connector (ServerCore)

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ServerCore
{
    class Connector
    {
        Func<Session> _sessionFactory; // Session을 어떤 방식으로, 어떤 Session 만들 것인지

        public void Connect(IPEndPoint endPoint, Func<Session> sessionFactory)
        {
            // 휴대폰 설정
            Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _sessionFactory = sessionFactory; 

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.Completed += OnConnectCompleted;
            args.RemoteEndPoint = endPoint; // 연결하기 위해 상대방의 주소를 넘겨준다.
            args.UserToken = socket; // UserToken을 통해 원하는 정보를 넘겨줄 수 있다.
            // socket을 전역변수로 선언하는 것이 아닌 UserToken을 통해 정보를 넘겨주는 이유는
            // Listener 가 계속해서 뺑뺑이를 돌며 여러명을 받을 수 있듯이
            // Connector 역시 여러명을 받을 수 있기 때문이다.

            RegisterConnect(args);
        }

        void RegisterConnect(SocketAsyncEventArgs args)
        {
            Socket socket = args.UserToken as Socket;
            if (socket == null)
                return;

            bool pending = socket.ConnectAsync(args);
            if (pending == false)
                OnConnectCompleted(null, args);
        }

        void OnConnectCompleted(object sender, SocketAsyncEventArgs args)
        {
            if (args.SocketError == SocketError.Success)
            {
                Session session = _sessionFactory.Invoke(); // _sessionFactory 를 통한 Session 생성 (Content 딴에서 요구한 방식대로 Session 생성)
                session.Start(args.ConnectSocket);
                session.OnConnected(args.RemoteEndPoint);
            }
            else
            {
                Console.WriteLine($"OnConnectCompleted Fail: {args.SocketError}");
            }
        }
    }
}​

 

 

Program (DummyClient)

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using ServerCore; // Servercore 라이브러리 참조

namespace DummyClient
{
    // GameSession Class 추가
    class GameSession : Session
    {
        public override void OnConnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnConnected : {endPoint}");

            // 보낸다
            byte[] sendBuff = Encoding.UTF8.GetBytes("Hello World");
            Send(sendBuff);
        }

        public override void OnDisconnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnDisconnected : {endPoint}");
        }

        public override void OnRecv(ArraySegment<byte> buffer)
        {
            string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
            Console.WriteLine($"[From Server] {recvData}");
        }

        public override void OnSend(int numOfBytes)
        {
            Console.WriteLine($"Transferred bytes : {numOfBytes}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string host = Dns.GetHostName();
            IPHostEntry ipHost = Dns.GetHostEntry(host);
            IPAddress ipAddr = ipHost.AddressList[0];
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);

            // Connector Class 사용
            Connector connector = new Connector();
            connector.Connect(endPoint, () => { return new GameSession(); });

            try
            {
                
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}​

 

'서버 > 서버 이론' 카테고리의 다른 글

RecvBuffer 개선  (0) 2025.04.24
TCP vs UDP  (0) 2025.04.20
Session 구현  (0) 2025.04.13
Listener 구현  (0) 2025.04.12
소켓 프로그래밍 기초  (0) 2025.04.09