안녕하세요. 언제나 휴일에 언휴예요.
이번 실습은 Echo 서버 클래스를 구현하는 실습이예요.
여기서 만들 서버 클래스는 라이브러리로 제작하기 위한 목적으로 사전 작업하는 것이예요.
서버 클래스를 사용하는 곳에서 연결 수락 및 닫기, 메시지 수신에 관한 이벤트 처리를 할 수 있게 정의합니다. 대리자 및 이벤트 인자를 정의하고 Echo 서버에 이벤트 멤버를 정의하여 콜백 처리를 하는 것이죠.
1. 연결 수락 이벤트 인자 및 대리자 소스
using System; using System.Net; namespace 에코_서버_응용___클래스로_정의 { public delegate void AcceptedEventHandler(object sender, AcceptedEventArgs e); public class AcceptedEventArgs:EventArgs { public IPEndPoint RemoteEP { get; private set; } public string IPStr { get { return RemoteEP.Address.ToString(); } } public int Port { get { return RemoteEP.Port; } } public AcceptedEventArgs(IPEndPoint remote_ep) { RemoteEP = remote_ep; } } }
2. 연결 닫기 이벤트 인자 및 대리자 소스
using System; using System.Net; namespace 에코_서버_응용___클래스로_정의 { public delegate void ClosedEventHandler(object sender, ClosedEventArgs e); public class ClosedEventArgs:EventArgs { public IPEndPoint RemoteEP { get; private set; } public string IPStr { get { return RemoteEP.Address.ToString(); } } public int Port { get { return RemoteEP.Port; } } public ClosedEventArgs(IPEndPoint remote_ep) { RemoteEP = remote_ep; } } }
3. 메시지 수신 이벤트 인자 및 대리자 소스
using System; using System.Net; namespace 에코_서버_응용___클래스로_정의 { public delegate void RecvedMsgEventHandler(object sender, RecvedMsgEventArgs e); public class RecvedMsgEventArgs:EventArgs { public IPEndPoint RemoteEP { get; private set; } public string IPStr { get { return RemoteEP.Address.ToString(); } } public int Port { get { return RemoteEP.Port; } } public string Msg { get; private set; } public RecvedMsgEventArgs(IPEndPoint remote_ep,string msg) { RemoteEP = remote_ep; Msg = msg; } } }
4. Echo 서버 클래스 소스
using System.IO; using System.Net; using System.Net.Sockets; namespace 에코_서버_응용___클래스로_정의 { public class EchoServer { public event AcceptedEventHandler AcceptedEventHandler = null; public event ClosedEventHandler ClosedEventHandler = null; public event RecvedMsgEventHandler RecvedMsgEventHandler = null; public string IPStr { get; private set; } public int Port { get; private set; } public EchoServer(string ipstr, int port) { IPStr = ipstr; Port = port; } Socket sock = null; public bool Start() { try { sock = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );//소켓 생성 //인터페이스와 결합 IPAddress addr = IPAddress.Parse(IPStr); IPEndPoint iep = new IPEndPoint(addr, Port); sock.Bind(iep); //백로그 큐 크기 설정 sock.Listen(5); AcceptLoopAsyn(); return true; } catch { return false; } } public void Close() { if (sock != null) { try { sock.Close(); } catch { } } } delegate void AcceptDele(); private void AcceptLoopAsyn() { AcceptDele dele = AcceptLoop; dele.BeginInvoke(null, null); } void AcceptLoop() { Socket dosock = null; while (true) { dosock = sock.Accept(); DoItAsync(dosock); } } delegate void DoItDele(Socket dosock); private void DoItAsync(Socket dosock) { DoItDele dele = DoIt; dele.BeginInvoke(dosock, null, null); } private void DoIt(Socket dosock) { IPEndPoint remote_ep = dosock.RemoteEndPoint as IPEndPoint; if(AcceptedEventHandler != null) { AcceptedEventHandler(this, new AcceptedEventArgs(remote_ep)); } try { byte[] packet = new byte[1024]; while (true) { dosock.Receive(packet); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); string msg = br.ReadString(); br.Close(); ms.Close(); if(RecvedMsgEventHandler != null) { RecvedMsgEventHandler(this, new RecvedMsgEventArgs(remote_ep, msg)); } dosock.Send(packet); } } catch { } finally { dosock.Close(); if (ClosedEventHandler != null) { ClosedEventHandler(this, new ClosedEventArgs(remote_ep)); } } } } }
5. Program. cs
using System; namespace 에코_서버_응용___클래스로_정의 { class Program { static void Main(string[] args) { EchoServer es = new EchoServer([서버 IP 주소], 서버 Port); es.RecvedMsgEventHandler += Es_RecvedMsgEventHandler; es.AcceptedEventHandler += Es_AcceptedEventHandler; es.ClosedEventHandler += Es_ClosedEventHandler; if(es.Start()==false) { Console.WriteLine("서버 가동 실패"); return; } Console.ReadKey(); es.Close(); } private static void Es_ClosedEventHandler(object sender, ClosedEventArgs e) { Console.WriteLine("{0}:{1}에서 연결을 닫음",e.IPStr,e.Port); } private static void Es_AcceptedEventHandler(object sender, AcceptedEventArgs e) { Console.WriteLine("{0}:{1}에서 연결 했음", e.IPStr, e.Port); } private static void Es_RecvedMsgEventHandler(object sender, RecvedMsgEventArgs e) { Console.WriteLine("{0}:{1} →{2}", e.IPStr, e.Port,e.Msg); } } }
6. Echo 클라이언트 측 Program.cs
다음은 이전 강의에서 작성했던 Echo 클라이언트의 소스 코드입니다.
이번 실습에서는 별도의 클라이언트를 제작하지 않고 이전 강의에서 작성한 것을 사용합니다.
using System; using System.IO; using System.Net; using System.Net.Sockets; namespace 에코_클라이언트_응용 { class Program { static void Main(string[] args) { Socket sock = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );//소켓 생성 //인터페이스 결합(옵션) //연결 IPAddress addr = IPAddress.Parse([서버 IP 주소]); IPEndPoint iep = new IPEndPoint(addr, [서버 Port 번호]); sock.Connect(iep); string str; string str2; byte[] packet = new byte[1024]; byte[] packet2 = new byte[1024]; while (true) { Console.Write("전송할 메시지:"); str = Console.ReadLine(); MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write(str); bw.Close(); ms.Close(); sock.Send(packet); if(str == "exit") { break; } sock.Receive(packet2); MemoryStream ms2 = new MemoryStream(packet2); BinaryReader br = new BinaryReader(ms2); str2 = br.ReadString(); Console.WriteLine("수신한 메시지:{0}", str2); br.Close(); ms2.Close(); } sock.Close();//소켓 닫기 } } }