제일 먼저 원격 제어를 요청하는 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