블로그 이미지
따시쿵

calendar

1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28

Notice

2015. 5. 30. 14:22 C# with TCP/IP
프로그램 설명

서버-클라이언트로 구성 된 파일 전송 예제입니다.
한 번에 전송되는 양은 8k 이며, 양방향 통신을 합니다.

1. 기본 클래스와 UI 파일입니다.

기본 클래스 : SocketAcceptedEventArgs, Listener, PacketWriter, PacketReader

Listener.cs PacketIO.cs


UI 파일 : main.cs (서버와 클라이언트가 공통된 UI 를 사용함)


2. QueueType, TransferQueue class 를 정의합니다.

enum QueueType define : 다운로드인지, 업로드인지 구분하는 enum
    public enum QueueType : byte
    {
        Download,
        Upload
    }

class TransferQueue define : 전송되는 queue 에 저장하는 class
    public class TransferQueue
    {
        public static TransferQueue CreateUploadQueue(TransferClient client, string fileName)
        {
            try
            {
                var queue = new TransferQueue();
                queue.Filename = Path.GetFileName(fileName);
                queue.Client = client;
                queue.Type = QueueType.Upload;
                queue.FS = new FileStream(fileName, FileMode.Open);
                queue.Thread = new Thread(new ParameterizedThreadStart(transferProc));
                queue.Thread.IsBackground = true;
                queue.ID = Program.Rand.Next();
                queue.Length = queue.FS.Length;

                return queue;
            }
            catch
            {
                return null;
            }
        }

        public static TransferQueue CreateDownloadQueue(TransferClient client, int id, string saveName, long length)
        {
            try
            {
                var queue = new TransferQueue();
                queue.Filename = Path.GetFileName(saveName);
                queue.Client = client;
                queue.Type = QueueType.Download;
                queue.FS = new FileStream(saveName, FileMode.Create);
                queue.FS.SetLength(length);
                queue.Length = length;
                queue.ID = id;

                return queue;
            }
            catch
            {
                return null;
            }
        }

        private const int FILE_BUFFER_SIZE = 8175;
        private static byte[] file_buffer = new byte[FILE_BUFFER_SIZE];

        private ManualResetEvent pauseEvent;

        public int ID;
        public int Progress, LastProgress;

        public long Transferred;
        public long Index;
        public long Length;

        public bool Running;
        public bool Paused;

        public string Filename;

        public QueueType Type;

        public TransferClient Client;
        public Thread Thread;
        public FileStream FS;

        private TransferQueue()
        {
            pauseEvent = new ManualResetEvent(true);
            Running = true;
        }

        public void Start()
        {
            Running = true;
            Thread.Start(this);
        }

        public void Stop()
        {
            Running = false;
        }

        public void Pause()
        {
            if(!Paused)
            {
                pauseEvent.Reset();
            }
            else
            {
                pauseEvent.Set();
            }

            Paused = !Paused;
        }

        public void Close()
        {
            try
            {
                Client.Transfers.Remove(ID);
            }
            catch
            { 
            
            }

            Running = false;
            FS.Close();
            pauseEvent.Dispose();

            Client = null;
        }

        public void Write(byte[] bytes, long index)
        {
            lock(this)
            {
                FS.Position = index;
                FS.Write(bytes, 0, bytes.Length);
                Transferred += bytes.Length;
            }
        }

        private static void transferProc(object o)
        {
            TransferQueue queue = (TransferQueue)o;

            while(queue.Running && queue.Index < queue.Length)
            {
                queue.pauseEvent.WaitOne();

                if (!queue.Running)
                    break;

                lock(file_buffer)
                {
                    queue.FS.Position = queue.Index;

                    int read = queue.FS.Read(file_buffer, 0, file_buffer.Length);

                    PacketWriter pw = new PacketWriter();
                    pw.Write((byte)Headers.Chunk);
                    pw.Write(queue.ID);
                    pw.Write(queue.Index);
                    pw.Write(read);
                    pw.Write(file_buffer, 0, read);

                    queue.Transferred += read;
                    queue.Index += read;

                    queue.Client.Send(pw.GetBytes());

                    queue.Progress = (int)((queue.Transferred * 100) / queue.Length);

                    if(queue.LastProgress < queue.Progress)
                    {
                        queue.LastProgress = queue.Progress;

                        queue.Client.callProgressChanged(queue);
                    }

                    Thread.Sleep(1);
                }
            }
            queue.Close();
        }
    }

