블로그 이미지
따시쿵

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
29 30 31

Notice

2015. 1. 22. 11:23 C# with TCP/IP

닷넷 환경에서 구조체를 이용한 소켓 통신을 지원하기 위한 방법은 크게 두 가지로 나뉩니다.


★ 마샬링(Marshaling)을 이용한 구조체 사용.

★ 바이너리 포매터(Binary Formatter)의 사용.


이번에는 마샬링 기법을 이용한 구조체를 통신하기 위한 방법을 소개합니다.


프로그램 설명


클라이언트가 학생의 4가지 데이타(이름, 과목, 점수, 메모)를 서버로 전송한다고 가정하고 필요한 클래스는 아래와 같습니다. 메소드 Serialize(), Deserialize(ref byte[] data) 가 구조체에서 배열로, 배열에서 구조체로 변경해 주는 기능을 담당하고 있습니다.

DataPacket.cs

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct DataPacket
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public string Name;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public string Subject;

        [MarshalAs(UnmanagedType.I4)]
        public int Grade;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
        public string Memo;

        // Calling this method will return a byte array with the contents
        // of the struct ready to be sent via the tcp socket.
        public byte[] Serialize()
        {
            // allocate a byte array for the struct data
            var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))];

            // Allocate a GCHandle and get the array pointer
            var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            var pBuffer = gch.AddrOfPinnedObject();

            // copy data from struct to array and unpin the gc pointer
            Marshal.StructureToPtr(this, pBuffer, false);
            gch.Free();

            return buffer;
        }

        // this method will deserialize a byte array into the struct.
        public void Deserialize(ref byte[] data)
        {
            var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
            this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket));
            gch.Free();
        }
    }


실행 후



메시지 전송 후



프로그램 작성 순서


1. 소켓/쓰레드과 관련한 네임스페이스를 서버/클라이언트 모두에 포함 시킵니다.

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;


