원격 제어 프로그램 9. 원격 제어 – 화면 수신 서버

원격 제어 컨트롤러는 원격 제어 호스트의 화면을 주기적으로 수신하여 이를 보면서 제어합니다. 이제 원격 제어 화면을 수신하는 ImageServer 클래스를 구현합시다.

public class ImageServer
{

먼저 Listen 소켓과 연결 요청을 대기하고 수용하는 부분을 위한 스레드를 멤버로 선언합시다.

    Socket lis_sock; //Listening 소켓
    Thread accept_thread = null; 

이미지를 수신할 때 이벤트 처리를 위해 RecvImageEventHandler 형식의 이벤트를 선언합시다.

    public event RecvImageEventHandler RecvedImage = null;

생성자에서는 IP와 포트 정보를 받습니다.

    public ImageServer(string ip, int port)
    {

TCP 소켓을 생성하세요.

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

소켓과 IP 단말 개체와 결합하고 백로그 큐를 설정합니다.

        //EndPoint와 소켓 결합
        IPAddress ipaddr = IPAddress.Parse(ip);
        IPEndPoint ep = new IPEndPoint(ipaddr, port);
        lis_sock.Bind(ep);
        //Back 로그 큐 크기 설정
        lis_sock.Listen(5);

클라이언트 연결 요청을 대기하고 수락하는 스레드를 생성하고 시작합니다.

        //연결 수용 Loop을 수행하는 스레드 시작
        ThreadStart ts = new ThreadStart(AcceptLoop);
        accept_thread = new Thread(ts);
        accept_thread.Start();
    }

연결 요청을 대기하고 수락하는 AcceptLoop 메서드를 구현합시다.

    void AcceptLoop()
    {
        try
        {

클라이언트 연결 요청을 대기하고 수락하는 부분은 무한 반복합니다.

            while (lis_sock != null)
            {

Listen 소켓의 Accept 메서드를 호출하여 이미지를 수신할 작업 소켓을 구합니다.

                Socket do_sock = lis_sock.Accept();//연결 수락

작업 소켓에서 이미지를 수신합니다.

                Receive(do_sock);//do_sock으로 이미지 수신
            }
        }
        catch
        {
            Close();
        }
    }

작업 소켓에서 이미지를 수신하는 Receive 메서드를 구현합시다.

    void Receive(Socket dosock)
    {

이미지 길이를 수신합니다.

        byte[] lbuf = new byte[4]; //이미지 길이를 수신할 버퍼
        dosock.Receive(lbuf); //이미지 길이 수신

수신한 이미지 길이만큼의 버퍼를 생성합니다.

        int len = BitConverter.ToInt32(lbuf, 0);//수신한 버퍼의 내용을 정수로 변환
        byte[] buffer = new byte[len];//이미지 길이만큼의 버퍼 생성

수신한 이미지 길이만큼의 이미지 데이터를 수신합니다.

        int trans = 0;
        while (trans < len)//수신할 이미지 데이터가 남아있으면
        {
            trans += dosock.Receive(buffer, trans, 
                                    len - trans, 
                                    SocketFlags.None);//이미지 수신
        }

이미지 수신 이벤트에 등록 개체가 있으면 이미지 수신 이벤트를 발생합니다.

        if (RecvedImage != null)//이미지 수신 이벤트가 존재하면
        {
            //이미지 수신 이벤트 발생
            IPEndPoint iep = dosock.RemoteEndPoint as IPEndPoint;

수신한 버퍼를 이미지로 변환하여 이벤트 인자를 생성합니다.

            RecvImageEventArgs e = new RecvImageEventArgs(iep, ConvertBitmap(buffer));

이미지를 수신한 이벤트를 발생합니다.

            RecvedImage(this, e);
        }

이미지를 수신하였으니 작업 소켓을 닫습니다.

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

수신한 이미지 버퍼를 이미지로 변환하는 ConvertBitmap 메서드를 구현합시다.

    public Bitmap ConvertBitmap(byte[] data)
    {

byte 배열을 이미지로 변환하기 위해 메모리 스트림 개체를 생성합니다.

        MemoryStream ms = new MemoryStream();

메모리 스트림 개체에 수신한 이미지가 있는 버퍼의 내용을 기록합니다.

        ms.Write(data, 0, (int)data.Length);//메모리 스트림에 버퍼를 기록

메모리 스트림 개체로 비트맵 개체를 생성하여 반환하세요.

        Bitmap bitmap = new Bitmap(ms); //메모리 스트림으로 비트맵 개체 생성
        return bitmap;
    }

이미지 서버를 닫는 메서드도 제공합시다.

    public void Close()
    {

이미 닫혀있을 수도 있으니 존재할 때만 닫습니다.

        if (lis_sock != null)
        {
            lis_sock.Close();

소켓을 닫았으면 다시 Close 메서드를 호출하여도 아무 작업을 하지 않게 null로 설정하세요.

            lis_sock = null;
        }
    }
}

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

//ImageServer.cs
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Drawing;
using System.IO;
using System;

namespace 원격제어기
{
    /// <summary>
    /// 이미지 수신 서버
    /// </summary>
    public class ImageServer
    {
        Socket lis_sock; //Listening 소켓
        Thread accept_thread = null; 