3. queue 작동 방법을 정의합니다.
    public enum Headers : byte
    {
        Queue,
        Start,
        Stop,
        pause,
        Chunk
    }

4. TransferClient class 정의 합니다.


transferClient calss define : 클라이언트 전송 class

    public class TransferClient
    {
        private Socket _baseSocket;

        private byte[] _buffer = new byte[8192];

        private ConnectCallback _connectCallback;

        private Dictionary<int, TransferQueue> _transfers = new Dictionary<int, TransferQueue>();

        public Dictionary<int, TransferQueue> Transfers
        {
            get { return _transfers; }
        }

        public bool Closed
        {
            get;
            private set;
        }

        public string OutputFolder
        {
            get;
            set;
        }

        public IPEndPoint EndPoint
        {
            get;
            private set;
        }

        public event TransferEventHandler Queued;
        public event TransferEventHandler ProgressChanged;
        public event TransferEventHandler Stopped;
        public event TransferEventHandler Complete;
        public event EventHandler Disconnected;

        public TransferClient()
        {
            _baseSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        public TransferClient(Socket sock)
        {
            _baseSocket = sock;
            EndPoint = (IPEndPoint)_baseSocket.RemoteEndPoint;
        }

        public void Connect(string hostName, int port, ConnectCallback callback)
        {
            _connectCallback = callback;
            _baseSocket.BeginConnect(hostName, port, connectionCallback, null);
        }

        private void connectionCallback(IAsyncResult ar)
        {
            string error = null;
            try
            {
                _baseSocket.EndConnect(ar);
                EndPoint = (IPEndPoint)_baseSocket.RemoteEndPoint;
            }
            catch(Exception ex)
            {
                error = ex.Message.ToString();
            }

            _connectCallback(this, error);
        }

        public void Run()
        {
            try
            {
                _baseSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.Peek, receiveCallback, null);
            }
            catch
            {
                Close();
            }
        }

        public void QueueTransfer(string fileName)
        {
            try
            {
                TransferQueue queue = TransferQueue.CreateUploadQueue(this, fileName);
                _transfers.Add(queue.ID, queue);
                PacketWriter pw = new PacketWriter();
                pw.Write((byte)Headers.Queue);
                pw.Write(queue.ID);
                pw.Write(queue.Filename);
                pw.Write(queue.Length);
                Send(pw.GetBytes());

                if(Queued != null)
                {
                    Queued(this, queue);
                }
            }
            catch
            {

            }
        }
        
        public void StartTransfer(TransferQueue queue)
        {
            PacketWriter pw = new PacketWriter();
            pw.Write((byte)Headers.Start);
            pw.Write(queue.ID);
            Send(pw.GetBytes());
        }

        public void StopTransfer(TransferQueue queue)
        {
            if(queue.Type == QueueType.Upload)
            {
                queue.Stop();
            }

            PacketWriter pw = new PacketWriter();
            pw.Write((byte)Headers.Stop);
            pw.Write(queue.ID);
            Send(pw.GetBytes());
            queue.Close();
        }

        public void PauseTransfer(TransferQueue queue)
        {
            if(queue.Type == QueueType.Upload)
            {
                queue.Pause();
                return;
            }

            PacketWriter pw = new PacketWriter();
            pw.Write((byte)Headers.pause);
            pw.Write(queue.ID);
            Send(pw.GetBytes());
        }

        public int GetOverallProgress()
        {
            int overall = 0;

            foreach(KeyValuePair<int, TransferQueue> pair in _transfers)
            {
                overall += pair.Value.Progress;
            }
            System.Diagnostics.Trace.WriteLine(string.Format("1. overall = {0}", overall));
            if (overall > 0)
            {
                overall = (overall * 100) / (_transfers.Count * 100);
            }

            System.Diagnostics.Trace.WriteLine(string.Format("2. overall = {0}", overall));
            if (overall > 100)
                overall = 100;

            return overall;
        }

        public void Send(byte[] data)
        {
            if (Closed)
                return;

            lock(this)
            {
                try
                {
                    _baseSocket.Send(BitConverter.GetBytes(data.Length), 0, 4, SocketFlags.None);
                    _baseSocket.Send(data, 0, data.Length, SocketFlags.None);
                }
                catch
                {
                    Close();
                }
            }
        }

        public void Close()
        {
            Closed = true;
            _baseSocket.Close();
            if (_transfers != null)
            {
                _transfers.Clear();
                _transfers = null;
            }
            _buffer = null;
            OutputFolder = null;

            if (Disconnected != null)
                Disconnected(this, EventArgs.Empty);
        }

        private void process()
        {
            PacketReader pr = new PacketReader(_buffer);

            Headers header = (Headers)pr.ReadByte();

            switch(header)
            {
                case Headers.Queue:
                    {
                        int id = pr.ReadInt32();
                        string fileName = pr.ReadString();
                        long length = pr.ReadInt64();

                        TransferQueue queue = TransferQueue.CreateDownloadQueue(this, id, Path.Combine(OutputFolder, Path.GetFileName(fileName)), length);

                        _transfers.Add(id, queue);

                        if (Queued != null)
                        {
                            Queued(this, queue);
                        }

                    }
                    break;
                case Headers.Start:
                    {
                        int id = pr.ReadInt32();

                        if(_transfers.ContainsKey(id))
                        {
                            _transfers[id].Start();
                        }
                    }
                    break;
                case Headers.Stop:
                    {
                        int id = pr.ReadInt32();

                        if (_transfers.ContainsKey(id))
                        {
                            TransferQueue queue = _transfers[id];

                            queue.Stop();
                            queue.Close();

                            if(Stopped != null)
                            {
                                Stopped(this, queue);
                            }

                            _transfers.Remove(id);
                        }
                    }
                    break;
                case Headers.pause:
                    {
                        int id = pr.ReadInt32();

                        if(_transfers.ContainsKey(id))
                        {
                            _transfers[id].Pause();
                        }
                    }
                    break;
                case Headers.Chunk:
                    {
                        int id = pr.ReadInt32();
                        long index = pr.ReadInt64();
                        int size = pr.ReadInt32();
                        byte[] buffer = pr.ReadBytes(size);

                        TransferQueue queue = _transfers[id];

                        queue.Write(buffer, index);

                        queue.Progress = (int)((queue.Transferred * 100) / queue.Length);

                        if(queue.LastProgress < queue.Progress)
                        {
                            queue.LastProgress = queue.Progress;

                            if(ProgressChanged != null)
                            {
                                ProgressChanged(this, queue);
                            }

                            if(queue.Progress == 100)
                            {
                                queue.Close();

                                if(Complete != null)
                                {
                                    Complete(this, queue);
                                }
                            }
                        }
                    }
                    break;
            }
            pr.Dispose();
        }

        private void receiveCallback(IAsyncResult ar)
        {
            try
            {
                int found = _baseSocket.EndReceive(ar);

                if(found >= 4)
                {
                    _baseSocket.Receive(_buffer, 0, 4, SocketFlags.None);

                    int size = BitConverter.ToInt32(_buffer, 0);

                    int read = _baseSocket.Receive(_buffer, 0, size, SocketFlags.None);

                    while(read < size)
                    {
                        read += _baseSocket.Receive(_buffer, read, size - read, SocketFlags.None);
                    }

                    process();
                }

                Run();
            }
            catch
            {
                Close();
            }
        }

        internal void callProgressChanged(TransferQueue queue)
        {
            if (ProgressChanged != null)
                ProgressChanged(this, queue);
        }
    }

