소켓 프로그래밍 입문

범죄 영화에 나오는 비밀 식당이 있다고 생각해보자.

비밀 식당에는 아무나 입장할 수 없고 위치도 숨겨져 있다. 고객은 고객의 대리인(아바타)을 통해서만 비밀 식당에 입장이 가능하다. 비밀 식당의 관리자는 문지기를 교육하여 식당의 출입을 담당하게 한다.

식당은 대리인에게 식당에서 준비한 핸드폰을 제공하여 고객과 연락을 가능하게 한다. 고객은 문지기에게 연락하여 식당에서 제공한 핸드폰 번호를 알아내어 대리인과 연락할 수 있다.

위와 같은 일련의 과정이 바로 소켓 프로그래밍이다.

비교하자면, 핸드폰은 소켓, 고객은 클라이언트, 대리인은 세션, 연락(통화)은 패킷 송수신, 식당 관리자는 서버, 문지기는 Listener, 문지기 교육은 Bind, 식당 개시는 Listen, 출입 허용은 Accept 이다.

이러한 소켓 프로그래밍은 C#을 이용해 로우 레벨로 구현해보자.


먼저 ServerCore 프로젝트를 만들어서 식당을 구현해보자.
주석에 코드의 설명이 달려있다.

    class Program
    {
        static void Main(string[] args)
        {
            //DNS(Domain Name System) 
            string host = Dns.GetHostName(); // DESKTOP-S7EG95G
            IPHostEntry ipHost = Dns.GetHostEntry(host); // System.Net.IPHostEntry
            IPAddress ipAddr = ipHost.AddressList[0];  // fe80::b599:93ca:869a:5736%18
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777); // 주소와 포트번호


            try // 에러가 날 위험이있다. 
            {
                // 문지기 == 문지기가 들고 있는 휴대폰(소켓)
                Socket listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                // 첫 번째 매개변수: IP 버전4 인지 버전6인지 
                // 그 다음 매개변수: TCP를 쓸건지 UDP를 쓸건지 

                // 문지기 교육 
                listenSocket.Bind(endPoint); // 식당주소와 비밀식당으로 가는 문을 문지기에게 교육 
                listenSocket.Listen(10); // 영업 개시 / 매개변수는 손님의 대기수

                while (true) // 식당(서버)은 계속 영업해야 하므로 무한루프 
                {
                    Console.WriteLine("Listening...");

                    // 손님 입장 허용 
                    Socket clientSocket = listenSocket.Accept(); // 반환값이 Socket임. 반환하는 Socket이 대리인에게 주는 핸드폰이다.

                    // 연락을 받는다. 
                    byte[] recvBuff = new byte[1024];
                    int recvBytesLength = clientSocket.Receive(recvBuff);
                    string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytesLength); // 문자열을 받는다고 가정 
                    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());
            }

        }

다음은 DummyClient 프로젝트에서 손님을 구현해보자.

    class Program
    {
        static void Main(string[] args)
        {
            //DNS(Domain Name System) 
            string host = Dns.GetHostName(); // DESKTOP-S7EG95G
            IPHostEntry ipHost = Dns.GetHostEntry(host); // System.Net.IPHostEntry
            IPAddress ipAddr = ipHost.AddressList[0];  // fe80::b599:93ca:869a:5736%18
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777); // 주소와 포트번호

            try
            {
                //휴대폰 설정 == 소켓 생성
                Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                // 문지기한테 입장 문의 
                socket.Connect(endPoint); // Blocking 함수라 통신이 없으면 함수가 빠져나오지 못함. 
                Console.WriteLine($"Connected to {socket.RemoteEndPoint}");

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

                // 연락을 받는다. 
                byte[] recvBuff = new byte[1024];
                int recvBytes = socket.Receive(recvBuff);
                string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
                Console.WriteLine($"[From Server] {recvData}");

                // 식당을 나간다. 
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            } 
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
           

솔루션의 [속성]을 변경하여 속성을 변경하여 DummyClient 프로젝트와 ServerCore 프로젝트를 동시에 시작하게 하자.

ServerCore 프로젝트의 출력창

DummyClient 프로젝트의 출력창


다음 내용은 Rookiss님의 답변이 인상적이어서 가져왔다.

Q.
카카오톡과 같은 앱을 이용해서 다른 사람들과 네트워크를 이용해서 통신하는 것들이 가능한 이유가 카카오톡 앱에서소켓 프로그래밍을 활용했기 때문인가요 ??? 즉, 네트워크를 통해서 통신하는 모든 프로그램이 소켓 프로그래밍을 활용한 것인가요 ???


A.
반은 맞고 반은 아닙니다.네트워크 통신은 기본적으로 응용 프로그램에서 멋대로 할 수는 없고 커널 (운영체제)한테 요청을 해서 간접적으로 할 수 있는데,그렇게 운영체제한테 통신을 받고 보내달라고 부탁을 하는 것이 결국 소켓 프로그래밍 입니다.그러니 네트워크 통해 통신하는 모든 프로그램이'언젠가'는 소켓을 활용한 것은 맞습니다.

그렇다고 앱 개발자들이 직접 소켓 프로그래밍을 꼭 해야 하는 것은 아니고 특수한 상황이 아니라면 오히려 그럴 확률은 적습니다.
예를 들어 NodeJS 같은 웹 프레임워크를 사용하면 로우레벨 단계에서 일일히 연결하고, 패킷을 보내준 다음, 연결을 끊는 등의 작업을
숨겨놓고 우린 함수 호출 하나로 편리하게 모든 작업을 할 수 있게 해주기 때문입니다. 그렇게 로우레벨 기능들을 조립해서 만든게 [프레임워크]라는 것이고 웹 통신에 맞게 만들어진게 [웹 프레임워크]입니다. 참고로 웹은 딱 한번 요청을 보내고/받은 다음, 연결을 끊어주는 TCP 통신이라고 보시면 됩니다. 반면 온라인 게임처럼 계속 연결을 유지해서 지속적으로 데이터를 교환이 필요한 경우 직접 소켓 프로그래밍을 이용해 우리한테 맞게 만들어줘야 합니다.

좋은 웹페이지 즐겨찾기