Võ Văn Hải's blog

Chỉ có một điều tuyệt đối đó là mọi thứ đều tương đối…

Gửi và nhận DataTable qua Socket trong C#

Trong bài này, tôi sẽ làm một ví dụ về gửi và nhận DataTable qua Socket trong C#. Bạn có thể thay DataTable bằng Dataset. Code tôi để ở chế độ mở (dùng object) nên bạn có thể làm được điều này.
Phương thức này dùng để Serialize một đối tượng bất kỳ thành mảng byte

  public byte[] SerializeData(Object o)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf1 = new BinaryFormatter();
            bf1.Serialize(ms, o);
            return ms.ToArray();
        }

Phương thức này dùng để DeSerialize một mảng byte thành đối tượng bất kỳ

public object DeserializeData(byte[] theByteArray)
        {
            MemoryStream ms = new MemoryStream(theByteArray);
            BinaryFormatter bf1 = new BinaryFormatter();
            ms.Position = 0;
            return bf1.Deserialize(ms);
        }

Tạo Solution

Bạn tạo 1 solution có tên chẳng hạn là SocketInCSharp.
1. Đầu tiên là ở server: Add 1project, đặt tên là SocketServer, thêm 1 form và trên form thêm 1 nút có tên Start. Kết quả như sau:
server

Source code như sau:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace SocketServer
{
    public partial class Form1 : Form
    {
        private Socket sock = null;
        public Form1()
        {
            InitializeComponent();
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            startButton.Text = "In process...";
            Application.DoEvents();

            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, 5656);
            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            sock.Bind(ipEnd);
            sock.Listen(100);
            Socket clientSock = sock.Accept();
            DataTable table = getdata();
            clientSock.Send(SerializeData(table));

            sock.Close();
            clientSock.Close();
            startButton.Text = "&Start Server";
            Application.DoEvents();
        }

        public byte[] SerializeData(Object o)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf1 = new BinaryFormatter();
            bf1.Serialize(ms, o);
            return ms.ToArray();
        }

       /*Ở đây tôi tạo 1 bảng dữ liệu thử.
Bạn có thể kết nối csdl và load dữ liệu lên*/
       private DataTable getdata()
        {
            DataTable dt = new DataTable();
            DataRow dr;

            dt.Columns.Add(new DataColumn("IntegerValue", typeof(Int32)));
            dt.Columns.Add(new DataColumn("StringValue", typeof(string)));
            dt.Columns.Add(new DataColumn("DateTimeValue", typeof(DateTime)));
            dt.Columns.Add(new DataColumn("BooleanValue", typeof(bool)));

            for (int i = 1; i <= 1000; i++)
            {
                dr = dt.NewRow();
                dr[0] = i;
                dr[1] = "Item " + i.ToString();
                dr[2] = DateTime.Now;
                dr[3] = (i % 2 != 0) ? true : false;

                dt.Rows.Add(dr);
            }
            return dt;
        }
    }
}

Biên dịch chương trình sau đó mở thư mục bin/Debug để chạy tập tin SocketServer.exe. Nhấn nút Start Server để bắt đầu chạy server. Mỗi lần gửi xong phải nhấn nút start một lần. Bạn có thể dùng Threading để cho nó chạy liên tục, tuy nhiên bạn sẽ phải xem đến phương thức SendAsync của socket.