5. UI method 를 정의 합니다.


실행 화면




소스 파일 : 
posted by 따시쿵
2015. 5. 25. 15:13 C# with TCP/IP

프로그램 설명


패킷을 이용한 데이타 전송 예제입니다. 로그인과 회원가입을 예로 들어 설명 합니다.

전송할 데이타를 바로 네트워크 스트림에 쓰지 않고 전송 패킷에 저장을 한 후, 일괄 전송하는 방법입니다.


1. 3개의 프로젝트를 먼저 만듭니다.


  MyLogin_Packet : 클래스 라이브러리 타입, dll class 파일, 패킷 정의용

  MyLoginClient1_WindowsForm : 윈도우즈 폼 타입. 클라이언트 폼. exe 파일

  MyLoginServer1_WindowsForm : 윈도우즈 폼 타입. 서버 폼. exe 파일



2. MyLogin_Packet : 클래스 라이브러리 타입, dll class 파일, 패킷 정의용

   

   기본 패킷 정의

namespace MyLogin_Packet
{
    public enum PacketType :int
    {
        Login = 0,
        Login_RESULT,
        Member_REGISTER,
        Member_REGISTER_RESULT
    }

    [Serializable]
    public class Packet
    {
        public int packet_Type;
        public int packet_Length;
        
