원격 제어 프로그램 13. 원격 제어 이벤트 수신 서버

원격 제어 컨트롤러에서 제어하기 위해 키보드와 마우스 이벤트를 원격 제어 호스트에서 전송합니다. 이를 위해 원격 제어 호스트에서는 이러한 키보드, 마우스 이벤트를 수신하기 위한 서버가 필요합니다.

이제 원격 제어 이벤트 수신 서버를 표현할 RecvEventServer 클래스를 정의합시다.

public class RecvEventServer
{

서버의 Listen 소켓을 멤버로 선언합니다.

    Socket lis_sock;

메시지를 수신하였을 때 처리를 위해 이벤트를 멤버로 선언합니다.

    public event RecvKMEEventHandler RecvedKMEvent = null;

클라이언트의 연결 요청과 수용하는 작업을 처리할 스레드를 멤버로 선언합시다.

    Thread th;

생성자 메서드는 로컬 IP와 포트를 입력 인자로 받습니다.

    public RecvEventServer(string ip, int port)
    {

소켓을 생성하세요.

        //소켓 생성
        lis_sock = new Socket(AddressFamily.InterNetwork, 
                              SocketType.Stream, 
                              ProtocolType.Tcp);

소켓과 IP 단말 개체를 결합하고 백 로그 큐 크기를 설정하세요.

        IPAddress ipaddr = IPAddress.Parse(ip);
        IPEndPoint ep = new IPEndPoint(ipaddr, port);
        lis_sock.Bind(ep);//소켓과 IP 단말 개체 결합
        lis_sock.Listen(5); //Back 로그 큐 크기 설정

연결 요청을 수락하는 쓰레드를 가동하세요.

        //연결 요청 수락 쓰레드 시작
        ThreadStart ts = new ThreadStart(AcceptLoop);
        th = new Thread(ts);
        th.Start();
    }

메시지를 수신하는 부분을 비동기로 처리합시다. 이를 위해 대리자를 정의하세요.

    //비동기로 수신하기 위한 대리자 정의
    public delegate void ReceiveDele(Socket dosock);

연결 요청을 수락하는 작업을 수행하는 AcceptLoop 메서드를 구현합시다.

    void AcceptLoop()
    {
        Socket do_sock;

메시지를 수신 대리자 개체를 생성하세요.

        ReceiveDele rld = new ReceiveDele(Receive);//수신 대리자 개체 생성
        try
        {
            while (true)
            {

연결 요청을 수락하여 메시지를 수신할 작업 소켓을 구합니다.

                do_sock = lis_sock.Accept();

수신 대리자 개체의 BeginInvoke 호출로 메시지 수신을 비동기로 처리하세요.

                rld.BeginInvoke(do_sock, null, null);//비동기로 수신
            }
        }
        catch
        {

예외가 발생하면 닫습니다. 상품화 수준으로 작성하시려면 스레드로 작성하는 부분을 비동기 방식으로 변경하고 다양한 테스트 조건에서 발생하는 버그들을 수정하셔야 합니다.

                Close();
        }
    }

메시지를 수신하는 메서드를 구현합시다.

    void Receive(Socket dosock)
    {

먼저 크기가 9인 버퍼를 생성하여 메시지를 수신합니다. 클라이언트 측에서는 전송할 메시지 종류에 관계없이 고정길이 버퍼에 데이터를 설정하여 보내게 구현하였습니다.

        byte[] buffer = new byte[9];//수신할 버퍼 생성

메시지를 수신하세요.

        int n = dosock.Receive(buffer);//메시지 수신

수신 이벤트를 구독 요청한 개체가 있다면 이벤트 인자를 생성하여 수신 이벤트를 게시하세요.

        if (RecvedKMEvent != null)//수신 이벤트 구독자가 있을 때
        {
            RecvKMEEventArgs e =new RecvKMEEventArgs(new Meta(buffer));//이벤트 인자 생성
            RecvedKMEvent(this, e);//수신 이벤트 통보(게시)
        }

작업이 끝났으니 작업 소켓을 닫습니다.

        dosock.Close();//소켓 닫기
    }

메시지 수신 서버를 닫는 Close 메서드를 작성합시다.

    public void Close()
    {

중복해서 닫지 않게 처리하기 위해 lis_sock 개체가 존재하는지 확인하세요.

        if (lis_sock != null)
        {

존재하면 소켓을 닫고 null로 지정하여 다시 Close를 호출하여도 아무 작업을 하지 않게 합니다.

            lis_sock.Close();//listen 소켓 닫기
            lis_sock = null;
        }
    }
}

다음은 이번 실습에서 작성한 소스 코드입니다.

//RecvEventServer.cs
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace 원격제어기
{
    /// <summary>
    /// 원격 제어 이벤트 수신 서버
    /// </summary>
    public class RecvEventServer
    {
        Socket lis_sock;
        /// <summary>
        /// 원격 제어 이벤트 수신하였음을 알리는 이벤트
        /// </summary>
        public event RecvKMEEventHandler RecvedKMEvent = null;
        Thread th;
        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="ip">로컬 IP</param>
        /// <param name="port">포트</param>
        public RecvEventServer(string ip, int port)
        {
            //소켓 생성
            lis_sock = new Socket(AddressFamily.InterNetwork, 
                                  SocketType.Stream, 
                                  ProtocolType.Tcp);            
            IPAddress ipaddr = IPAddress.Parse(ip);
            IPEndPoint ep = new IPEndPoint(ipaddr, port);
            lis_sock.Bind(ep);//소켓과 IP 단말 개체 결합
            lis_sock.Listen(5); //Back 로그 큐 크기 설정
            //연결 요청 수락 쓰레드 시작
            ThreadStart ts = new ThreadStart(AcceptLoop);
            th = new Thread(ts);
            th.Start();
        }

        /// <summary>
        /// 비동기로 수신하기 위한 대리자
        /// </summary>
        /// <param name="dosock"></param>
        public delegate void ReceiveDele(Socket dosock);
        void AcceptLoop()
        {
            Socket do_sock;
            ReceiveDele rld = new ReceiveDele(Receive);//수신 대리자 개체 생성
            try
            {
                while (true)
                {
                    do_sock = lis_sock.Accept();
                    rld.BeginInvoke(do_sock, null, null);//비동기로 수신
                }
            }
            catch
            {
                Close();
            }
        }
        void Receive(Socket dosock)
        {
            byte[] buffer = new byte[9];//수신할 버퍼 생성
            int n = dosock.Receive(buffer);//메시지 수신
            if (RecvedKMEvent != null)//수신 이벤트 구독자가 있을 때
            {
                //이벤트 인자 생성
                RecvKMEEventArgs e =new RecvKMEEventArgs(new Meta(buffer));
                RecvedKMEvent(this, e);//수신 이벤트 통보(게시)
            }
            dosock.Close();//소켓 닫기
        }
        /// <summary>
        /// 원격 제어 이벤트 수신 서버 닫기
        /// </summary>
        public void Close()
        {            
            if (lis_sock != null)
            {
                lis_sock.Close();//listen 소켓 닫기
                lis_sock = null;
            }
        }
    }
}