NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# 1부 - C# ZeroMQ를 이용한 1:1 채팅 프로그램 만들기. (1:1 chatting)

    페이지 정보

    본문

    안녕하세요. 소심비형입니다. ZeroMQ를 이용하여 다중 접속 채팅 프로그램을 만들어 보도록 하겠습니다. 몇회에 걸쳐서 진행될지는 모르겠지만, 최대한 빨리 마무리 하는 방향으로 진행하겠습니다. 채팅 관련해서 조금만 검색해 보시면 많은 자료들을 찾을 수 있습니다. 일반적으로 많이 검색되는 C#의 Socket을 이용하지 않고 ZeroMQ를 사용하여 메시지를 처리합니다. 이번 글에서는 간단하게 ZeroMQ를 설치하고 1:1 채팅이 가능한 클라이언트 프로그램을 만듭니다. 일반적인 Server / Client 시나리오는 아니죠^^;

    ※ 소스는 첨부 파일을 다운로드 받으세요.

     

    Visual Studio를 실행한 후 새로운 콘솔 프로젝트를 추가합니다. 간단한 테스트 프로젝트이므로 이름은 아무렇게나 지정해도 상관 없습니다^^; 저는 ChatClientForZeroMQ로 했습니다.

    4kvIYoG.png

     

     

    콘솔 프로젝트가 생성되었다면, 패키지 관리자 콘솔에서 아래 명령어를 통해 ZeroMQ를 설치합니다.

    PM> Install-Package ZeroMQ

    qMNDzvZ.png

     

     

    정상적으로 설치가 완료되면, 솔루션 탐색기에 아래와 같이 ZeroMQ관련 라이브러리가 추가됩니다.

    I6cLwZU.png

     

     

    자신의 CPU 제조사가 Intel이라면 i386의 라이브러리를, AMD라면 amd64에 있는 라이브러리를 사용해야 합니다. 그리고, 라이브러리 폴더에 있는 so확장자는 리눅스용입니다. 아시다시피 동적 라이브러리인 dll은 윈도우에서 사용되죠. 지금은 윈도우 환경이기 때문에 libsodium.dll과 libzmq.dll을 사용하면 되겠습니다. 중요한 부분은 아니므로 바로 코딩으로 넘어가겠습니다.

     

    먼저 참조와 using을 추가해야 합니다. ZeroMQ를 설치하면서 관련 라이브러리는 자동으로 참조에 추가되어 있을겁니다.

    zwU0ZHU.png

     

     

    using을 2개 추가합니다.

    Program.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using ZeroMQ;

     

     

    일반적인 채팅 프로그램의 모습(Server와 Client가 독립적으로 존재하는...)은 아니지만, 아주 간단하게 ZeroMQ를 이용한 1:1 채팅을 구현하기 위해 Receiver와 Sender를 동시에 구현해야 합니다. 리시버는 메시지를 받기 위한 주소이며, 센더는 메시지를 보낼 상대방의 주소입니다. 사용자로부터 입력받은 값을 저장하기 위한 속성을 추가합니다.

    public static string ReceiverAddress { get; set; }
    public static string SenderAddress { get; set; }
    public static string UserName { get; set; }

     

     

    아래는 Receiver의 코드입니다.

    private void Receiver()
    {
        using (var context = ZContext.Create())
        using (var socket = new ZSocket(context, ZSocketType.PAIR))
        {
            socket.Bind(string.Format("tcp://{0}", ReceiverAddress));
            while (true)
            {
                List<ZFrame> frames = socket.ReceiveFrames(2).ToList();
                byte[] bytes = new byte[frames[0].ReadInt32()];
                frames[1].Read(bytes, 0, bytes.Length);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine(Encoding.UTF8.GetString(bytes));
                Console.ForegroundColor = ConsoleColor.White;
            }
        }
    }
    

     

     

    아래는 Sender의 코드입니다.

    private void Sender()
    {
        using (var context = ZContext.Create())
        using (var socket = new ZSocket(context, ZSocketType.PAIR))
        {
            socket.Connect(string.Format("tcp://{0}", SenderAddress));
            while (true)
            {
                byte[] bytes = Encoding.UTF8.GetBytes(string.Format("{0}:{1}", UserName, Console.ReadLine()));
                socket.SendFrames(new List<ZFrame>() { new ZFrame(bytes.Length), new ZFrame(bytes, 0, bytes.Length) });
            }
        }
    }
    

     

     

    메시지를 주고 받기 위해 대기해야 하는 코드를 작성합니다.

    static void Main(string[] args)
    {
        Console.Write("User Name:");
        UserName = Console.ReadLine();
        Console.Write("ReceiverAddress:");
        ReceiverAddress = Console.ReadLine();
        Console.Write("SenderAddress:");
        SenderAddress = Console.ReadLine();
        Thread receiverThread = new Thread(new ThreadStart(new Program().Receiver));
        receiverThread.Start();
        Thread senderThread = new Thread(new ThreadStart(new Program().Sender));
        senderThread.Start();
    }
    

     

     

    간단하게 구현된 1:1 채팅을 테스트해봅니다. 솔루션 빌드(Ctrl + Shift + B)를 실행하세요. 빌드가 완료 되었으면 프로젝트에서 우클릭 후 "파일 탐색기에서 폴더 열기"를 선택하세요.

    bk4DNTn.png

     

     

    윈도우 탐색기에서 bin > Debug폴더로 이동하세요. "ChatClientForZeroMQ.exe" 를 실행합니다. ReceiverAddress에 상대방이 보낸 메시지를 받을 내 PC의 아이피와 포트 번호를 입력합니다. 로컬에서 실행중이므로 127.0.0.1:2001로 설정합니다. 그리고, SenderAddress는 상대방에게 보낼 메시지이므로 상대방의 아이피와 포트를 입력합니다. Sender도 로컬에서 실행중이므로 localhost:2002를 입력합니다.

    tGvJ1xk.jpg

     

     

    실행 해보면 아래와 같이 에러가 발생됩니다. 내용은 ZeroMQ를 초기화할 때 필요한 파일이 없기 때문에 발생하고 있습니다. 이 문제를 해결하기 위해서는 위에서 언급한 2개의 동적 라이브러리를 배포되는 폴더에 넣어줘야 합니다. 다음에 만들 서버/클라이언트에서는 빌드할 때 자동 배포 되도록 구성할 예정입니다. 여기에서는 "libsodium.dll", "libzmq.dll" 파일을 Debug폴더로 복사합니다. 이 때 자신의 CPU에 따라 i386 또는 amd64에 있는 라이브러리를 복사하면 됩니다.

    Cb3BVl3.png

     

     

    복사한 후 다시 "ChatClientForZeroMQ.exe"를 실행합니다.

    rmHMRsB.png

     

     

    소심 사용자의 정보입니다. 로컬에서 테스트중이기 때문에 포트번호로 구분해야 합니다.

    Nb8BEYz.png

     

     

    비형 사용자의 정보입니다.

    R3wbQVJ.png

     

     

    소심님과 비형님의 대화입니다. 잘 동작하고 있군요.

    BPErjwf.png

     

     

    아래는 이 프로그램의 전체 소스입니다.

    Program.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using ZeroMQ;
     
    namespace ChatClientForZeroMQ
    {
        class Program
        {
            public static string ReceiverAddress { get; set; }
            public static string SenderAddress { get; set; }
            public static string UserName { get; set; }
     
            static void Main(string[] args)
            {
                Console.Write("User Name:");
                UserName = Console.ReadLine();
                Console.Write("ReceiverAddress:");
                ReceiverAddress = Console.ReadLine();
                Console.Write("SenderAddress:");
                SenderAddress = Console.ReadLine();
                Thread receiverThread = new Thread(new ThreadStart(new Program().Receiver));
                receiverThread.Start();
                Thread senderThread = new Thread(new ThreadStart(new Program().Sender));
                senderThread.Start();
            }
     
            private void Receiver()
            {
                using (var context = ZContext.Create())
                using (var socket = new ZSocket(context, ZSocketType.PAIR))
                {
                    socket.Bind(string.Format("tcp://{0}", ReceiverAddress));
                    while (true)
                    {
                        List<ZFrame> frames = socket.ReceiveFrames(2).ToList();
                        byte[] bytes = new byte[frames[0].ReadInt32()];
                        frames[1].Read(bytes, 0, bytes.Length);
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine(Encoding.UTF8.GetString(bytes));
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                }
            }
     
            private void Sender()
            {
                using (var context = ZContext.Create())
                using (var socket = new ZSocket(context, ZSocketType.PAIR))
                {
                    socket.Connect(string.Format("tcp://{0}", SenderAddress));
                    while (true)
                    {
                        byte[] bytes = Encoding.UTF8.GetBytes(string.Format("{0}:{1}", UserName, Console.ReadLine()));
                        socket.SendFrames(new List<ZFrame>() { new ZFrame(bytes.Length), new ZFrame(bytes, 0, bytes.Length) });
                    }
                }
            }
        }
    }
    

     

     

    간단하게 ZeroMQ를 이용한 1:1 채팅을 만들어 보았습니다. .NET의 Socket을 이용하는 것보다 쉽고 빠르게 개발할 수 있죠? 사실 ZeroMQ와 같은 라이브러리들은 많이 있습니다. 완전히 새로운 개념은 아니기 때문에, 이미 ActiveMQ나 TibRV등등을 접해보신 분들은 쉽고 빠르게 적응할 수 있죠. 이런 라이브러리들을 사용할수록 원시적인 TCP나 UDP 소캣을 사용해서 개발하는게 의미 없게 느껴지기도 합니다. 쉬운 길을 두고 몰라서 어려운 길을 택하지 않았으면 좋겠군요. 그래서 개발자들은 새로운 기술에 항상 관심을 가지고 가볍게라도 접해보는게 좋습니다.

    QqsDiK2.gif

     

     

    ※ 좀 더 쉽게 메시지를 주고 받을 수 있지만, 아직 한글을 전송할 때 정상적으로 Receiver에서 받지 못하는 문제가 있습니다. 따라서, ZFrame 배열을 통해 바이트와 바이트의 길이를 넘겨주고 처리하도록 되어 있습니다. 만약, 영문이나 숫자만 통신하는 시나리오라면 굳이 바이트로 보내지 않아도 상관은 없습니다.

    이어서 Server / Client 시나리오를 통한 다중 채팅을 진행하도록 하겠습니다.

    다음 시간에...

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    첨부파일

    댓글목록

    profile_image

    쏜프로님의 댓글

    no_profile 쏜프로 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일 Date

    0MQ 알아보다가 여기까지 왔네요~