프로젝트/C# 원격제어 프로그램

[C#] 원격 제어 프로그램 4. 원격 제어 요청 클라이언트

언휴 2024. 1. 8. 09:12

1. 유튜브 동영상 강의

*동영상 강의에서는 Connect 부분을 비동기로 처리하는 BeginConnect를 호출합니다. 그리고 연결 요청 성공과 실패를 처리할 수 있게 이벤트를 추가 정의하였습니다. *

2. 원격 제어 요청 클라이언트

원격 제어를 요청하는 SetupClient 클래스를 구현합시다.

SetupClient는 단순히 상대에게 누가 요청하는지 알려주는 역할만 수행할 거예요. 값을 유지할 필요도 없고 개체를 만들 필요도 없는 클래스이므로 정적 클래스로 정의하세요.

public static class SetupClient
{

Setup 클라이언트에서는 원격 제어하고자 하는 상대 IP와 포트 정보를 입력 인자로 받습니다.

    public static void Setup(string ip, int port)
    {

상대 IP 주소를 입력 인자로 IPEndPoint 개체를 생성하세요.

        IPAddress ipaddr = IPAddress.Parse(ip); //상대 IP 주소 개체를 생성
        IPEndPoint ep = new IPEndPoint(ipaddr, port); //IP 단말 주소(IP주소 + 포트번호) 개체 생성

TCP 방식의 소켓을 생성합니다.

        //TCP 소켓 생성(네트워크 주소 체계, 전송 방식, 프로토콜)
        Socket sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계
            SocketType.Stream, //전송 방식
            ProtocolType.Tcp);//프로토콜

연결한 후에 바로 닫습니다. 원격 제어를 수용할 지 여부를 판단하는 곳에 원격 제어 요청이 있다는 것을 알려주는 목적 뿐이어서 전달할 정보는 없습니다.

        sock.Connect(ep);//연결 요청
        sock.Close();//소켓 닫기
    }
}

이처럼 작성하여 컴파일하면 “형식 또는 네임 스페이스 이름을 찾을 수 없습니다.”와 같은 오류 메시지가 뜰 거예요. IPAddress와 IPEndPoint, Socket 등을 사용하기 위해 필요한 네임 스페이스를 소스 코드 맨 위에 추가하세요.

using System.Net;
using System.Net.Sockets;

Microsoft Visual Studio에서 프로그래밍할 때 어떠한 형식이 어느 namespace에 있는지 모르더라도 작성한 형식 이름에 커서를 위치한 상태에서 Ctrl키와 .(DOT)을 누르면 아래처럼 인텔리센스가 동작하여 쉽게 using 문을 추가할 수 있습니다. (개발 도구 버전에 따라 다를 수 있습니다.)

[그림 8] 인텔리센스 기능으로 using 문 추가
[그림 8] 인텔리센스 기능으로 using 문 추가

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

//SetupClient.cs
using System.Net;
using System.Net.Sockets;

namespace 원격제어기
{
    /// <summary>
    /// 원격 제어 요청 클라이언트 - 정적 클래스
    /// </summary>
    public static class SetupClient
    {
        /// <summary>
        /// 원격 제어 요청 메서드
        /// </summary>
        /// <param name="ip">상대 IP 주소</param>
        /// <param name="port">상대 포트 번호</param>
        public static void Setup(string ip, int port)
        {
            IPAddress ipaddr = IPAddress.Parse(ip); //상대 IP 주소 개체를 생성
            IPEndPoint ep = new IPEndPoint(ipaddr, port); //IP 단말 주소(IP주소 + 포트번호) 개체 생성
            //TCP 소켓 생성(네트워크 주소 체계, 전송 방식, 프로토콜)
            Socket sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계
                SocketType.Stream, //전송 방식
                ProtocolType.Tcp);//프로토콜
            sock.Connect(ep);//연결 요청
            sock.Close();//소켓 닫기
        }
    }
}

 다음은 연결 요청 부분을 비동기로 처리하여 연결 요청 과정에 Blocking 현상이 발생하지 않도록 품질 수준을 높인 코드입니다.  동영상 강의를 참고하세요.

using System;
using System.Net;
using System.Net.Sockets;

namespace 원격제어기
{
    ///
    /// 원격 제어 요청 클라이언트 - 정적 클래스
    /// 
    public static class SetupClient
    {
        public static event EventHandler ConnectedEventHandler = null;
        public static event EventHandler ConnectFailedEventHandler = null;
        static Socket sock;
        ///
        /// 원격 제어 요청 메서드
        /// 
        ///상대 IP 주소
        ///상대 포트 번호
        public static void Setup(string ip, int port)
        {
            IPAddress ipaddr = IPAddress.Parse(ip); //상대 IP 주소 개체를 생성
            IPEndPoint ep = new IPEndPoint(ipaddr, port); //IP 단말 주소(IP주소 + 포트번호) 개체 생성
            //TCP 소켓 생성(네트워크 주소 체계, 전송 방식, 프로토콜)
            sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계
                SocketType.Stream, //전송 방식
                ProtocolType.Tcp);//프로토콜
            //sock.Connect(ip, port);
            sock.BeginConnect(ep, DoConnect, sock);
        }
        static void DoConnect(IAsyncResult result)
        {            
            try
            {
                sock.EndConnect(result);
                if(ConnectedEventHandler != null)
                {
                    ConnectedEventHandler(null, new EventArgs());//연결 성공
                }
            }
            catch
            {
                if(ConnectFailedEventHandler != null)
                {
                    ConnectFailedEventHandler(null, new EventArgs());//연결 실패
                }
            }
            sock.Close();
        }
    }
}