        /// <summary>
        /// 이미지 수신 이벤트
        /// </summary>
        public event RecvImageEventHandler RecvedImage = null;

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="ip">로컬 IP</param>
        /// <param name="port">포트</param>
        public ImageServer(string ip, int port)
        {
            //소켓 생성            
            lis_sock = new Socket(AddressFamily.InterNetwork, 
                                  SocketType.Stream, 
                                  ProtocolType.Tcp);

            //EndPoint와 소켓 결합
            IPAddress ipaddr = IPAddress.Parse(ip);
            IPEndPoint ep = new IPEndPoint(ipaddr, port);
            lis_sock.Bind(ep);

            //Back 로그 큐 크기 설정
            lis_sock.Listen(5);


            //연결 수용 Loop을 수행하는 스레드 시작
            ThreadStart ts = new ThreadStart(AcceptLoop);
            accept_thread = new Thread(ts);
            accept_thread.Start();
        }

        void AcceptLoop()
        {
            try
            {
                while (lis_sock != null)
                {
                    Socket do_sock = lis_sock.Accept();//연결 수락
                    Receive(do_sock);//do_sock으로 이미지 수신
                }
            }
            catch
            {
                Close();
            }
        }

        void Receive(Socket dosock)
        {
            byte[] lbuf = new byte[4]; //이미지 길이를 수신할 버퍼
            dosock.Receive(lbuf); //이미지 길이 수신
            int len = BitConverter.ToInt32(lbuf, 0);//수신한 버퍼의 내용을 정수로 변환
            byte[] buffer = new byte[len];//이미지 길이만큼의 버퍼 생성
            int trans = 0;
            while (trans < len)//수신할 이미지 데이터가 남아있으면
            {
                trans += dosock.Receive(buffer, trans, 
                                        len - trans, 
                                        SocketFlags.None);//이미지 수신
            }
            if (RecvedImage != null)//이미지 수신 이벤트가 존재하면
            {
                //이미지 수신 이벤트 발생
                IPEndPoint iep = dosock.RemoteEndPoint as IPEndPoint;
                RecvImageEventArgs e = new RecvImageEventArgs(iep, ConvertBitmap(buffer));
                RecvedImage(this, e);
            }
            dosock.Close();//소켓 닫기
        }
        /// <summary>
        /// 수신한 버퍼를 비트맵 이미지로 변환 메서드
        /// </summary>
        /// <param name="data">수신한 버퍼</param>
        /// <returns>비트맵 이미지</returns>
        public Bitmap ConvertBitmap(byte[] data)
        {
            MemoryStream ms = new MemoryStream();
            ms.Write(data, 0, (int)data.Length);//메모리 스트림에 버퍼를 기록
            Bitmap bitmap = new Bitmap(ms); //메모리 스트림으로 비트맵 개체 생성
            return bitmap;
        }
        /// <summary>
        /// 비트맵 이미지 서버 닫기 메서드
        /// </summary>
        public void Close()
        {
            if (lis_sock != null)
            {
                lis_sock.Close();
                lis_sock = null;
            }
        }
    }
}