2022년 3월 11일 금요일

[c# 간단한 비동기 TCP]


비동기

더미 클라이언트

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

/// <summary>
/// 유니티 대신해서 테스트 하기위한 클라이언트
/// 테스팅 환경 구축용
/// </summary>
///
namespace DummyClient
{
    class Program
    {
        static void Main(string[] args)
        {

            /// DNS (Domain Name System)
            /// 172.1.2.3(실제로 넣으면 문제가됨!) 하드코딩 X!
            /// 하지만 도메인 등록이라면 문제가 없다
            /// www.rookiss.com -> 123.123.123.12
            string host = Dns.GetHostName();
            IPHostEntry ipHost = Dns.GetHostEntry(host);
            IPAddress ipAddr = ipHost.AddressList[0];
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);



            while (true)
            {
                ///휴대폰 설정
                Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);


                try
                {
                    ///문지기한테 입장 문의
                    socket.Connect(endPoint);
                    Console.WriteLine($"Connected To{socket.RemoteEndPoint.ToString()}");


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



                    ///받는다
                    byte[] recvBuff = new byte[1024];
                    int recvBytes = socket.Receive(recvBuff);

                    string receiveData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);

                    Console.WriteLine($"From server{receiveData }");

                    ///나간다
                    socket.Shutdown(SocketShutdown.Both);
                    socket.Close();

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

                Thread.Sleep(100);
            }

        }
    }
}






비동기 서버 Listener

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

namespace ServerCore
{
    class Listener
    {
        Socket _listenSocket;

        /// Accept가 완료되면 어떻게 처리할건지
        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);

            ///영업 시작
            ///backlog : 최대 대기수
            /// 문지기가 안내하기 전까지 대기하는 수
            /// 만약 이 수가 넘어가는 사람이 문의하면 바로 fail이 뜬다
            _listenSocket.Listen(10);

            /// 한번만 만들어주면 계속 재활용 할수있는 어마어마한 장점이 있음
            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);///나중에라도 완료되면 여기 이벤트로 연락오게하기!
            RegisterAccept(args);///최초로 낚시대를 강물에 던짐!
        }








        void RegisterAccept(SocketAsyncEventArgs args)
        {

            ///중요!
            ///다시 돌아올때 비어있는 args여야한다!!!
            args.AcceptSocket = null;


            /// 비동기! 동시에 처리안되고 나중에 처리될수있음!!!
            ///성공하든 아니든 상관없이 바로 리턴을 하고 본다(문제가 될수있음)
            ///로그인에 실패했는데 리턴때릴수있음
            ///비동기 계열을 사용하면 대화를 해야함(결과에 대해서)
            ///

            ///당장 완료한다는 보장은 없고 요청을 하긴 할꺼다
            ///말그대로 등록을 한거라고 보면됨
            bool pending = _listenSocket.AcceptAsync(args);
            if (pending == false)
            {
                ///비동기로 실행했지만 운좋게 완료된 케이스
                ///낚시대를 강물에 던지자마자 물고기가 잡힌 케이스
                OnAcceptCompleted(null,args);
            }
            else
            {
                ///pending true
                /// 나중에 args로 Completed 로 연락이 올것이다.
            }

        }

        void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
        {
            if (args.SocketError == SocketError.Success)
            {
                ///에러 없이 잘 처리 됫다(실제로 accept를 했다)
                /// 유저가 커넥트 연결이 와서 실제로 accept까지 했다면
                ///TODO
                _onAcceptHandler.Invoke(args.AcceptSocket);///완료됬으면 _onAccepHandler 실행!

            }
            else
            {
                Console.WriteLine(  args.SocketError.ToString());
            }


            ///모두 완료됬으니 다음 아이를 위해서 다시 등록!!
            ///다음 낚시대 던지기
            RegisterAccept(args);
        }

    }
}


비동기 서버 Main


using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;



/// <summary>
/// 소켓 프로그래밍 기초
///
/// 메모장 코드를 메인안에 때려박아넣으면 안됨!
///어짜피 커질꺼니까 처음부터 분리하는 습관을 들여야함!
///
///
/// </summary>

namespace ServerCore
{
    class Program
    {
        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 MMORPG SERVER!");
                clientSocket.Send(sendBuff);

                ///쫒아낸다
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
                /// 받는다
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

           
        }


        static void Main(string[] args)
        {
            /// DNS (Domain Name System)
            /// 172.1.2.3(실제로 넣으면 문제가됨!) 하드코딩 X!
            /// 하지만 도메인 등록이라면 문제가 없다
            /// www.rookiss.com -> 123.123.123.12
            string host = Dns.GetHostName();
            IPHostEntry ipHost = Dns.GetHostEntry(host);
            IPAddress ipAddr =  ipHost.AddressList[0];
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);


            ///혹시나 누가 들어오면 OnAcceptHandler에 전달해줘
            _listener.Init(endPoint, OnAcceptHandler);
            Console.WriteLine("Listening...");

            while (true)
            {

            }
        }
    }
}




 




댓글 없음:

댓글 쓰기

git rejected error(feat. cherry-pick)

 문제 아무 생각 없이 pull을 받지않고 로컬에서 작업! 커밋, 푸시 진행을 해버렷다. push에선 remote와 다르니 당연히 pull을 진행해라고 하지만 로컬에서 작업한 내용을 백업하지 않고 진행하기에는 부담스럽다(로컬작업 유실 가능성) 해결하려...