2. 서버 프로그램


    public partial class MainForm : Form
    {
        TcpListener server = null;
        public MainForm()
        {
            InitializeComponent();
            FormClosing += new FormClosingEventHandler(WindowsFormClosing);
            InitStart();
        }

        private void WindowsFormClosing(object sender, FormClosingEventArgs s)
        {
            if (server != null)
                server = null;

            Application.Exit();
        }
        private void InitStart()
        {
            Thread socketworker = new Thread(new ThreadStart(socketThread));
            socketworker.IsBackground = true;
            socketworker.Start();
        }

        private void socketThread()
        {
            try
            {
                server = new TcpListener(IPAddress.Parse("192.168.0.12"), 13000);
                server.Start();

                while (true)
                {
                    TcpClient client = server.AcceptTcpClient();
                    updateStatusInfo("Connected");
                    Thread clientworker = new Thread(new ParameterizedThreadStart(clientThread));
                    clientworker.IsBackground = true;
                    clientworker.Start(client);
                }
            }
            catch (SocketException se)
            {
                Debug.WriteLine("SocketException : {0}", se.Message);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception : {0}", ex.Message);
            }
        }

        private void clientThread(object sender)
        {
            // 1. 데이타 받기
            TcpClient client = sender as TcpClient;
            NetworkStream stream = client.GetStream();
            
            byte[] buffer = new byte[8092];
            DataPacket packet = new DataPacket();

            while (stream.Read(buffer, 0, buffer.Length) != 0)
            {
                // deserializing;                
                packet.Deserialize(ref buffer);
            }

            stream.Close();
            client.Close();

            // 2. 데이타 표시하기
            string Name = packet.Name;
            string Subject = packet.Subject;
            Int32 Grade = packet.Grade;
            string Memo = packet.Memo;

            Debug.WriteLine("{0} : {1} : {2} : {3}", Name, Subject, Grade, Memo);

            Invoke((MethodInvoker)delegate
            {
                int count = listView1.Items.Count;
                count++;

                ListViewItem i = new ListViewItem();
                i.Text = count.ToString();
                i.SubItems.Add(Name);
                i.SubItems.Add(Subject);
                i.SubItems.Add(Grade.ToString());
                i.SubItems.Add(Memo);
                i.SubItems.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                listView1.Items.Add(i);

                listView1.Items[this.listView1.Items.Count - 1].EnsureVisible();
            });

            
            // 3. 상태값 표시하기
            updateStatusInfo("Data Accepted");
        }

        private void updateStatusInfo(string content)
        {
            Action del = delegate()
            {
                lblStatus.Text = content;
            };
            Invoke(del);
        }

        private void btnDataClear_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("데이타를 모두 삭제 하시겠습니까?", "질문", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK)
            {
                listView1.Items.Clear();
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }


3. 클라이언트 프로그램


        public MainForm()
        {
            InitializeComponent();

            btnSend.MouseEnter += new EventHandler(btnSend_MouseEnter);
            btnSend.MouseLeave += new EventHandler(btnSend_MouseLeave);
        }

        void btnSend_MouseEnter(object sender, EventArgs e)
        {
            btnSend.UseVisualStyleBackColor = false;
            btnSend.BackColor = Color.FromArgb(255, 255, 165, 00);  // 배경색을 오렌지 색으로 변경함...
            btnSend.ForeColor = Color.White;                        // 글자색을 흰색으로 변경함
        }

        void btnSend_MouseLeave(object sender, EventArgs e)
        {
            btnSend.UseVisualStyleBackColor = true;
            btnSend.BackColor = SystemColors.Control;               // 배경색을 시스템 기본색으로 변경함...
            btnSend.ForeColor = SystemColors.ControlText;           // 글자색을 시스템 기본색으로 변경함...
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            Thread socketworker = new Thread(new ThreadStart(socketThread));
            socketworker.IsBackground = true;
            socketworker.Start();
        }

        private void socketThread()
        {
            // 1. 데이타패킷 조합
            Debug.WriteLine("{0} : {1} : {2} : {3}", txtName.Text, txtSubject.Text, txtGrade.Text, txtMemo.Text);

            DataPacket packet = new DataPacket();

            packet.Name = txtName.Text;
            packet.Subject = txtSubject.Text;

            Int32 outNum;
            if (Int32.TryParse(txtGrade.Text, out outNum))
            {
                packet.Grade = Convert.ToInt32(txtGrade.Text);
            }
            else
            {
                packet.Grade = 0;
            }

            packet.Memo = txtMemo.Text;

            if (string.IsNullOrEmpty(packet.Name))
            {
                MessageBox.Show("[이 름]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Invoke((MethodInvoker)delegate
                {
                    txtName.Focus();

                });
                return;
            }

            if (string.IsNullOrEmpty(packet.Subject))
            {
                MessageBox.Show("[과 목]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Invoke((MethodInvoker)delegate
                {
                    txtSubject.Focus();

                });
                return;
            }

            if (packet.Grade == 0)
            {
                MessageBox.Show("[점 수]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Invoke((MethodInvoker)delegate
                {
                    txtGrade.Focus();

                });
                return;
            }

            if (string.IsNullOrEmpty(packet.Memo))
            {
                packet.Memo = " ";
            }

            // 2. TcpClient 생성 및 설정
            TcpClient client = new TcpClient(txtServerIP.Text, Convert.ToInt32(txtServerPort.Text));
            NetworkStream stream = client.GetStream();
            updateStatusInfo("Connected");

            // 3. 전송하기
            byte[] buffer = packet.Serialize();

            stream.Write(buffer, 0, buffer.Length);
            updateStatusInfo(string.Format("{0} data sent", buffer.Length));

            // 4. 스트림과 소켓 닫기
            stream.Close();
            client.Close();

            // 5. listview 에 추가하기
            Invoke((MethodInvoker)delegate
            {
                int count = listView1.Items.Count;
                count++;

                ListViewItem i = new ListViewItem();
                i.Text = count.ToString();
                i.SubItems.Add(txtName.Text);
                i.SubItems.Add(txtSubject.Text);
                i.SubItems.Add(txtGrade.Text);
                i.SubItems.Add(txtMemo.Text);
                i.SubItems.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                listView1.Items.Add(i);

                listView1.Items[this.listView1.Items.Count - 1].EnsureVisible();
            });
        }

        private void updateStatusInfo(string content)
        {
            Action del = delegate()
            {
                lblStatus.Text = content;
            };
            Invoke(del);
        }

    }

posted by 따시쿵
2015. 1. 21. 16:37 C# with TCP/IP

닷넷 환경에서 구조체를 이용한 소켓 통신을 지원하기 위한 방법은 크게 두 가지로 나뉩니다.


★ 마샬링(Marshaling)을 이용한 구조체 사용.

★ 바이너리 포매터(Binary Formatter)의 사용.


