가. 원격 제어 요청 및 허용

제일 먼저 원격 제어를 요청하는 SetupClient 클래스와 허용하는 SetupServer를 구현합시다.

SetupClient는 단순히 상대에게 누가 요청하는지 알려주는 역할만 할 것입니다. 값을 유지할 필요도 없고 단순한 기능을 수행하는 클래스여서 정적 클래스로 정의합시다.

public static class SetupClient
{

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

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

입력 인자로 IPEndPoint 개체를 생성합니다.

        IPAddress ipaddr = IPAddress.Parse(ip);
        IPEndPoint ep = new IPEndPoint(ipaddr, port);

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

        Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

그리고 연결한 후에 바로 닫습니다.

        sock.Connect(ep);
        sock.Close();

원격 제어를 수용할 지 여부를 판단하는 곳에 누가 연결을 요청하였는지 알려주기 위함일 뿐 별다른 메시지 전달할 것은 없습니다.

    }
}

원격 제어 요청을 수신하여 수락 혹은 거절하는 SetupServer에서는 상대측에서 원격 제어 요청이 온 시점을 알아야 합니다. 이를 위해 이벤트 처리를 할 수 있게 대리자와 이벤트 인자 형식을 정의합시다.

public class RecvRCInfoEventArgs:EventArgs
{

누가 요청했는지 알 수 있게 IPEndPoint 속성을 제공합시다.

    public IPEndPoint IPEndPoint { get; private set; }

IP 주소를 문자열 형태로 변환하여 제공하고 포트 번호도 속성으로 제공합시다.

    public string IPAddressStr
    {
        get { return IPEndPoint.Address.ToString(); }
    }
    public int Port
    {
        get { return IPEndPoint.Port; }
    }

생성자에서는 EndPoint를 입력 인자로 받아 속성에 설정합니다.

    internal RecvRCInfoEventArgs(EndPoint RemoteEndPoint)
    {
        IPEndPoint = RemoteEndPoint as IPEndPoint;
    }
}

이벤트 처리를 위해 대리자 형식을 정의합시다.

public delegate void RecvRCInfoEventHandler(object sender, RecvRCInfoEventArgs e);

원격 제어 요청을 수락하거나 거절하는 SetupServer 클래스도 정적 클래스로 정의합시다.

public static class SetupServer
{

서버 측은 연결 요청을 수신하기 위한 Listening 소켓을 생성하는 부분과 연결 요청을 대기하고 수용하는 부분으로 나눌 수 있습니다. 특히 연결 요청을 대기하고 수용하는 부분은 무한 반복할 것으로 블로킹을 막기 위해 여기에서는 스레드를 사용할게요.

Listening 소켓을 멤버로 선언합시다.

static Socket lis_sock;

무한 대기하는 부분의 스레드를 멤버로 선언합시다.

static Thread accept_thread = null;

연결 요청이 왔을 때 이벤트 처리를 위해 RecvRCInfoEventHandler 형식의 이벤트를 멤버로 선언하세요.

static public event RecvRCInfoEventHandler RecvedRCInfo = null;

Setup서버를 시작하는 메서드에서도 IP 주소와 포트 번호를 입력 인자로 받습니다.

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

먼저 TCP 소켓을 생성합니다.

    IPAddress ipaddr = IPAddress.Parse(ip);
    IPEndPoint ep = new IPEndPoint(ipaddr, port);
    lis_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

네트워크 인터페이스와 결합하고 Back 로그 큐를 설정합니다.

    lis_sock.Bind(ep);
    lis_sock.Listen(1);

연결 요청을 대기하고 수용하는 스레드를 생성하고 시작합니다.

    ThreadStart ts = new ThreadStart(AcceptLoop);
    accept_thread = new Thread(ts);
    accept_thread.Start();
}
static void AcceptLoop()
{
    try
    {

연결 요청을 대기하고 수용하는 부분 Listen 소켓의 Accept 메서드를 호출는 것을 반복합니다.

        while (true)
        {
            Socket do_sock = lis_sock.Accept();
            if (RecvedRCInfo != null)
            {

연결 요청을 수신하는 이벤트 핸들러가 있다면 이벤트 인자를 생성하여 이벤트를 실행합니다.

                RecvRCInfoEventArgs e = new RecvRCInfoEventArgs(do_sock.RemoteEndPoint);
                RecvedRCInfo(null, e);
            }

원격 제어를 요청한 이가 누구인지 확인하고 바로 연결을 닫습니다.

            do_sock.Close();
        }
        catch {    Close();    }
}

SetupServer를 종료할 수 있게 Close 메서드를 정의합시다.

public static void Close()
{

대기 스레드와 소켓을 해제화 작업을 수행합니다.

    if (accept_thread != null){    accept_thread = null;    }
    if (lis_sock != null){    lis_sock.Close();    lis_sock = null;    }
}
using System.Net;
using System.Net.Sockets;

namespace 원격제어기
{
    public static class SetupClient
    {
        public static void Setup(string ip, int port)
        {
            IPAddress ipaddr = IPAddress.Parse(ip);
            IPEndPoint ep = new IPEndPoint(ipaddr, port);
            Socket sock = new Socket(AddressFamily.InterNetwork,
                                               SocketType.Stream, ProtocolType.Tcp);
            sock.Connect(ep);
            sock.Close();
        }
    }
}

[소스 9.1] SetupClient.cs

using System;
using System.Net;

namespace 원격제어기
{
    public class RecvRCInfoEventArgs:EventArgs
    {
        public IPEndPoint IPEndPoint
        {
            get;
            private set; 
        }
        public string IPAddressStr
        {
            get 
            {
                return IPEndPoint.Address.ToString(); 
            }
        }
        public int Port
        {
            get 
            {
                return IPEndPoint.Port; 
            }
        }
        internal RecvRCInfoEventArgs(EndPoint RemoteEndPoint)
        {
            IPEndPoint = RemoteEndPoint as IPEndPoint;
        }
    }
    public delegate void RecvRCInfoEventHandler(object sender, RecvRCInfoEventArgs e);
}

[소스 9.2] RecvRCInfoEventArgs.cs

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

namespace 원격제어기
{
    public static class SetupServer
    {
        static Socket lis_sock;
        static Thread accept_thread = null;
        static public event RecvRCInfoEventHandler RecvedRCInfo = null;

        static public void Start(string ip, int port)
        {
            IPAddress ipaddr = IPAddress.Parse(ip);
            IPEndPoint ep = new IPEndPoint(ipaddr, port);
            lis_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                                         ProtocolType.Tcp);
            lis_sock.Bind(ep);
            lis_sock.Listen(1);
            ThreadStart ts = new ThreadStart(AcceptLoop);
            accept_thread = new Thread(ts);
            accept_thread.Start();
        }

        static void AcceptLoop()
        {
            try
            {
                while (true)
                {
                    Socket do_sock = lis_sock.Accept();
                    if (RecvedRCInfo != null)
                    {
                        RecvRCInfoEventArgs e = new RecvRCInfoEventArgs(
                                                          do_sock.RemoteEndPoint);
                        RecvedRCInfo(null, e);
                    }
                    do_sock.Close();
                }
            }
            catch 
            {
                Close();
            }
        }
        public static void Close()
        {
            if (accept_thread != null)
            {
                accept_thread = null;
            }
            if (lis_sock != null)
            {
                lis_sock.Close();
                lis_sock = null;
            }
        }
    }
}

[소스 9.3] SetupServer.cs