원격 제어 요청을 수신하는 SetupServer 클래스도 정적 클래스로 정의합시다.
public static class SetupServer {
서버 측은 연결 요청을 수신하기 위한 Listening 소켓을 생성하는 부분과 연결 요청을 대기하고 수용하는 부분으로 나눌 수 있습니다. 특히 연결 요청을 대기하고 수용하는 부분은 무한 반복할 것으로 블로킹을 막기 위해 여기에서는 스레드를 사용할게요.
Listening 소켓을 멤버로 선언합시다.
static Socket lis_sock; //연결 요청 수신 Listening 소켓
무한 대기하는 부분의 스레드를 멤버로 선언합시다.
static Thread accept_thread = null; //연결 요청 허용 스레드
연결 요청이 왔을 때 이벤트 처리를 위해 RecvRCInfoEventHandler 형식의 이벤트를 멤버로 선언하세요.
//연결 요청 수신 이벤트 핸들러 static public event RecvRCInfoEventHandler RecvedRCInfo = null;
Setup서버를 시작하는 메서드에서도 IP 주소와 포트 번호를 입력 인자로 받습니다.
static public void Start(string ip, int port) {
먼저 TCP 소켓을 생성합니다.
//로컬 호스트의 IPEndPoint 개체 생성 IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port); //연결 요청 수신 Listening 소켓 생성 lis_sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계 SocketType.Stream,//전송 방식 ProtocolType.Tcp);//프로토콜
소켓과 IPEndPoint를 결합하고 Back 로그 큐 크기를 설정하세요.
lis_sock.Bind(ep);//소켓과 IPEndPoint 결합 lis_sock.Listen(1); //Back 로그 큐 크기 설정
연결 요청을 대기하고 수용하는 스레드를 생성하고 시작합니다.
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은 닫습니다.
do_sock.Close();//소켓 닫기 } } catch { Close(); } }
SetupServer를 종료할 수 있게 Close 메서드를 정의합시다.
public static void Close() {
소켓을 해제화 작업을 수행합니다.
if (lis_sock != null) { lis_sock.Close(); lis_sock = null; } } }
다음은 이번 실습에서 작성한 소스 코드입니다.
//SetupServer.cs using System.Net.Sockets; using System.Threading; using System.Net; namespace 원격제어기 { /// <summary> /// 연결 요청 수신 서버 클래스 - 정적 클래스 /// </summary> public static class SetupServer { static Socket lis_sock; //연결 요청 수신 Listening 소켓 static Thread accept_thread = null; //연결 요청 허용 스레드 /// <summary> /// 연결 요청 수신 이벤트 핸들러 /// </summary> static public event RecvRCInfoEventHandler RecvedRCInfo = null; /// <summary> /// 연결 요청 수신 서버 시작 메서드 /// </summary> /// <param name="ip">서버의 IP주소</param> /// <param name="port">포트</param> static public void Start(string ip, int port) { //로컬 호스트의 IPEndPoint 개체 생성 IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port); //연결 요청 수신 Listening 소켓 생성 lis_sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계 SocketType.Stream,//전송 방식 ProtocolType.Tcp);//프로토콜 lis_sock.Bind(ep);//소켓과 IPEndPoint 결합 lis_sock.Listen(1); //Back 로그 큐 크기 설정 //연결 요청 허용 쓰레드 진입점 개체 생성 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(); } } /// <summary> /// 연결 요청 수신 서버 닫기 /// </summary> public static void Close() { if (lis_sock != null) { lis_sock.Close(); lis_sock = null; } } } }
이 책에서는 비동기적인 요소를 스레드로 작성하고 있습니다. 소켓의 비동기 메서드를 이용하여 프로그래밍하면 보다 나은 형태로 작성할 수 있습니다. 그 부분은 여러분의 몫으로 남길게요.
*Accept 부분을 BeginAccept 메서드 호출로 비동기 처리하는 코드입니다. 동영상 강의를 참고하세요. 동영상 강의에서는 RecvRCInfoEventArgs 부분을 라이브러리로 제작하였습니다.
그리고 해당 형식의 namespace를 RCEventArgsLib로 정했습니다.
첫 using 문은 이러한 이유 때문입니다.*
using RCEventArgsLib; using System; using System.Net; using System.Net.Sockets; namespace 원격제어기 { public static class SetupServer { static Socket lis_sock; static public event RecvRCInfoEventHandler RecvedRCInfoEventHandler = null; static string ip; static int port; public static void Start(string ip,int port) { SetupServer.ip = ip; SetupServer.port = port; SocketBooting(); } private static void SocketBooting() { 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); lis_sock.BeginAccept(DoAccept, null); } static void DoAccept(IAsyncResult result) { if(lis_sock == null) { return; } try { Socket sock = lis_sock.EndAccept(result); DoIt(sock); lis_sock.BeginAccept(DoAccept, null); } catch { Close(); } } static void DoIt(Socket dosock) { if(RecvedRCInfoEventHandler != null) { RecvRCInfoEventArgs e = new RecvRCInfoEventArgs(dosock.RemoteEndPoint); RecvedRCInfoEventHandler(null, e); } dosock.Close(); } public static void Close() { if(lis_sock != null) { lis_sock.Close(); lis_sock = null; } } } }