P2P 메신저 – 파일 송수신 기능 구현 [네트워크 프로그래밍 C#]

안녕하세요. 언제나 휴일에 언휴예요.

이번에는 P2P 메신저의 파일 송수신 기능을 구현할 거예요.

이미 P2P 메신저에서 숏 메시지를 송수신하는 부분은 구현했어요.

그리고 파일 전송 클라이언트파일 수신 서버도 구현했죠.

이번 강의에서는 파일 전송 클라이언트와 파일 수신 서버를 라이브러리를 만들고 이를 참조하여 P2P 메신저에 파일 송수신 기능을 구현할 거예요.

파일 송수신 라이브러리에 사용할 소스 코드는 앞에서 제작한 파일 전송 클라이언트와 파일 수신 서버에 있는 소스 코드를 그대로 사용합니다.

1. 컨트롤 추가 배치

먼저 Form1에 자신의 파일 수신 서버 포트와 전송할 때 상대 파일 수신 포트를 입력할 텍스트 상자를 추가합니다. 그리고 설정 버튼을 추가하세요.

그리고 파일을 마우스로 드래그하여 리스트 박스에 옮기면 전송할 수 있게 할 거예요. 이를 위해 리스트 박스에 AllowDrop 속성을 true로 설정 변경하세요.

컨트롤 추가 배치
[그림] 컨트롤 추가 배치

2. 컨트롤의 이벤트 핸들러 등록

btn_my_fset 버튼과 btn_other_fset 버튼 Click 이벤트 핸들러를 등록하세요.

메시지 리스트 박스인 lbox_msg의 DragEnter와 DragDrop 이벤트 핸들러를 등록하세요.

3. 소스 코드 구현

Form1.cs 파일에 추가한 이벤트 핸들러를 구현합니다. 다른 부분은 차이가 없어요.

using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using 파일_수신_서버;
using 파일_전송_클라이언트;

namespace P2P_예광탄_숏메시지
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btn_my_set_Click(object sender, EventArgs e)
        {
            string ip = tbox_my_ip.Text;
            int port = 0;
            if(int.TryParse(tbox_my_port.Text,out port)==false)
            {
                MessageBox.Show("포트를 잘못 입력하셨네요.");
                return;
            }
            SmsgServer sms = new SmsgServer(ip,port);
            sms.SmsgRecvEventHandler += Sms_SmsgRecvEventHandler;
            if(sms.Start()==false)
            {
                MessageBox.Show("숏메시지 서버 가동 실패!");
            }
            else
            {
                tbox_my_ip.Enabled = tbox_my_port.Enabled = btn_my_set.Enabled = false;
            }
        }

        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_other_set_Click(object sender, EventArgs e)
        {
            other_ip = tbox_other_ip.Text;
            if(int.TryParse(tbox_other_port.Text, out other_port)==false)
            {
                MessageBox.Show("포트 번호를 정수로 변환할 수 없습니다.");
            }
        }

        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);
            }
        }

        private void btn_my_fset_Click(object sender, EventArgs e)
        {
            string ip = tbox_my_ip.Text;
            int port = 0;
            if(int.TryParse(tbox_my_fport.Text,out port)==false)
            {
                MessageBox.Show("포트를 잘못 입력하셨네요.");
                return;
            }
            FileRecvServ frs= new FileRecvServ(ip, port);
            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
            {
                tbox_my_ip.Enabled = tbox_my_fport.Enabled = false;
                btn_my_fset.Enabled = false;
            }

        }

        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;
        private void btn_other_fset_Click(object sender, EventArgs e)
        {
            if(int.TryParse(tbox_other_fport.Text,out other_fport)==false)
            {
                MessageBox.Show("포트 번호를 정수로 변환할 수 없습니다.");
            }
        }
    }
}