        public Packet()
        {
            this.packet_Type = 0;
            this.packet_Length = 0;
        }

        public static byte[] Serialize(Object data)
        {
            try
            {
                MemoryStream ms = new MemoryStream(1024 * 4); // packet size will be maximum 4k
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(ms, data);
                return ms.ToArray();
            }
            catch
            {
                return null;
            }
        }

        public static Object Deserialize(byte[] data)
        {
            try
            {
                MemoryStream ms = new MemoryStream(1024 * 4);
                ms.Write(data, 0, data.Length);

                ms.Position = 0;
                BinaryFormatter bf = new BinaryFormatter();
                Object obj = bf.Deserialize(ms);
                ms.Close();
                return obj;
            }
            catch
            {
                return null;
            }
        }
    }
}

   

   파생된 패킷 정의(로그인, 회원가입)

namespace MyLogin_Packet
{
    [Serializable]
    public class Login : Packet
    {
        public string id_str { get; set; }
        public string pw_str { get; set; }

    }
}

namespace MyLogin_Packet
{
    [Serializable]
    public class MemberRegister : Packet
    {
        public string id_str { get; set; }
        public string pw_str { get; set; }
        public string nickname_str { get; set; }
    }
}

   


3. MyLoginClient1_WindowsForm : 윈도우즈 폼 타입. 클라이언트 폼. exe 파일

   

  로그인 버튼 클릭시 전송할 데이타 Login Packet class 로 Serialize 시킴. 받은 데이타는 LoginResult Packet class 로 Deserialize 시킴.

   

        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = new byte[1024 * 4];

                // 1. connect to server
                TcpClient client = new TcpClient("192.168.0.11", 7778);
                NetworkStream stream = client.GetStream();

                // 2. send the packet
                Login login = new Login();
                login.packet_Type = (int)PacketType.Login;
                login.id_str = textBox1.Text.Trim();
                login.pw_str = textBox2.Text.Trim();

                Packet.Serialize(login).CopyTo(buffer, 0);

                stream.Write(buffer, 0, buffer.Length);

                // 3. receive the packet
                Array.Clear(buffer, 0, buffer.Length);

                int bytesRead = stream.Read(buffer, 0, buffer.Length);
                LoginResult loginResult = (LoginResult)Packet.Deserialize(buffer);

                if (loginResult.result)
                {
                    MessageBox.Show(loginResult.reason, "클라이언트 확인", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    MessageBox.Show(loginResult.reason, "클라이언트 오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                // 4. close the socket
                stream.Close();
                client.Close();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message, "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


4. MyLoginServer1_WindowsForm : 윈도우즈 폼 타입. 서버 폼. exe 파일

  

   stream 으로 데이타를 받은 후, PacketType class 로 deserialize 시킴으로, 받은 패킷이 어떤 기능을 하는 패킷인지 구분하는 로직

  

            Packet packet = (Packet)Packet.Deserialize(buffer);

            if (packet == null)
                return;

            switch ((int)packet.packet_Type)
            {
                case (int)PacketType.Login:
                    {
                              // 로그인 로직 추가
                    }
                    break;
                case (int)PacketType.Member_REGISTER:
                    {
                              // 회원가입 로직 추가
                    }
                    break;
            }

   

   Login packet class 로 deserialize 시킴

  

                        // 받은 패킷을 Login class 로 deserialize 시킴
                        Login login = (Login)Packet.Deserialize(buffer);

                        setLog(string.Format("ID : {0}, PWD : {1}", login.id_str, login.pw_str)); // 화면에 display 시킴

   

   Loginresult packet class 로 serialize 시켜서 클라이언트에게 전송함

  

                        // 전송할 패킷을 LoginResult class 로 serialize 시킴
                        LoginResult loginResult = new LoginResult();
                        loginResult.packet_Type = (int)PacketType.Login_RESULT;
                        if (random.Next(1, 100) % 2 == 0)    // 짝수라면.... 로그인 성공 
                        {
                            loginResult.result = true;
                            loginResult.reason = "정상적으로 로그인이 되었습니다.";
                        }
                        else                                // 홀수라면.... 로그인 실패
                        {
                            loginResult.result = false;
                            loginResult.reason = "아이디와 비밀번호를 확인 하시기 바랍니다.";
                        }

                        Array.Clear(buffer, 0, buffer.Length);
                        Packet.Serialize(loginResult).CopyTo(buffer, 0);
                        stream.Write(buffer, 0, buffer.Length);


실행 화면


소스 파일 :


MyLogin_Packet.zip

MyLoginClient1_WindowsForm.zip  MyLoginServer1_WindowsForm.zip

posted by 따시쿵
2015. 5. 20. 11:50 C# with TCP/IP

프로그램 설명


서버에서 연결되어 있는 클라이언트의 유효성을 체크하는 예제입니다.


1. 윈도우폼의 [도구상자]에서 [Timer] 클래스를 끌어다 폼에 추가 


2. 윈도우폼에 체크 박스를 두어서 체크시 클라이언트 connection 체크를 주기적으로 실행.


   메시지 전송시 헤드만 전송하고 바디 전송은 하지 않습니다. 왜냐하면 유효한지만 

   체크하는 것이므로 연결이 정상적인지, 비정상적인지만 체크하면 됩니다.


   client.SendMessage(BitConverter.GetBytes((int)0));


        #region checkBox1_CheckedChanged
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                //
                timer1.Tick += new EventHandler(TimerEventProcessor);

                // Sets the timer interval to 10 seconds.
                timer1.Interval = 10000;
                timer1.Start();
            }
            else
                timer1.Stop();
        }
        #endregion

        #region TimerEventProcessor
        // This is the method to run when the timer is raised.
        private void TimerEventProcessor(Object myObject, EventArgs myEventArgs)
        {
            Trace.WriteLine("Event raise the TimerEventProcessor");

            Invoke((MethodInvoker)delegate
            {
                for (int i = 0; i < listView1.Items.Count; i++)
                {
                    Client client = listView1.Items[i].Tag as Client;

                    if (client.sck.Connected)
                        client.SendMessage(BitConverter.GetBytes((int)0));  
                    else
                    {
                        this.client_Disconnected(client);
                        DisconnectedClientList(client.sck);
                    }
                }
            });
        }
        #endregion




실행 후


소스 파일 : 

 MyKeepAliveClient1_WindowsForm.zip MyKeepAliveServer1_WindowsForm.zip


posted by 따시쿵