C#/네트워크 프로그래밍 C#

P2P 메신저 - 파일 수신 서버 구현 [C#]

언휴 2024. 1. 7. 10:00

1. 유튜브 동영상 강의

이번 강의는 파일 수신 서버 구현 실습이예요.

이전 글(P2P 메신저 - 파일 전송 클라이언트 구현 [C#]) 이 필요하겠네요.

2. 이벤트 인자 및 대리자 소스 코드

2.1 연결 요청 수락 이벤트 인자 및 대리자

using System;
using System.Net;

namespace 파일_수신_서버
{
    public delegate void AcceptedEventHandler(object sender, AcceptedEventArgs e);
    public class AcceptedEventArgs:EventArgs
    {
        public IPEndPoint RemoteEndPoint
        {
            get;
            private set;
        }
        public string IPStr
        {
            get
            {
                return RemoteEndPoint.Address.ToString();
            }
        }
        public int Port
        {
            get
            {
                return RemoteEndPoint.Port;
            }
        }
        public AcceptedEventArgs(IPEndPoint rep)
        {
            RemoteEndPoint = rep;
        }
    }
}

2.2 연결 종료 이벤트 인자 및 대리자

using System.Net;

namespace 파일_수신_서버
{
    public delegate void ClosedEventHandler(object sender, ClosedEventArgs e);
    public class ClosedEventArgs
    {
        public IPEndPoint RemoteEndPoint
        {
            get;
            private set;
        }
        public string IPStr
        {
            get
            {
                return RemoteEndPoint.Address.ToString();
            }
        }
        public int Port
        {
            get
            {
                return RemoteEndPoint.Port;
            }
        }
        public ClosedEventArgs(IPEndPoint rep)
        {
            RemoteEndPoint = rep;
        }
    }
}

2.3 파일 이름 수신 이벤트 인자 및 대리자

using System;
using System.Net;

namespace 파일_수신_서버
{
    public delegate void RecvFileNameEventHandler(object sender, RecvFileNameEventArgs e);
    public class RecvFileNameEventArgs:EventArgs
    {
        public IPEndPoint RemoteEndPoint
        {
            get;
            private set;
        }
        public string FileName
        {
            get;
            private set;
        }
        public RecvFileNameEventArgs(string fname,IPEndPoint rep)
        {
            FileName = fname;
            RemoteEndPoint = rep;
        }
    }
}

2.4 파일 길이 수신 이벤트 인자 및 대리자

using System;
using System.Net;

namespace 파일_수신_서버
{
    public delegate void FIleLengthRecvEventHandler(object sender, FIleLengthRecvEventArgs e);
    public class FIleLengthRecvEventArgs:EventArgs
    {
        public IPEndPoint RemoteEndPoint
        {
            get;
            private set;
        }
        public string FileName
        {
            get;
            private set;
        }
        public long Length
        {
            get;
            private set;
        }
        public FIleLengthRecvEventArgs(string fname,IPEndPoint rep,long length)
        {
            FileName = fname;
            RemoteEndPoint = rep;
            Length = length;
        }
    }
}

2.4 파일 데이터 수신 이벤트 인자 및 대리자

using System;
using System.Net;

namespace 파일_수신_서버
{
    public delegate void FileDataRecvEventHandler(object sender, FileDataRecvEventArgs e);
    public class FileDataRecvEventArgs:EventArgs
    {
        public IPEndPoint RemoteEndPoint
        {
            get;
            private set;
        }
        public string FileName
        {
            get;
            private set;
        }
        public long RemainLength
        {
            get;
            private set;
        }
        public byte[] Data
        {
            get;
            private set;
        }
        public FileDataRecvEventArgs(string fname,IPEndPoint rep, long rlen,byte[] data)
        {
            FileName = fname;
            RemoteEndPoint = rep;
            RemainLength = rlen;
            Data = data;
        }
    }
}

3. 파일 수신 서버 클래스 소스 코드

/*                                                                                                                                          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);
                sock.Bind(iep);

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

4. Program.cs 소스 코드

/* https://ehpub.co.kr
 * 파일 수신 서버   
 */
using System;
using System.IO;

namespace 파일_수신_서버
{
    class Program
    {
        static void Main(string[] args)
        {
            FileRecvServ fs = new FileRecvServ("192.168.219.106", 10340);
            fs.AcceptedEventHandler += Fs_AcceptedEventHandler;
            fs.ClosedEventHandler += Fs_ClosedEventHandler;
            fs.RecvFileNameEventHandler += Fs_RecvFileNameEventHandler;
            fs.FIleLengthRecvEventHandler += Fs_FIleLengthRecvEventHandler;
            fs.FileDataRecvEventHandler += Fs_FileDataRecvEventHandler;
            fs.Start();
            Console.ReadKey();
        }


        static long length;
        static FileStream fs;
        private static void Fs_FileDataRecvEventHandler(object sender, FileDataRecvEventArgs e)
        {
            Console.WriteLine("{0}:{1}에서 {2} 남은 길이:{3}", e.RemoteEndPoint.Address, e.RemoteEndPoint.Port, e.FileName, e.RemainLength);
            fs.Write(e.Data, 0, e.Data.Length);
            if(e.RemainLength==0)
            {
                fs.Close();
            }
        }

        private static void Fs_FIleLengthRecvEventHandler(object sender, FIleLengthRecvEventArgs e)
        {
            Console.WriteLine("{0}:{1}에서 {2} 길이:{3}", e.RemoteEndPoint.Address, e.RemoteEndPoint.Port, e.FileName,e.Length);
            length = e.Length;
        }

        private static void Fs_RecvFileNameEventHandler(object sender, RecvFileNameEventArgs e)
        {
            Console.WriteLine("{0}:{1}에서 {2}전송 시작", e.RemoteEndPoint.Address, e.RemoteEndPoint.Port, e.FileName);
            fs = File.Create(e.FileName);
        }

        private static void Fs_ClosedEventHandler(object sender, ClosedEventArgs e)
        {
            Console.WriteLine("{0}:{1}와 연결 해제", e.IPStr, e.Port);
        }

        private static void Fs_AcceptedEventHandler(object sender, AcceptedEventArgs e)
        {
            Console.WriteLine("{0}:{1}와 연결", e.IPStr, e.Port);
        }
    }
}