안녕하세요. 언제나 휴일입니다.
앞에서 P2P 메신저의 숏메시지와 파일 송수신을 구현하였고 인증 서비스의 공용 라이브러리를 제작했어요.
이번에는 인증 서버 및 Peer 부분을 수정하는 작업을 진행할게요.
1. 인증 서버
인증 서버는 콘솔 응용으로 제작할게요. .NET 리모팅 서비스로 제작할 것이기 때문에 별다른 코드는 필요하지 않죠. ( 이전 강의에서 제작한 공용 라이브러리를 참조 추가합니다.)
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; namespace 인증_서버 { class Program { static void Main(string[] args) { HttpChannel hc = new HttpChannel(10800); ChannelServices.RegisterChannel(hc, false); RemotingConfiguration.RegisterWellKnownServiceType( typeof(EHAAALib.EHAAA), "AAASVC", WellKnownObjectMode.Singleton); Console.ReadKey(); } } }
2. Peer 프로그램 – 컨트롤 배치
이전에 만들었던 Form1.cs를 MainForm.cs로 변경하고 StartForm을 추가한 후 컨트롤 배치하세요.
세 개의 버튼에 이벤트 핸들러를 각각 등록합니다.
lbox_user 선택 변경 이벤트 핸들러를 등록합니다.
timer1의 Tick 이벤트 핸들러를 등록합니다.
폼의 Load 이벤트 핸들러를 등록합니다.
두 개의 버튼에 클릭 이벤트 핸드러를 등록합니다.
3. Peer 프로그램 – 소스 코드
Program.cs
using System; using System.Windows.Forms; namespace P2P_예광탄_숏메시지 { static class Program { /// /// 해당 애플리케이션의 주 진입점입니다. /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new StartForm()); } } }
StartForm.cs
using EHAAALib; using System; using System.Windows.Forms; namespace P2P_예광탄_숏메시지 { public partial class StartForm : Form { public StartForm() { InitializeComponent(); } private void btn_join_Click(object sender, EventArgs e) { if(Eaaa.Join(tbox_id.Text,tbox_pw.Text)) { MessageBox.Show("가입을 축하합니다."); } else { MessageBox.Show("가입 실패입니다."); } } EHAAA Eaaa { get { return Activator.GetObject( typeof(EHAAA), "http://인증 서버 IP주소:10800/AAASVC" ) as EHAAA; } } private void btn_login_Click(object sender, EventArgs e) { int re = Eaaa.Login(tbox_id.Text, tbox_pw.Text); if(re == 0) { MainForm mf = new MainForm(tbox_id.Text, tbox_pw.Text); mf.FormClosed += Mf_FormClosed; this.Visible = false; mf.ShowDialog(); } else { MessageBox.Show(string.Format("로긴 실패 -{0}", re)); } } private void Mf_FormClosed(object sender, FormClosedEventArgs e) { this.Visible = true; } } }
MainForm.cs
using EHAAALib; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Windows.Forms; using 파일_수신_서버; using 파일_전송_클라이언트; namespace P2P_예광탄_숏메시지 { public partial class MainForm : Form { string ID { get; set; } string PW { get; set; } public MainForm(string id,string pw) { ID = id; PW = pw; InitializeComponent(); } private void Sms_SmsgRecvEventHandler(object sender, SmsgRecvEventArgs e) { AddMessage(string.Format("{0}:{1}→{2}", e.IPStr, e.Port, e.Msg)); } delegate void MyDele(string msg); private void AddMessage(string msg) { if(lbox_msg.InvokeRequired) { MyDele dele = AddMessage; object[] objs = new object[] { msg }; lbox_msg.BeginInvoke(dele, objs); } else { lbox_msg.Items.Add(msg); } } string other_ip; int other_port=10300; private void btn_send_Click(object sender, EventArgs e) { SmsgClient.SendMsgAsync(other_ip, other_port, tbox_msg.Text); lbox_msg.Items.Add(string.Format("{0}:{1}←{2}", other_ip, other_port, tbox_msg.Text)); tbox_msg.Text = ""; } private void lbox_msg_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.All; } private void lbox_msg_DragDrop(object sender, DragEventArgs e) { FileSendClient fsc = new FileSendClient(other_ip, other_fport); fsc.SendFileDataEventHandler += Fsc_SendFileDataEventHandler; string[] fs = e.Data.GetData(DataFormats.FileDrop) as string[]; foreach(string f in fs) { fsc.SendAsync(f); string msg = string.Format("{0}:{1}에게 {2}파일 전송 시작", other_ip, other_fport); AddMessage(msg); } } private void Fsc_SendFileDataEventHandler(object sender, SendFileDataEventArgs e) { if(e.Remain ==0) { string msg = string.Format("{0}파일 {1}bytes 남음...", e.FileName, e.Remain); AddMessage(msg); } } Dictionary<string, FileStream> fsdic = new Dictionary<string, FileStream>(); private void Frs_RecvFileNameEventHandler(object sender, RecvFileNameEventArgs e) { string fname = e.FileName; int index = fname.LastIndexOf(@"\"); if(index !=-1) { fname = fname.Substring(index + 1); } FileStream fs = File.Create(fname); fsdic[e.FileName] = fs; } private void Frs_FIleLengthRecvEventHandler(object sender, FIleLengthRecvEventArgs e) { string msg = string.Format("{0}:{1}에서 파일{2},{3}bytes 전송 시작", e.RemoteEndPoint.Address, e.RemoteEndPoint.Port, e.FileName, e.Length); AddMessage(msg); } private void Frs_FileDataRecvEventHandler(object sender, FileDataRecvEventArgs e) { FileStream fs = fsdic[e.FileName]; fs.Write(e.Data, 0, e.Data.Length); if(e.RemainLength ==0) { string msg = string.Format("{0}:{1}에서 파일{2} 전송 완료", e.RemoteEndPoint.Address, e.RemoteEndPoint.Port, e.FileName); AddMessage(msg); fs.Close(); } } private void Frs_ClosedEventHandler(object sender, ClosedEventArgs e) { string msg = string.Format("{0}:{1}파일 전송을 마치고 연결 해제", e.IPStr, e.Port); AddMessage(msg); } private void Frs_AcceptedEventHandler(object sender, AcceptedEventArgs e) { string msg = string.Format("{0}:{1}파일 전송을 위해 연결", e.IPStr, e.Port); AddMessage(msg); } int other_fport; EHAAA Eaaa { get { return Activator.GetObject( typeof(EHAAA), "http://인증 서버 IP주소:10800/AAASVC" ) as EHAAA; } } private void timer1_Tick(object sender, EventArgs e) { Eaaa.KeepAlive(ID); } private void btn_logout_Click(object sender, EventArgs e) { Eaaa.Logout(ID); timer1.Enabled = false; Close(); } private void btn_withdraw_Click(object sender, EventArgs e) { Eaaa.Withdraw(ID, PW); timer1.Enabled = false; Close(); } int sport = 10400; int fport = 10200; int bport = 10600; private void MainForm_Load(object sender, EventArgs e) { timer1.Enabled = true; MySSet(); MyFSet(); UserInfoCSServer ucbs = new UserInfoCSServer(DefAddress.ToString(), bport); ucbs.UserInfoEventHandler += Ucbs_UserInfoEventHandler; if(ucbs.Start()==false) { MessageBox.Show("허거거^^;;"); } bport = ucbs.Port; Eaaa.KeepAlive(ID, DefAddress.ToString(), sport, fport, bport); } private void Ucbs_UserInfoEventHandler(object sender, UserInfoEventArgs e) { if(e.FPort == 0) { UserInfoEventArgs ru = null; foreach (UserInfoEventArgs uiea in lbox_user.Items) { if(uiea.ID == e.ID) { ru = uiea; break; } } if(ru != null) { lbox_user.Items.Remove(ru); } } else { lbox_user.Items.Add(e); } } IPAddress DefAddress { get { string hname = Dns.GetHostName(); IPHostEntry ihe = Dns.GetHostEntry(hname); foreach(IPAddress ipaddr in ihe.AddressList) { if(ipaddr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { return ipaddr; } } return IPAddress.Any; } } private void MyFSet() { FileRecvServ frs = new FileRecvServ(DefAddress.ToString(), fport); frs.AcceptedEventHandler += Frs_AcceptedEventHandler; frs.ClosedEventHandler += Frs_ClosedEventHandler; frs.FileDataRecvEventHandler += Frs_FileDataRecvEventHandler; frs.FIleLengthRecvEventHandler += Frs_FIleLengthRecvEventHandler; frs.RecvFileNameEventHandler += Frs_RecvFileNameEventHandler; if (frs.Start() == false) { MessageBox.Show("파일 수신 서버 가동 실패"); } else { fport = frs.Port; } } private void MySSet() { SmsgServer sms = new SmsgServer(DefAddress.ToString(), sport); sms.SmsgRecvEventHandler += Sms_SmsgRecvEventHandler; if (sms.Start() == false) { MessageBox.Show("숏메시지 서버 가동 실패!"); } else { sport = sms.Port; } } private void lbox_user_SelectedIndexChanged(object sender, EventArgs e) { UserInfoEventArgs uie = lbox_user.SelectedItem as UserInfoEventArgs; other_ip = uie.IPStr; other_port = uie.SPort; other_fport = uie.FPort; } } }
SmsgClient.cs
using System.IO; using System.Net; using System.Net.Sockets; namespace P2P_예광탄_숏메시지 { public static class SmsgClient { public static void SendMsgAsync(string other_ip, int other_port, string text) { SendDele dele = SendMsg; dele.BeginInvoke(other_ip, other_port, text, null, null); } delegate void SendDele(string other_ip, int other_port, string text); public static void SendMsg(string other_ip, int other_port, string text) { try { byte[] packet = new byte[1024]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write(text); bw.Close(); ms.Close(); Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse(other_ip), other_port); sock.Connect(iep); sock.Send(packet); sock.Close(); } catch { } } } }
SmsgRecvEventArgs.cs
using System; using System.Net; namespace P2P_예광탄_숏메시지 { public delegate void SmsgRecvEventHandler(object sender, SmsgRecvEventArgs e); public class SmsgRecvEventArgs:EventArgs { public IPEndPoint RemoteEndPoint { get; private set; } public string Msg { get; private set; } public string IPStr { get { return RemoteEndPoint.Address.ToString(); } } public int Port { get { return RemoteEndPoint.Port; } } public SmsgRecvEventArgs(IPEndPoint remote,string msg) { RemoteEndPoint = remote; Msg = msg; } } }
SmsgServer.cs
using System.IO; using System.Net; using System.Net.Sockets; namespace P2P_예광탄_숏메시지 { public class SmsgServer { public event SmsgRecvEventHandler SmsgRecvEventHandler = null; public string IPStr { get; private set; } public int Port { get; private set; } public SmsgServer(string ipstr, int port) { IPStr = ipstr; Port = port; } Socket sock; public bool Start() { try { sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse(IPStr), Port); bool check = true; while(check) { try { sock.Bind(iep); check = false; } catch { Port += 2; iep = new IPEndPoint(IPAddress.Parse(IPStr), Port); } } sock.Listen(5); AcceptLoopAsync(); return true; } catch { return false; } } delegate void AcceptDele(); private void AcceptLoopAsync() { AcceptDele dele = AcceptLoop; dele.BeginInvoke(null, null); } private void AcceptLoop() { Socket dosock; while(true) { dosock = sock.Accept(); DoIt(dosock); } } private void DoIt(Socket dosock) { IPEndPoint remote = dosock.RemoteEndPoint as IPEndPoint; byte[] packet = new byte[1024]; dosock.Receive(packet); dosock.Close(); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); string msg = br.ReadString(); br.Close(); ms.Close(); if(SmsgRecvEventHandler != null) { SmsgRecvEventHandler(this, new SmsgRecvEventArgs(remote,msg)); } } } }
UserInfoCSServer.cs
using System.IO; using System.Net; using System.Net.Sockets; namespace P2P_예광탄_숏메시지 { public class UserInfoCSServer { public event UserInfoEventHandler UserInfoEventHandler = null; public string IPStr { get; set; } public int Port { get; set; } public UserInfoCSServer(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 ipaddr = IPAddress.Parse(IPStr); IPEndPoint iep = new IPEndPoint(ipaddr, Port); bool check = true; while(check) { try { sock.Bind(iep); check = false; } catch { Port += 2; iep = new IPEndPoint(ipaddr, Port); } } sock.Listen(5); AcceptLoopAsync(); return true; } catch { return false; } } delegate void AcceptDele(); private void AcceptLoopAsync() { AcceptDele dele = AcceptLoop; dele.BeginInvoke(null, null); } private void AcceptLoop() { Socket dosock; 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) { byte[] packet = new byte[1024]; dosock.Receive(packet); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); string id = br.ReadString(); string ip = br.ReadString(); int sport = br.ReadInt32(); int fport = br.ReadInt32(); br.Close(); ms.Close(); if(UserInfoEventHandler != null) { UserInfoEventHandler(this, new UserInfoEventArgs(id, ip, sport, fport)); } dosock.Close(); } } }
UserInfoEventArgs.cs
using System; namespace P2P_예광탄_숏메시지 { public delegate void UserInfoEventHandler(object sender, UserInfoEventArgs e); public class UserInfoEventArgs:EventArgs { public string ID { get; private set; } public string IPStr { get; private set; } public int SPort { get; private set; } public int FPort { get; private set; } public UserInfoEventArgs(string id, string ipstr, int sport,int fport) { ID = id; IPStr = ipstr; SPort = sport; FPort = fport; } public override string ToString() { return ID; } } }
4. 파일 송수신 라이브러리 수정
FileRecvServ.cs 파일 수정 후
/* https://ehpub.co.kr * 파일 수신 서버 */ using System; using System.Diagnostics.Eventing.Reader; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; namespace 파일_수신_서버 { public class FileRecvServ { const int MAX_PACK_SIZE = 1024; public event AcceptedEventHandler AcceptedEventHandler = null; public event ClosedEventHandler ClosedEventHandler = null; public event RecvFileNameEventHandler RecvFileNameEventHandler = null; public event FIleLengthRecvEventHandler FIleLengthRecvEventHandler = null; public event FileDataRecvEventHandler FileDataRecvEventHandler = null; public string IPStr { get; private set; } public int Port { get; private set; } public FileRecvServ(string ip,int port) { IPStr = ip; Port = port; } Socket sock; public bool Start() { try { sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ipaddr = IPAddress.Parse(IPStr); IPEndPoint iep = new IPEndPoint(ipaddr, Port); bool check = true; while (check) { try { sock.Bind(iep); check = false; } catch { Port += 2; iep = new IPEndPoint(ipaddr, Port); } } sock.Listen(5); AcceptLoopAsync(); } catch { return false; } return true; } delegate void AcceptDele(); private void AcceptLoopAsync() { AcceptDele dele = AcceptLoop; dele.BeginInvoke(null, null); } private void AcceptLoop() { while(true) { Socket dosock = sock.Accept(); DoItAsync(dosock); } } Thread thread; private void DoItAsync(object dosock) { ParameterizedThreadStart pts = DoIt; thread = new Thread(pts); thread.Start(dosock); } void DoIt(object osock) { Socket dosock = osock as Socket; IPEndPoint rep = dosock.RemoteEndPoint as IPEndPoint; if(AcceptedEventHandler != null) { AcceptedEventHandler(this, new AcceptedEventArgs(rep)); } string fname = RecvFileName(dosock); if(RecvFileNameEventHandler != null) { RecvFileNameEventHandler(this, new RecvFileNameEventArgs(fname, rep)); } long length = RecvFileLength(dosock); if(FIleLengthRecvEventHandler !=null) { FIleLengthRecvEventHandler(this, new FIleLengthRecvEventArgs(fname, rep, length)); } RecvFile(dosock, fname, length); dosock.Close(); if(ClosedEventHandler != null) { ClosedEventHandler(this, new ClosedEventArgs(rep)); } } private void RecvFile(Socket dosock, string fname, long length) { IPEndPoint rep = dosock.RemoteEndPoint as IPEndPoint; byte[] packet = new byte[MAX_PACK_SIZE]; while(length>=MAX_PACK_SIZE) { int rlen = dosock.Receive(packet); if(FileDataRecvEventHandler != null) { byte[] pd2 = new byte[rlen]; MemoryStream ms = new MemoryStream(pd2); ms.Write(packet, 0, rlen); FileDataRecvEventHandler(this, new FileDataRecvEventArgs(fname, rep, length, pd2)); } length -= rlen; } dosock.Receive(packet, (int)length, SocketFlags.None); if (FileDataRecvEventHandler != null) { byte[] pd2 = new byte[length]; MemoryStream ms = new MemoryStream(pd2); ms.Write(packet, 0,(int)length); FileDataRecvEventHandler(this, new FileDataRecvEventArgs(fname, rep, 0, pd2)); } } private long RecvFileLength(Socket dosock) { byte[] packet = new byte[8]; dosock.Receive(packet); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); long length = br.ReadInt64(); br.Close(); ms.Close(); return length; } private string RecvFileName(Socket dosock) { byte[] packet = new byte[MAX_PACK_SIZE]; dosock.Receive(packet); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); string fname = br.ReadString(); br.Close(); ms.Close(); return fname; } } }