이번에는 마샬링 기법을 이용한 구조체를 통신하기 위한 방법을 소개합니다.


프로그램 설명


클라이언트가 학생의 4가지 데이타(이름, 과목, 점수, 메모)를 서버로 전송한다고 가정하고 필요한 클래스는 아래와 같습니다. 메소드 Serialize(), Deserialize(ref byte[] data) 가 구조체에서 배열로, 배열에서 구조체로 변경해 주는 기능을 담당하고 있습니다.

DataPacket.cs


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct DataPacket
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public string Name;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public string Subject;

        [MarshalAs(UnmanagedType.I4)]
        public int Grade;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
        public string Memo;

        // Calling this method will return a byte array with the contents
        // of the struct ready to be sent via the tcp socket.
        public byte[] Serialize()
        {
            // allocate a byte array for the struct data
            var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))];

            // Allocate a GCHandle and get the array pointer
            var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            var pBuffer = gch.AddrOfPinnedObject();

            // copy data from struct to array and unpin the gc pointer
            Marshal.StructureToPtr(this, pBuffer, false);
            gch.Free();

            return buffer;
        }

        // this method will deserialize a byte array into the struct.
        public void Deserialize(ref byte[] data)
        {
            var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
            this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket));
            gch.Free();
        }
    }


실행 후



메시지 전송 후



프로그램 작성 순서


1. 소켓/쓰레드과 관련한 네임스페이스를 서버/클라이언트 모두에 포함 시킵니다.


using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;


2. 서버 프로그램


        static void Main(string[] args)
        {
            TcpListener server = null;

            try
            {
                server = new TcpListener(IPAddress.Parse("192.168.0.12"), 13000);
                server.Start();

                byte[] buffer = new byte[1024];
                
                while (true)
                {
                    Console.WriteLine("Waiting for a connection.....");

                    TcpClient client = server.AcceptTcpClient();
                    Console.WriteLine("\nConnected!!");

                    NetworkStream stream = client.GetStream();

                    while (stream.Read(buffer, 0, buffer.Length) != 0)
                    {
                        // deserializing;
                        DataPacket packet = new DataPacket();
                        packet.Deserialize(ref buffer);

                        string Name = packet.Name;
                        string Subject = packet.Subject;
                        Int32 Grade = packet.Grade;
                        string Memo = packet.Memo;
                        
                        Console.WriteLine("이 름 : {0}", Name);
                        Console.WriteLine("과 목 : {0}", Subject);
                        Console.WriteLine("점 수 : {0}", Grade);
                        Console.WriteLine("메 모 : {0}", Memo);
                        Console.WriteLine("");
                        Console.WriteLine("===========================================");
                        Console.WriteLine("");
                    }

                    stream.Close();
                    client.Close();
                }
            }
            catch (SocketException se)
            {
                Console.WriteLine(se.Message.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }

            Console.ReadLine();
        }


3. 클라이언트 프로그램


        static void Main(string[] args)
        {
            try 
            {
                string Name = string.Empty;
                string Subject = string.Empty;
                Int32 Grade = 0;
                string Memo = string.Empty;

                do
                {
                    // 1. 데이타 입력
                    Console.Write("이름 : ");
                    Name = Console.ReadLine();

                    Console.Write("과목 : ");
                    Subject = Console.ReadLine();

                    Console.Write("점수 : ");
                    string tmpGrage = Console.ReadLine();
                    if (tmpGrage != "")
                    {
                        int outGrade = 0;
                        if (Int32.TryParse(tmpGrage, out outGrade))
                            Grade = Convert.ToInt32(tmpGrage);
                        else
                            Grade = 0;
                    }
                    else
                        Grade = 0;

                    Console.Write("메모 : ");
                    Memo = Console.ReadLine();

                    if (string.IsNullOrEmpty(Name) || string.IsNullOrEmpty(Subject))
                        break;

                    // 2. 구조체 데이타를 바이트 배열로 변환
                    DataPacket packet = new DataPacket();
                    packet.Name = Name;
                    packet.Subject = Subject;
                    packet.Grade = Grade;
                    packet.Memo = Memo;

                    byte[] buffer = packet.Serialize();

                    // 3. 서버에 접속
                    TcpClient client = new TcpClient();
                    client.Connect("192.168.0.12", 13000);
                    Console.WriteLine("Connected...");

                    NetworkStream stream = client.GetStream();

                    // 4. 데이타 전송
                    stream.Write(buffer, 0, buffer.Length);
                    Console.WriteLine("{0} data sent", buffer.Length);
                    Console.WriteLine("===============================\n");

                    // 5. 스트림&소켓 닫기
                    stream.Close();
                    client.Close();

                } while (Name != "" && Subject != "");
            }
            catch (SocketException se)
            {
                Console.WriteLine("SocketException : {0} ", se.Message.ToString());
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception : {0} ", ex.Message.ToString());
            }
            Console.ReadLine();
        }

posted by 따시쿵
2015. 1. 20. 10:33 C# with TCP/IP

.닷넷 환경에서 구조체를 이용한 소켓 통신을 지원하기 위한 방법은 크게 두 가지로 나뉩니다.


★ 마샬링(Marshaling)을 이용한 구조체 사용.

★ 바이너리 포매터(Binary Formatter)의 사용.


이번에는 마샬링 기법을 이용한 구조체를 통신하기 위한 방법을 소개합니다.


프로그램 설명


클라이언트가 학생의 4가지 데이타(이름, 과목, 점수, 메모)를 서버로 전송한다고 가정하고 필요한 클래스는 아래와 같습니다.


클라스 파일은 DataPacket.cs 파일입니다.

DataPacket.cs


    class DataPacket
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public string Name;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public string Subject;

        [MarshalAs(UnmanagedType.I4)]
        public int Grade;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
        public string Memo;
    }


실행 후



메시지 전송 후



프로그램 작성 순서


1. 소켓/쓰레드과 관련한 네임스페이스를 서버/클라이언트 모두에 포함 시킵니다.

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;


2. 서버 프로그램


        TcpListener server = null;            
        public MainForm()
        {
            InitializeComponent();
            FormClosing += new FormClosingEventHandler(WindowsFormClosing);
            InitStart();
        }

        private void WindowsFormClosing(object sender, FormClosingEventArgs s)
        {
            if (server != null)
                server = null;

            Application.Exit();
        }
        private void InitStart()
        {
            Thread socketworker = new Thread(new ThreadStart(socketThread));
            socketworker.IsBackground = true;
            socketworker.Start();
        }

        private void socketThread()
        {
            try
            {
                server = new TcpListener(IPAddress.Parse("192.168.0.12"), 13000);
                server.Start();

                while(true)
                { 
                    TcpClient client = server.AcceptTcpClient();
                    updateStatusInfo("Connected");
                    Thread clientworker = new Thread(new ParameterizedThreadStart(clientThread));
                    clientworker.IsBackground = true;
                    clientworker.Start(client);
                }              

            }
            catch(SocketException se)
            {
                Debug.WriteLine("SocketException : {0}", se.Message);
            }
            catch(Exception ex)
            {
                Debug.WriteLine("Exception : {0}", ex.Message);
            }
        }

        private void clientThread(object sender)
        {
            // 1. 데이타 받기
            TcpClient client = sender as TcpClient;
            NetworkStream stream = client.GetStream();

            byte[] buffer = new byte[8092];
            DataPacket packet = new DataPacket();

            while (stream.Read(buffer, 0, Marshal.SizeOf(packet)) != 0)
            {
                unsafe
                {
                    fixed (byte* fixed_buffer = buffer)
                    {
                        Marshal.PtrToStructure((IntPtr)fixed_buffer, packet);
                    }
                }
            }

            stream.Close();
            client.Close();

            // 2. 데이타 표시하기
            string Name = packet.Name;
            string Subject = packet.Subject;
            Int32 Grade = packet.Grade;
            string Memo = packet.Memo;

            Debug.WriteLine("{0} : {1} : {2} : {3}", Name, Subject, Grade, Memo);

            Invoke((MethodInvoker)delegate
            {
                int count = listView1.Items.Count;
                count++;

                ListViewItem i = new ListViewItem();
                i.Text = count.ToString();
                i.SubItems.Add(Name);
                i.SubItems.Add(Subject);
                i.SubItems.Add(Grade.ToString());
                i.SubItems.Add(Memo);
                i.SubItems.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                listView1.Items.Add(i);

                listView1.Items[this.listView1.Items.Count - 1].EnsureVisible();
            });

            // 3. 상태값 표시하기
            updateStatusInfo("Data Accepted");
        }

        private void updateStatusInfo(string content)
        {
            Action del = delegate()
            {
                lblStatus.Text = content;
            };
            Invoke(del);
        }

        private void btnDataClear_Click(object sender, EventArgs e)
        {
            if(MessageBox.Show("데이타를 모두 삭제 하시겠습니까?","질문", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK)
            {
                listView1.Items.Clear();
            }
        }


3. 클라이언트 프로그램


        public MainForm()
        {
            InitializeComponent();

            btnSend.MouseEnter += new EventHandler(btnSend_MouseEnter);
            btnSend.MouseLeave += new EventHandler(btnSend_MouseLeave);
        }

        void btnSend_MouseEnter(object sender, EventArgs e)
        {
            btnSend.UseVisualStyleBackColor = false;
            btnSend.BackColor = Color.FromArgb(255, 255, 165, 00);  // 배경색을 오렌지 색으로 변경함...
            btnSend.ForeColor = Color.White;                        // 글자색을 흰색으로 변경함
        }

        void btnSend_MouseLeave(object sender, EventArgs e)
        {
            btnSend.UseVisualStyleBackColor = true;
            btnSend.BackColor = SystemColors.Control;               // 배경색을 시스템 기본색으로 변경함...
            btnSend.ForeColor = SystemColors.ControlText;           // 글자색을 시스템 기본색으로 변경함...
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            Thread socketworker = new Thread(new ThreadStart(socketThread));
            socketworker.IsBackground = true;
            socketworker.Start();
        }

        private void socketThread()
        {
            // 1. 데이타패킷 조합
            Debug.WriteLine("{0} : {1} : {2} : {3}", txtName.Text, txtSubject.Text, txtGrade.Text, txtMemo.Text);

            DataPacket packet = new DataPacket();

            packet.Name = txtName.Text;
            packet.Subject = txtSubject.Text;

            Int32 outNum;
            if(Int32.TryParse(txtGrade.Text, out outNum))
            {
                packet.Grade = Convert.ToInt32(txtGrade.Text);
            }
            else
            {
                packet.Grade = 0;
            }
            
            packet.Memo = txtMemo.Text;

            if (string.IsNullOrEmpty(packet.Name))
            {
                MessageBox.Show("[이 름]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Invoke((MethodInvoker)delegate
                {
                    txtName.Focus();
                    
                });
                return;
            }

            if (string.IsNullOrEmpty(packet.Subject))
            {
                MessageBox.Show("[과 목]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Invoke((MethodInvoker)delegate
                {
                    txtSubject.Focus();

                });
                return;
            }

            if (packet.Grade == 0)
            {
                MessageBox.Show("[점 수]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Invoke((MethodInvoker)delegate
                {
                    txtGrade.Focus();

                });
                return;
            }

            if (string.IsNullOrEmpty(packet.Memo))
            {
                packet.Memo = " ";
            }

            // 2. TcpClient 생성 및 설정
            TcpClient client = new TcpClient(txtServerIP.Text, Convert.ToInt32(txtServerPort.Text));
            NetworkStream stream = client.GetStream();
            updateStatusInfo("Connected");

            // 3. 전송하기
            byte[] buffer = new byte[Marshal.SizeOf(packet)];

            unsafe
            {
                fixed (byte* fixed_buffer = buffer)
                {
                    Marshal.StructureToPtr(packet, (IntPtr)fixed_buffer, false);
                }
            }

            stream.Write(buffer, 0, Marshal.SizeOf(packet));
            updateStatusInfo(string.Format("{0} data sent", Marshal.SizeOf(packet)));

            // 4. 스트림과 소켓 닫기
            stream.Close();
            client.Close();

            // 5. listview 에 추가하기
            Invoke((MethodInvoker)delegate
            {
                int count = listView1.Items.Count;
                count++;

                ListViewItem i = new ListViewItem();
                i.Text = count.ToString();
                i.SubItems.Add(txtName.Text);
                i.SubItems.Add(txtSubject.Text);
                i.SubItems.Add(txtGrade.Text);
                i.SubItems.Add(txtMemo.Text);
                i.SubItems.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                listView1.Items.Add(i);

                listView1.Items[this.listView1.Items.Count - 1].EnsureVisible();
            });
        }

        private void updateStatusInfo(string content)
        {
            Action del = delegate()
            {
                lblStatus.Text = content;
            };
            Invoke(del);
        }

posted by 따시쿵