2. Ở phía Client: Bạn Thêm 1 project khác có tên SocketClient vào Solution. Thêm vào 1 textbox để nhập server address, một nút nhấn để nhận dữ liệu. Thiết kế như sau
clientdesign
Source code cho form này như sau:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Net.Sockets;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace SocketClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public object DeserializeData(byte[] theByteArray)
        {
            MemoryStream ms = new MemoryStream(theByteArray);
            BinaryFormatter bf1 = new BinaryFormatter();
            ms.Position = 0;
            return bf1.Deserialize(ms);
        }

        private void receiveButton_Click(object sender, EventArgs e)
        {
            try
            {
                IPAddress[] ipAddress = Dns.GetHostAddresses(serverTextBox.Text);
                IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"),5656);
                Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                clientSock.Connect(ipEnd);
                byte[] data = new byte[1024 * 5000];
                clientSock.Receive(data);
                DataTable dt = (DataTable)DeserializeData(data);
                dataGridView1.DataSource = dt;
                clientSock.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}
Kết quả sau khi nhấn nút nhận dữ liệu
clientresult
Full Source Code: download here

Chúc thành công!

32 Responses to “Gửi và nhận DataTable qua Socket trong C#”

  1. Nogaez said

    Em áp dụng Threading cho Server để có thể kết nối với nhiều Server và không làm cho lớp giao diện bị treo.
    Nhưng vì về kĩ thuật Threading em chưa nắm rõ lắm, nói chung là còn rất kém. Khi em nhấn nút Start thì nó sẽ sinh ra Thread chờ yêu cầu của Client. Vì Thread này em muốn dùng để kết nối với nhiều Client nên em để trong vòng lặp while(true), cụ thể như sau:


    public partial class StartServer : Form
    {
    .....................................................
    private void btstart_Click(object sender, EventArgs e)
    {
    new Thread(new ThreadStart(Listening)).Start();
    }
    private void Listening()
    {
    IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2011);
    TcpListener server = new TcpListener(ip);
    server.Start();
    while (true)
    {
    TcpClient client = server.AcceptTcpClient();
    new SocketServer(client);
    }
    }
    }

    class SocketServer
    {
    private TcpClient client;
    public SocketServer(TcpClient client)
    {
    this.client = client;
    new Thread(new ThreadStart(Processing)).Start();
    }
    private void Processing()
    {
    //
    // Xử lý trao đổi dữ liệu với Client
    //
    }
    }

    Giờ em muốn xử lý nút Stop trên Server sẽ đóng các Thread và đóng các kết nối nhưng không biết đóng Thread như thế nào?
    Khi em Exit chương trình dùng lệnh Application.Exit(); nhưng Thread vẫn chạy ngầm trong hệ thống. Không như Java, em dùng System.exit(0) thì nó thoát chương trình và ngắt luôn Thread.

    Mong thầy hướng dẫn em cách xử lý nút Stop để đóng các Thread an toàn trước khi tắt ứng dụng.
    Cảm ơn thầy đã quan tâm.

  2. Nogaez said

    Em xin lỗi, em không biết dùng thẻ code như thầy nên code nhìn có vẻ không rõ ràng. Mong thầy thông cảm.

  3. Võ Văn Hải said

    Bạn phải hiểu là không có chuyện ngắt Thread mà chỉ có yêu cầu ngắt thôi, còn ngắt hay không còn là chuyện khác.🙂.
    Yêu cầu để dừng 1 thread dạng: workerObject.RequestStop();

    Đây là đoạn code trong msdn để yêu cầu ngắt 1 thread:

    
    using System;
    using System.Threading;
    
    public class Worker
    {
        // This method will be called when the thread is started.
        public void DoWork()
        {
            while (!_shouldStop)
            {
                Console.WriteLine("worker thread: working...");
            }
            Console.WriteLine("worker thread: terminating gracefully.");
        }
        public void RequestStop()
        {
            _shouldStop = true;
        }
        // Volatile is used as hint to the compiler that this data
        // member will be accessed by multiple threads.
        private volatile bool _shouldStop;
    }
    
    public class WorkerThreadExample
    {
        static void Main()
        {
            // Create the thread object. This does not start the thread.
            Worker workerObject = new Worker();
            Thread workerThread = new Thread(workerObject.DoWork);
    
            // Start the worker thread.
            workerThread.Start();
            Console.WriteLine("main thread: Starting worker thread...");
    
            // Loop until worker thread activates.
            while (!workerThread.IsAlive);
    
            // Put the main thread to sleep for 1 millisecond to
            // allow the worker thread to do some work:
            Thread.Sleep(1);
    
            // Request that the worker thread stop itself:
            workerObject.RequestStop();
    
            // Use the Join method to block the current thread 
            // until the object's thread terminates.
            workerThread.Join();
            Console.WriteLine("main thread: Worker thread has terminated.");
        }
    }
    
  4. Nogaez said

    Em chân cảm ơn hướng dẫn rất nhiệt của thầy.

  5. vovanduy said

    private void btnlisten_Click(object sender, EventArgs e)
    {
    results.Items.Add(“Listening for a client…”);
    IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
    Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
    ProtocolType.Tcp);
    newsock.Bind(iep);
    newsock.Listen(5);
    newsock.BeginAccept(new AsyncCallback(AcceptConn), newsock);
    }
    Thầy giúp dùm em,em làm bài tập chat giữa client,em thấy tài liệu nào cũng như code trên,nhưng khi em chạy và nhấn button listen thì báo ở dòng newsock.Bind(iep); Only one usage of each socket address (protocol/network address/port) is normally permitted

  6. Dung k2b said

    thưa thầy em có một thắc mắc nhỏ về môn lập trình mạng mong thầy giải thích hộ em

    em đang làm một chương trình thi chắc nghiệm trực tuyến

    vì vậy mỗi câu hỏi có độ dài khác nhau
    em không hiểu vì sao khi gửi câu hỏi mã hóa đó đưới dạng asci lại bị mất dữ liệu
    (câu hỏi mà client nhận được không hoàn chỉnh) ví dụ
    câu hỏi là
    vị vua nào có công khai sinh ra nước Việt Nam ta ?
    a. Lý Thái Tổ
    b. Lý Công Uẩn
    c. các vua Hùng
    d. một phương án khác

    và client chỉ nhận được là
    vị vua nào có công khai sinh ra nước Việt Nam ta ?
    a. Lý Thái Tổ
    b. Lý Công Uẩn
    c.

  7. Võ Văn Hải said

    Có 2 vấn đề: (1) có thể trường chứa dữ liệu của em không đủ rộng. (2) Buffer chưa được flush.

  8. quay said

    thầy có code gửi nhận hình ảnh trong c# không cho e xin tham khảo với.thank thầy

  9. Silverhand1990 said

    Thưa thầy em cũng có câu hỏi như bạn Nogaez. Nhưng mà cho em hỏi là mình đóng thread vậy không cần đóng kết nối socket đã tạo đấy ạ. Và nếu cần thì phải làm thế nào ạ. Em cám ơn thầy!!!

  10. thuhuong said

    ai có thể giúp em được không ah thu 7 nay phai nôp bai rồi ma em chưa làm gì được hết hà!!:hãy tạo ra một lớp phanso tinh toán cộng trừ nhân chia,sau đó tạo ra một form nhập tử và mẫu ở phân số thứ nhất và phân số thưa 2 sau đó tạo một nút tính cộng khi nhấn vào nút cộng thì nó se hieur là dùng phương thức cộng ở lớp phanso và gán kq cho o textket qua?

  11. Thầy có thể giúp em làm cách nào để trên 1 máy có thể vừa làm server vừa làm client không ạ hoặc cách khắc phục lỗi như bạn vovanduy.
    ————————————————————————————————————————————————————–
    Thầy giúp dùm em,em làm bài tập chat giữa client,em thấy tài liệu nào cũng như code trên,nhưng khi em chạy và nhấn button listen thì báo ở dòng newsock.Bind(iep); Only one usage of each socket address (protocol/network address/port) is normally permitted
    —————————————————————————————————————————————————————
    em chân thành cảm ơn

  12. em quên chưa lấy notify

  13. Võ Văn Hải said

    public class RationalNumber
        {
            private int numerator, denominator;
    
            //-----------------------------------------------------------------
            //  Constructor: Sets up the rational number by ensuring a nonzero
            //  denominator and making only the numerator signed.
            //-----------------------------------------------------------------
            public RationalNumber(int numer, int denom)
            {
                if (denom == 0)
                    denom = 1;
    
                // Make the numerator "store" the sign
                if (denom < 0)
                {
                    numer = numer * -1;
                    denom = denom * -1;
                }
    
                numerator = numer;
                denominator = denom;
    
                reduce();
            }
    
            //-----------------------------------------------------------------
            //  Returns the numerator of this rational number.
            //-----------------------------------------------------------------
            public int Numerator
            {
                get
                {
                    return numerator;
                }
            }
    
            //-----------------------------------------------------------------
            //  Returns the denominator of this rational number.
            //-----------------------------------------------------------------
            public int Denominator
            {
                get
                {
                    return denominator;
                }
            }
    
            //-----------------------------------------------------------------
            //  Returns the reciprocal of this rational number.
            //-----------------------------------------------------------------
            public RationalNumber reciprocal()
            {
                return new RationalNumber(denominator, numerator);
            }
    
            //-----------------------------------------------------------------
            //  Adds this rational number to the one passed as a parameter.
            //  A common denominator is found by multiplying the individual
            //  denominators.
            //-----------------------------------------------------------------
            public RationalNumber add(RationalNumber op2)
            {
                int commonDenominator = denominator * op2.Denominator;
                int numerator1 = numerator * op2.Denominator;
                int numerator2 = op2.Numerator * denominator;
                int sum = numerator1 + numerator2;
    
                return new RationalNumber(sum, commonDenominator);
            }
    
            //-----------------------------------------------------------------
            //  Subtracts the rational number passed as a parameter from this
            //  rational number.
            //-----------------------------------------------------------------
            public RationalNumber subtract(RationalNumber op2)
            {
                int commonDenominator = denominator * op2.Denominator;
                int numerator1 = numerator * op2.Denominator;
                int numerator2 = op2.Numerator * denominator;
                int difference = numerator1 - numerator2;
    
                return new RationalNumber(difference, commonDenominator);
            }
    
            //-----------------------------------------------------------------
            //  Multiplies this rational number by the one passed as a
            //  parameter.
            //-----------------------------------------------------------------
            public RationalNumber multiply(RationalNumber op2)
            {
                int numer = numerator * op2.Numerator;
                int denom = denominator * op2.Denominator;
    
                return new RationalNumber(numer, denom);
            }
    
            //-----------------------------------------------------------------
            //  Divides this rational number by the one passed as a parameter
            //  by multiplying by the reciprocal of the second rational.
            //-----------------------------------------------------------------
            public RationalNumber divide(RationalNumber op2)
            {
                return multiply(op2.reciprocal());
            }
    
            //-----------------------------------------------------------------
            //  Determines if this rational number is equal to the one passed
            //  as a parameter.  Assumes they are both reduced.
            //-----------------------------------------------------------------
            public override bool Equals(object obj)
            {
                RationalNumber op2 = (RationalNumber)obj;
                return (numerator == op2.Numerator &&
                         denominator == op2.Denominator);
            }
            
    
            //-----------------------------------------------------------------
            //  Returns this rational number as a string.
            //-----------------------------------------------------------------
            public String toString()
            {
                String result;
    
                if (numerator == 0)
                    result = "0";
                else
                    if (denominator == 1)
                        result = numerator + "";
                    else
                        result = numerator + "/" + denominator;
    
                return result;
            }
    
            //-----------------------------------------------------------------
            //  Reduces this rational number by dividing both the numerator
            //  and the denominator by their greatest common divisor.
            //-----------------------------------------------------------------
            private void reduce()
            {
                if (numerator != 0)
                {
                    int common = gcd(Math.Abs(numerator), denominator);
    
                    numerator = numerator / common;
                    denominator = denominator / common;
                }
            }
    
            //-----------------------------------------------------------------
            //  Computes and returns the greatest common divisor of the two
            //  positive parameters. Uses Euclid's algorithm.
            //-----------------------------------------------------------------
            private int gcd(int num1, int num2)
            {
                while (num1 != num2)
                    if (num1 > num2)
                        num1 = num1 - num2;
                    else
                        num2 = num2 - num1;
    
                return num1;
            }
        }
    
  14. purplepries@yahoo.com said

    thưa thầy em có câu hỏi, em sử dụng hàm SerializeData của thầy,
    class packet
    {
    public int number;
    public string temp;
    }

    nhưng em bị lỗi này ngay dòng phía sau đây,
    byte[] temp= SerializeData(packet);

    An unhandled exception of type ‘System.Runtime.Serialization.SerializationException’ occurred in mscorlib.dll

    mong thầy chỉ dẫn cho em

  15. Tâm said

    Chào thầy!

    Thầy cho em hỏi cái số 5656 là gì vậy thầy?

    Cám ơn thầy!

  16. Xac Chet said

    Chào thầy !
    _xin thầy hướng dẫn về lập trình mạng
    chia sẻ thư mục và tập tin với nhau , giữa server / client
    __hướng dẫn cụ thể bằng C#.
    .Cám ơn thầy!!

  17. Manh Ha said

    Nếu thay DataTable trên bằng một object (tạo class cho object này) thì lỗi trong hàm SerializeData(Object o) thầy ạ, ý em định truyền 1 object thì lại không được, mặc dù DataTable là một object. Mong thầy giải đáp.

  18. Võ Văn Hải said

    Trong lớp đó em phải thêm attribute Serialize.

  19. VanPhuong said

    Em chào thay.
    Em tên Phương
    Em có làm một bài tập C#
    Kết nôi CSDL như sau thày có thể giúp em được không.
    Server: combobox load server từ hệ thống
    DataBase: combobox load Data khi chọn server
    User TextBox nhập tên đang nhập(vd: sa)
    pass: Nhâp pass đăng nhập
    button: connect

  20. VanPhuong said

    Chào thầy.
    Em đang học C# có một bài tập thầy hướng dẫn giúp em.

    em muốn tạo một form nhu sau.
    server : Combobox. combobox này load DataSource vd như em load (local) hoăc .\\SQLExpress
    DataBase Combox. combobox này load tên CSDL trong DataSource
    user Textbox textbox này nhập tên đang nhập là sa
    pass Textbox textbox nay nhâp pass cho user
    button connect ket noi voi CSDL

    Mong thầy giúp cho em.
    Xin chân thành cám on thầy.

  21. Thằng Mất Dạy said

    mbobox. combobox này load DataSource vd như em load (local) hoăc .\\SQLExpress
    DataBase Combox. combobox này load tên CSDL trong DataSource
    user Textbox textbox này nhập tên đang nhập là sa
    pass Textbox textbox na

  22. thầy cho em hỏi: em làm game cờ vua chơi qua mạng client bằng wpf, em tạo mạng client chat được với nhau.nhưng khi ấn new game thì nó bị nỗ doi hàm Case không nhận ra câu lệnh mà . em gắn giá tri khi ấn “new game” =sendPacket(“N”). thầy có thể sửa cho em được khôg ah
    private void SendData(IAsyncResult iar)
    {
    Socket remote = (Socket)iar.AsyncState;
    int sent = remote.EndSend(iar);
    }
    public void sendPacket(string packet)
    {
    try
    {
    byte[] Sent = Encoding.ASCII.GetBytes(packet);
    socket.BeginSend(Sent, 0, Sent.Length, 0, new AsyncCallback(SendData), socket);
    }
    catch (SocketException e)
    {
    state = StateConnection.Breaken;
    }
    }
    private void ReceiveData()
    {
    int recv;
    string packData = “”;
    try
    {
    while (true)
    {
    recv = socket.Receive(data);
    packData = Encoding.ASCII.GetString(data, 0, recv);
    if (packData.ToString().Equals(“”))
    return;

    if (packData.StartsWith(“@”))
    {
    AddChatMessage(packData.ToString());
    }

    else
    {
    switch (packData.ToString())
    {
    case “N”:
    {
    SetStatusMessage1(“Người chơi đã sẵn sàng…!”);
    batdau();
    } break;
    case “X”:
    {
    SetStatusMessage1(“Người chơi đã thoát game…!”);
    //oat();
    state = StateConnection.Breaken;
    } break;

    }
    }
    }
    }
    catch (Exception ex)
    {
    SetStatusMessage(“ReceiveData ” + ex.Message.ToString());
    }
    }

  23. Lê Hoàng Phúc said

    Thưa thầy cho em hỏi.
    Em làm hệ thống chấm điểm tự động. Máy chủ và các máy con. mình tạo tcplistener ở server và tcpclient ở máy con. tạo các thread ở máy chủ mỗi khi bắt được máy con. máy chủ và các máy con luôn kết nối với nhau. Làm sao mình có thể chuyển file từ máy con lên máy chủ và ngược lại ah? có phải là phải dùng riêng một cổng port cho chuyển dữ liệu ko ah?
    Mong nhận được trả lời sớm của thầy!!

  24. hoangplait said

    Thầy cho em hỏi, phương thưc gửi và nhận dữ liệu web server la thế nào hả thầy

  25. Lang said

    Cho e hỏi minhg muốn truyền table access được truy vấn ở server qua client thì làm sao ạ! Có code tham khảo với ạ!

  26. danh said

    thay cho em hoi
    muon truyen mot table tu winform sang web form co the lam duoc khong .

    nho thay giup de

  27. Minh Tuấn said

    Thưa thầy, em đã dùng 2 phương thức của thầy để gửi và nhận datatable. Tuy nhiên em thường xuyên bắt được lỗi:
    “The input stream is not a valid binary format. The starting contents (in bytes) are: 6D-65-3E-0D-0A-20-20-20-20-3C-2F-63-61-72-64-74-79 …”
    Lỗi này xuất hiện thường xuyên, thầy có thể cho em biết lý do và cách khắc phục được không ạ, xin cảm ơn thầy.

  28. Võ Văn Hải said

    Thầy chưa gặp lỗi nầy nên cũng chưa biết sao nữa.

  29. Đinh Thị Luận said

    Em chào Thầy.
    Em Thưa Thầy Thầy có code về Xây dựng ứng dụng hỗ trợ giao bài tập thực hành từ máy giáo viên tới máy sinh viên, thu bài từ máy sinh viên về máy giáo viên. Không Thầy cho em xin với ạ
    Em Cảm ơn Thầy!

  30. Chào thầy!
    em có làm bài Remote Desktop em muốn bên Server copy (ấn tổ hợp phím Ctrl+C hoặc kích chuột phải chọn copy) một file (.doc, .jpg, forder) bên Client sau đó Paste (Ctrl+V) lưu vào thư mục trong Server. Mong thầy xem giúp

  31. Lê Đức Quang said

    Chào thầy, em hiện đang học ở lớp Se111 bộ môn java của thầy. Em gặp phải 1 vấn đề thế này:
    TcpListener listener = new TcpListener(address, 8080);
    listener.Start();
    socket = listener.AcceptSocket();
    khi socket đang chờ kết nối từ client thì form Server không thể thao tác được gì hết(đơ). Cái này do em để socket trong 1 cái timer để có thể nhận kết nối từ nhiều máy client. Vậy có cách nào khắc phục được tình trạng này không vậy thầy?

  32. Võ Văn Hải said

    Dùng Thread để giải quyết vấn đề này nhé!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: