C# – Lập trình Socket giao tiếp TCP client/server

Trong lập trình, Socket là một API (Application Programming Interface) cung cấp các phương thức để giao tiếp thông qua mạng. Trước khi bắt đầu tìm hiểu và viết một ví dụ đơn giản về socket, bạn có thể tham khảo bài  viết “Networking – Một số khái niệm cơ bản“ để có cái nhìn sơ lược về những khái niệm cơ bản trong lập trình mạng.

Các lớp .Net cơ bản trong lập trình mạng

Các lớp này được cung cấp trong hai namespace System.NetSystem.Net.Sockets. Hai namespace này chứa rất nhiều lớp dùng trong lập trình mạng, nhưng trong phạm vi bài viết ta chỉ quan tâm đến các lớp sau::

Class Namespace Desciption
IPAddress System.Net Provides an Internet Protocol (IP) address.
IPEndPoint System.Net Represents a network endpoint as an IP address and a port number.
TcpListener System.Net.Sockets Listens for connections from TCP network clients.
Socket System.Net.Sockets Implements the Berkeley sockets interface.
TcpClient System.Net.Sockets Provides client connections for TCP network services.
NetworkStream System.Net.Sockets Provides the underlying stream of data for network access.

Kết nối Server-Client với TCP/IP

Khi được chạy, server cần được xác định rõ địa chỉ IP và sẽ “lắng nghe” trên một port cụ thể. Server sẽ nằm trong trạng thái này cho đến khi client gửi đến một yêu cầu kết nối. Sau khi được server chấp nhận, một connection sẽ hình thành cho phép server và client giao tiếp với nhau.

Cụ thể hơn, các bước tiến hành trên server và client mà ta cần thực hiện sử dụng giao thức TCP/IP trong C# (có thể chạy server và client trên cùng một máy):

Server:

  1. Tạo một đối tượng System.Net.Sockets.TcpListener để bắt đầu “lắng nghe” trên một cổng cục bộ.
  2. Đợi và chấp nhận kết nối từ client với phương thức AccepSocket(). Phương thức này trả về một đối tượng System.Net.Sockets.Socket dùng để gửi và nhận dữ liệu.
  3. Thực hiện giao tiếp với client.
  4. Đóng Socket.

Thông thường quy trình này sẽ được đặt trong một vòng lặp (lặp lại bước 2) để chấp nhận nhiều kết nối cùng lúc (sử dụng Thread) hoặc các kết nối lần lượt.

Client:

  1. Tạo một đối tượng System.Net.Sockets.TcpClient
  2. Kết nối đến server với địa chỉ và port xác định với phương thức TcpClient.Connect()
  3. Lấy luồng (stream) giao tiếp bằng phương thức TcpClient.GetStream().
  4. Thực hiện giao tiếp với server.
  5. Đóng luồng và socket.

Quy trình này có thể được minh họa theo mô hình sau:

Example v1: Gửi nhận dữ liệu dạng byte[]

Lớp NetworkStream và  Socket cung cấp các phương thức gửi và nhận dữ liệu dạng mảng byte. Vì vậy bạn cần phải thực hiện các bước chuyển đổi dữ liệu sang dạng byte và ngược lại. Trong ví dụ sau tôi sử dụng dữ liệu dạng văn bản ASCII trong console, và dùng các lớp trong namespace System.Text để chuyển đổi. Có hai cách bạn có thể áp dụng:

–       Dùng các static property của lớp abstract System.Text.Encoding với các phương thức GetString() và GetBytes().

–       Tạo đối tượng có kiểu XXXEncoding (thừa kế từ System.Text.Encoding). Ví dụ: UTF8Encoding, ASCIIEncoding,…

Một ví dụ gửi nhận dữ liệu đơn giản nhất sử dụng TCPListener, Socket (phía server) và TCPClient, NetworkStream (phía client) dạng mảng byte với địa chỉ loop-back 127.0.0.1 trên cùng một máy.

Tạo hai dự án console là Y2Server và Y2Client với nội dung sau:

Y2Server.cs (v1):

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;

public class Y2Server {

    private const int BUFFER_SIZE=1024;
    private const int PORT_NUMBER=9999;

    static ASCIIEncoding encoding=new ASCIIEncoding();

    public static void Main() {
        try {
            IPAddress address = IPAddress.Parse("127.0.0.1");

            TcpListener listener=new TcpListener(address,PORT_NUMBER);

            // 1. listen
            listener.Start();

            Console.WriteLine("Server started on "+listener.LocalEndpoint);
            Console.WriteLine("Waiting for a connection...");

            Socket socket=listener.AcceptSocket();
            Console.WriteLine("Connection received from " + socket.RemoteEndPoint);

            // 2. receive
            byte[] data=new byte[BUFFER_SIZE];
            socket.Receive(data);

            string str=encoding.GetString(data);

            // 3. send
            socket.Send(encoding.GetBytes("Hello "+str));

            // 4. close
            socket.Close();
            listener.Stop();

        }
        catch (Exception ex) {
            Console.WriteLine("Error: " + ex);
        }
        Console.Read();
    }
}

Y2Client.cs (v1):

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Sockets;

public class Y2Client{

    private const int BUFFER_SIZE=1024;
    private const int PORT_NUMBER=9999;

    static ASCIIEncoding encoding= new ASCIIEncoding();

    public static void Main() {

        try {
            TcpClient client = new TcpClient();

            // 1. connect
            client.Connect("127.0.0.1",PORT_NUMBER);
            Stream stream = client.GetStream();

            Console.WriteLine("Connected to Y2Server.");
            Console.Write("Enter your name: ");

            string str = Console.ReadLine();

            // 2. send
            byte[] data=encoding.GetBytes(str);

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

            // 3. receive
            data =new byte[BUFFER_SIZE];
            stream.Read(data,0,BUFFER_SIZE);

            Console.WriteLine(encoding.GetString(data));

              // 4. Close
stream.Close();
            client.Close();
        }

        catch (Exception ex) {
            Console.WriteLine("Error: " + ex);
        }

        Console.Read();
    }
}

Để kiểm tra ví dụ, bạn chạy server trước, cửa sổ console của server sẽ hiển thị:

Server started on 127.0.0.1:9999

Waiting for a connection…

Tiếp đến cho chạy client, nếu kết nối thành công, server sẽ hiển thị thêm dòng thông báo tương tự như sau:

Connection received from 127.0.0.1:2578

Chuyển qua cửa sổ console của client và nhập tên của bạn vào, nếu nhận được dữ liệu, server sẽ gửi trả lại dòng thông điệp “Hello [Your Name]”

Connected to Y2Server.

Enter your name: Yin Yang

Hello Yin Yang

Ngay sau bước này, cả server và client đều thực hiện đóng kết nối.

Example v2: Sử dụng StreamReader và StreamWriter

Sẽ tiện lợi hơn nếu ta sử dụng StreamReader và StreamWriter để gửi nhận dữ liệu mà không cần bước chuyển đổi qua lại mảng byte. Các đối tượng StreamReader và StreamWriter có thể được khởi tạo trực tiếp từ NetworkStream. Thuộc tính AutoFlush của StreamWriter thường được đặt là true để tự động gửi dữ liệu mà không cần đợi bộ đệm đầy hoặc bạn phải gọi thủ công phương thức Flush().

Ví dụ sau sử dụng vòng lặp để thực hiện gửi nhận dữ liệu liên tục giữa server/client cho đến khi client nhập vào chuỗi “exit”:

 Y2Server.cs (v2):

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class Y2Server {

    private const int BUFFER_SIZE=1024;
    private const int PORT_NUMBER=9999;

    static ASCIIEncoding encoding=new ASCIIEncoding();

    public static void Main() {
        try {
            IPAddress address = IPAddress.Parse("127.0.0.1");

            TcpListener listener=new TcpListener(address,PORT_NUMBER);

            // 1. listen
            listener.Start();

            Console.WriteLine("Server started on "+listener.LocalEndpoint);
            Console.WriteLine("Waiting for a connection...");

            Socket socket=listener.AcceptSocket();
            Console.WriteLine("Connection received from " + socket.RemoteEndPoint);

            var stream = new NetworkStream(socket);
            var reader=new StreamReader(stream);
            var writer=new StreamWriter(stream);
            writer.AutoFlush=true;

            while(true)
            {
                // 2. receive
                string str=reader.ReadLine();
                if(str.ToUpper()=="EXIT")
                {
                    writer.WriteLine("bye");
                    break;
                }
                // 3. send
                writer.WriteLine("Hello "+str);
            }
            // 4. close
            stream.Close();
            socket.Close();
            listener.Stop();
        }
        catch (Exception ex) {
            Console.WriteLine("Error: " + ex);
        }
        Console.Read();
    }
}

Y2Client.cs (v2):

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Sockets;

public class Y2Client{

    private const int BUFFER_SIZE=1024;
    private const int PORT_NUMBER=9999;

    static ASCIIEncoding encoding= new ASCIIEncoding();

    public static void Main() {

        try {
            TcpClient client = new TcpClient();

            // 1. connect
            client.Connect("127.0.0.1",PORT_NUMBER);
            Stream stream = client.GetStream();

            Console.WriteLine("Connected to Y2Server.");
            while(true)
            {
                Console.Write("Enter your name: ");

                string str = Console.ReadLine();
                var reader=new StreamReader(stream);
                var writer=new StreamWriter(stream);
                writer.AutoFlush=true;

                // 2. send
                writer.WriteLine(str);

                // 3. receive
                str=reader.ReadLine();
                Console.WriteLine(str);
                if(str.ToUpper()=="BYE")
                    break;
            }
            // 4. close
            stream.Close();
            client.Close();
        }

        catch (Exception ex) {
            Console.WriteLine("Error: " + ex);
        }

        Console.Read();
    }
}

Bạn  chạy ví dụ này giống như ví dụ đầu tiên và gõ ‘exit’ vào client để thoát ứng dụng.

 

Cập nhật: (27/9/2010)

Thay đổi BUFFER_SIZE từ 100 lên 1024. (cảm ơn bạn hoang)

https://yinyangit.wordpress.com

 

135 thoughts on “C# – Lập trình Socket giao tiếp TCP client/server

  1. Những ví dụ như vậy cũng tương tự như hai ví dụ trên, bạn chỉ cần viết thêm vài dòng vào phần xử lý của server. Bạn có thể đưa ra một yêu cầu có nhiều khác biệt hơn, mình sẽ thực hiện và post để bạn tham khảo.

  2. Chào bạn,
    Đối tượng TCPListener không hỗ trợ event nào. Như bạn thấy, phương thức StreamReader.ReadLine() sẽ chờ nhận dữ liệu từ client, sau đó mới thực hiện các mã lệnh bên dưới. Bạn có thể không cần event hoặc tự viết một event kích hoạt ngay sau phương thức ReadLine() này.

    Một giải pháp khác là bạn có thể tham khảo là sử dụng phương thức BeginAcceptSocket() với tham số là một delegate AsyncCallback tham chiếu đến phương thức xử lý nhận kết nối. Bạn có thể xem ví dụ tại: http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.beginacceptsocket.aspx

  3. Hi, bài của bạn khá hay. Mình sắp thi môn lập trình mạng với C#. bạn có thể làm giúp mình dạng bài tập này không:
    hãy xây dựng chương trình chat giữa 2 máy. Sau đó: Server lắng nghe yêu cầu kết nối từ client. Sau khi server đã chấp nhận kết nối, thì cả client và server đều được phép nhận thoải mái các thông điệp.
    nếu được bạn có thể làm ở cả console và winfrom cho mình tham khảo, rất mong sớm được hồi âm vì 2 ngày nữa mình thi rui!

  4. Hi thanhk bạn nhé! mình sẽ tham khảo cách làm của bạn để hoàn thiện bài của mình. Còn vấn đề mà chuyển từ Console sang windows Form mình không biết cách chuyển như thế nào, bạn có linhk nào hướng dẫn cách chuyển không? có thể gửi cho mình xem được chứ?

  5. Việc chuyển từ Console sang Windows Form chỉ phụ thuộc vào bạn có biết lập trình Windows Form không thôi. Tùy theo mục đích thiết kế của bạn mà sẽ thay thế các Console.ReadLine(), WriteLine(),… thành các control thích hợp. Bạn có thể dành một vài tiếng thực hành một số bài tập về Windows Form là có thể làm được. Trong lúc thiết kế nếu gặp rắc rối mình sẽ giải quyết giúp bạn.

  6. Pingback: C# – Lập trình Socket giao tiếp TCP client/server (via YinYang’s Programming Blog) « Chanmodoi's Blog

  7. Bạn ơi sao ví dụ đầu tiên nó gửi dữ liệu chẳng chính xác gì cả ví dụ Client nhập tên Hoang server trả về chuỗi là helloHoang\………………………….
    là sao nhỉ phải chăng tham số truyền tại hàm “write( , , ), read( , , )” chưa đúng???

  8. Không mình đang nói ở ví dụ đầu tiên đó:”Example v1: Gửi nhận dữ liệu dạng byte[]”
    Bạn xem lại nhé! (Client nhập tên Hoang server trả về chuỗi là helloHoang
    0…)
    còn set value AutoFlush=true; là ở “Example v2: Sử dụng StreamReader và StreamWriter” chứ

    • Xin lỗi bạn vì câu trả lời sơ sài của mình ở trên. Sau khi kiểm tra lại ví dụ 1 thì mình nhận thấy dữ liệu mà server (Socket) gửi đi lớn hơn BUFFER_SIZE ở client. Cụ thể BUFFER_SIZE mình chỉ đặt là 100 byte còn độ lớn dữ liệu gửi đi là 106 byte.
      Bạn thử khắc phục lại bằng cách đặt BUFFER_SIZE lớn hơn, khoảng 1024 (1 KB). Trong trường hợp dữ liệu lớn và khó xác định kích thước, bạn có thể dùng vòng lặp để đọc dữ liệu nhiều lần.

      Cảm ơn bạn đã giúp phát hiện lỗi và hi vọng bạn có thể hồi âm lại để mình biết vấn đề của bạn có được giải quyết chưa.

  9. Bài của bạn sửa như vậy vẫn chưa ổn bạn Test kỹ chưa? mình sửa như vậy nó vẫn gửi linh tinh lắm do mình mới tìm hiểu UDP giờ tiếp tục tìm hiểu TCP lên vẫn chưa giải quyết được bạn ạ.

    • Bạn có thể thử lại phương thức encoding.GetString() của client. Phương thức này có thể truyền thêm một vài tham số xác định độ dài dữ liệu cần lấy. Độ dài dữ liệu này được lấy từ giá trị trả về của phương thức NetwordStream.Read().
      Vấn đề của bạn mình test mà vẫn chưa gặp phải nên chưa xác định được, có thời gian rành mình sẽ coi lại cẩn thận.

  10. mình đang viết chương trình về lập trình mạng, với đề tài là Remoteshell : điều khiển máy tính từ xa bằng command line nhưng mình không biết cách để chuyển dữ liệu vào trong hệ thố để thực thi các lệnh trên máy client. Xin bạn cho một vài gợi ý về các phương thức hay namespace nào đó …

    • Nếu có thể liên lạc được với máy khách thì bạn chỉ cần cài chương trình tương ứng trên máy khách để thực thi các lệnh từ remote. Bạn có thể tách riêng ra 1 thư viện và từ chuỗi lệnh nhận được từ remote, sử dụng if else hay switch case để thực thi các phương thức trong thư viện đó.

  11. Cảm ơn bài viết rất hay của bạn 😀 . mình đã thử chuyển sang winform , nhưng khi tạo 1 form là server với 1 button có chức năng start listen , khi mình click vào button thì form server của mình bị not responding , tuy nhiên client vẫn cso thể kết nối đến được . còn khi client ngừng kết nối thì form server mới hoạt động trở lại , tức là sẽ hiển thị các thông tin lên 1 textbox mình tạo . bạn cso thể giải quyết vấn đề này đc không.
    Cảm ơn !

  12. chào bạn , cho mình hỏi tiếp 😀
    làm thế nào để khi server gửi bất cứ thông điệp nào đi thì tại phía client cũng nhận được và đẩy lên 1 textbox (mình đang làm bên winform) . Mình ko biết cho nó vào sự kiện nào cả :-s
    Thanks !

  13. Khi tạo client bạn nên sử dụng một Thread để nhận được các thông điệp từ server. Tạo một phương thức chạy liên tục trong Thread đó và nhận thông điệp với StreamReader như ví dụ trong bài. Khi nhận được dữ liệu thì bạn chỉ cần gán nó lên TextBox thôi.

  14. bạn ơi cho mình tại sao khi mình chạy file y2client.cs thì nó báo lỗi ở màn hình console:
    Error: system.net.socketexception: no connection could be made because the target machine actively refused it 127.0.0.1:9999 …

  15. anh có thể gói gọn các hàm lại thành các phương thức được không anh, vd như: connect, send, receive… em chuyển sang winform mà không biết xử lý sao cho khi nhấn vào một button thì sẽ gửi được qua server một thông điệp

  16. Bạn có thể viết chỉ cho tôi cách validate dữ liệu ngay trên server rồi truyền về client. Vi du nhu + – * /. Nếu dữ liệu từ client truyền lên không phải số thì báo invalid. Tôi gặp khó khăn ở chỗ này. Cam ơn bạn.

    • Việc này không liên quan gì tới lập trình mạng hay mô hình client/server. Bạn chỉ cần viết và gọi các phương thức cần thiết là được. Khi lấy được dữ liệu từ client kiểu string, bạn chỉ cần chuyển qua kiểu số để kiểm tra thôi.

  17. Bạn có thể tạo thêm 1 extension method cho lớp Socket như sau:

    static class SocketExtensions
    {
      public static bool IsConnected(this Socket socket)
      {
        try
        {
          return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
        }
        catch (SocketException) { return false; }
      }
    }
    

    Xem: Instantly detect client disconnection from server socket
    Đây là cách kiểm tra tổng quát, đối với trường hợp này thì bạn cần thêm khối try catch khi đọc dữ liệu từ client với ReadLine(), vì phương thức này sẽ ngừng vòng lặp while cho đến khi đọc được dữ liệu từ client (hoặc client ngắt kết nối).

  18. Em có app được viết bằng VB6, khi app đó bị ngắt kết nối thì ko tài nào mà bắt dc Exception. Em test trên local thì được nhưng cứ cho chạy trên server thì thua. Em cũng có nhận dc góp ý là tạo 1 timer mỗi lần client gửi packet thì reset timer về 0 và đếm lại nếu timer qúa giá trị thì sẽ coi như client bị ngắt kết nối. Nhưng thuật toán này em ko làm được vì trên cùng 1 luồng em ko tạo thể vừa đếm vừa chờ packet. Còn nếu 2 luồng thì em ko biết cách hủy luồng A từ luồng B. Anh giúp em cái này được không. Còn cái đoạn code anh gửi cho em thì em lại bị thế này. Khi có n client connect tới và khi có 1 client bị ngắt thì đồng loạt các client khác cũng bị server cho ngắt theo. Em sẽ thử lại xem được không

  19. // phần này khai báo trên class
    public TcpListener server;
    public static TcpClient client;
    // hàm này sẽ chạy server
    public void maychu()
            {
                try
                {
                    //khởi tạo server
                    IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 2011);
                    server = new TcpListener(ipep);
                }
                catch (Exception exs)
                {
                    Console.WriteLine("Co loi khi tao server "+exs.Message);
                }
                try
                {
                    //tạo 1 luồng khác
                    ThreadStart th = new ThreadStart(connect);
                    Thread objthread = new Thread(th, 2048);
                    //chạy luồng vừa tạo
                    objthread.Start();
                }
                catch (Exception ext)
                {
                    Console.WriteLine("Co loi khi tao luong moi " + ext.Message);
                }
                 
                //ThreadPool.QueueUserWorkItem(new WaitCallback(connect));
            }
    // hàm connect để chứa 1 client connect đến server
              public void connect()
            {
                int check = 0;
                string packetfirt = "";
                string packet = "";
                string name = "";
                string ip = "";
                string id = "";
                string key = "";
                //TimerCallback objcallback = new TimerCallback(tick);
                //lock (syncobj)
                {
                    try
                    {
                        if (bool15)
                        {
                            Console.WriteLine("Da chay luong moi");
                        }
                        // mở cổng nghe mới
                        server.Start(5000);
                        if (bool15)
                        {
                            Console.WriteLine("Da chay server, dang cho client connect .....");
                        }
                        //chờ client kết nối tới
                        TcpClient client;
                        client = server.AcceptTcpClient();
                        //client kết nối tới thì chạy timer đếm
                        client.NoDelay = true;
                        client.Client.ReceiveTimeout = 10000;
                        name = client.Client.RemoteEndPoint.ToString();
                        
                        if (arr.Count > 0)
                        {
                            if (arr.Find(ip).Value.ToString() == ip)
                            {
                                if (bool15)
                                {
                                    Console.WriteLine("client " + ip + " da bi chan lai");
                                }
                                client.Close();
                                return;
                            }
                        }
                        Console.WriteLine(client.Client.RemoteEndPoint.ToString() + " ket noi toi");
                        //có client kết nối tới thì tạo 1 luồng mới để cho 1 client khác kết nối tới
                        lock (syncobj1)
                        {
                            //tạo 1 luồng khác
                            try
                            {
                                ThreadStart th = new ThreadStart(connect);
                                Thread objthread = new Thread(th, 2048);
                                //chạy luồng vừa tạo
                                objthread.Start();  
                            }
                            catch (Exception exth)
                            {
                                Console.WriteLine("Co loi khi tao luong in connect " + exth);
                            }
                            //ThreadPool.QueueUserWorkItem(new WaitCallback(connect));
                            if (number_count >= 7000)
                            {
                                client.Close();
                                return;
                            }
                        }
                        StreamReader sr;
                        StreamWriter wr;
                        lock (syncobj4)
                        {
                            //đọc packet từ client đến server
                            sr = new StreamReader(client.GetStream());
                            wr = new StreamWriter(client.GetStream());
                        }
                        //client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1);
                        
                        while (true)
                        {
                            
                            try
                            {
                                packet = null;
                                //nếu chưa nhận packet nào thì nhận 1 packet sau đó bỏ qua ko đọc nữa mà chuyển xuống vòng while ở dưới(vòng while ở dưới để kiểm tra client có bị ngắt kết nối hay ko)
                                if (packetfirt == "")
                                {
                                    packet = sr.ReadLine();
                                    if (packet == null)
                                        client.Close();
                                }
                                else
                                {
                                    lock (syncobj6)
                                    {
                                        while (true)
                                        {
                                            Thread.Sleep(1000);
                                            try
                                            {
                                             //gửi packet đến server
                                                sendpacket(wr, "123456", key);
                                                // kiểm tra xem client có còn connect không
                                                if (!SocketConnected(client))
                                                {
                                                    //thực hiện tính toán
                                                    client.Close();
                                                    return;
                                                }
                                            }
                                            catch
                                            {
                                                //thực hiện tính toán
                                                client.Close();
                                                return;
                                            }
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                //thực hiện tính toán
                                    client.Close();
                                    return
                            }
                            if (packet == null)
                            {
                                check++;
                                if (packetfirt != "")
                                {
                                    //thực hiện tính toán
                                    client.Close();
                                    return;
                                }
                            }
    
                            if (packet != null && packet != "")
                            {
                                if (bool10)
                                {
                                    client.Close();
                                    return;
                                }
                                lock (syncobj2)
                                {
                                    try
                                    {
                                        id = packet.Substring(0, 1);
                                    }
                                    catch
                                    {
                                        return;
                                    }
                                    //tính toán lấy thông tin key và giải mã
                                    packet = decrypt(packet, key);
                                    if (bool15)
                                    {
                                        Console.WriteLine(name + " gui packet: " + packet);
                                    }
                                    // gửi lại packet đến client
                                    if (process(wr, packet, key, id, ref packetfirt) == false)
                                    {
                                        check++;
                                    }
                                    
                                    //Timer stateTimer = new Timer(objcallback, objtimer, 0, 5000);
                                    
                                    if (check > 20)
                                    {
                                        if (bool8)
                                        {
                                            arr.AddLast(ip);
                                            if (bool15)
                                            {
                                                Console.WriteLine(name + " da bi khoa");
                                            }
                                            //client.Close();
                                            //return;
                                        }
                                    }
                                    if (client.Connected == false)
                                    {
                                        try
                                        {
                                            if (bool15)
                                            {
                                                Console.WriteLine(name + " ngat ket noi");
                                            }
                                        }
                                        catch
                                        { }
                                        try
                                        {
                                            lock (syncobj3)
                                            {
                                                
                                                Console.WriteLine("da tru 1 nick online");
                                                try
                                                {
                                                    client.Close();
                                                    //objthread.Abort();
                                                    //objthread.Join();
                                                }
                                                catch { }
                                                //return;
                                            }
                                        }
                                        catch (Exception exa)
                                        {
                                            ghifile(exa.Message);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (IOException ex)
                    {
                        //Console.WriteLine("ex" + ex);
                        try
                        {
                            if (bool15)
                            {
                                Console.WriteLine(name + " bi ngat ket noi");
                            }
                        }
                        catch (Exception ex2)
                        {
                            //Console.WriteLine( "Ex2" +ex2.Message);
                        }
                    }
                }
                /*ngắt kết nối
                try
                {
                    if (packetfirt != "")
                    {
                        //thực hiện tính toán
                        client.Close();
                        return;
                    }
                }
                catch
                {
                }*/
            }
    //kiểm tra trạng thái client
    public bool SocketConnected(TcpClient s)
            {
                lock (syncobj5)
                {
                    try
                    {
                        return !(s.Client.Poll(1, SelectMode.SelectRead) && s.Client.Available == 0);
                    }
                    catch (Exception ex)
                    {
                        return false;
                    }
                }
            }
    

    Code của em chỉ có thế thôi không có gì phức tạm lắm hàm connect() chỉ chạy đi chạy lại. Ở vòng while kiểm tra cứ hễ 1 client ngắt kết nối là nó sẽ check nhiều client ngắt kết nối theo hoặc là client 1 và client 2 connect đến client 2 ngắt kết nối nhưng nó vẫn ko nhận ra, nhưng mà client 1 ngắt kết nối thì nó sẽ nhận ra cả 2 vào thời điểm client 1 ngắt kết nối.

  20. Em chào Anh!
    Anh cho em hỏi làm sao để giới hạn băng thông trong mạng ?
    vd: có 3 server gửi file data.dat đến cho client thông qua 1 router
    băng thông của từng server đến Router là không biết trước(100 ~ 1000 kbps)
    băng thông của Client đến Router là 10mbps
    em dùng TcpClient trong c# để truyền file dưới dạng byte
    Em Cám ơn Anh!.

  21. Anh ơi em muốn làm như sau thì phải làm thế nào:
    Khi client connect đến thì server sẽ mở 1 luồng để lắng nghe packet từ client gọi là luồng A. Và 1 luồng khác gọi là luồng B, luồng này có nhiệm vụ là kiểm tra xem client có bị ngắt kết nối không. Luồng B sẽ được tạo bởi luồng A khi client đã connect đến, luồng B sẽ tạo 1 timer và đếm, mỗi khi có packet từ client đến luồng A thì luồng B sẽ reset timer, nếu timer vượt qúa giá trị định sẵn thì luồng B sẽ hủy luồng A. Anh làm ơn giúp em cái này. Em không thể nào nghĩ ra được gì trong vấn đề này.
    Em cảm ơn anh

  22. Bạn đã hiểu rõ các việc cần làm rồi thì việc này ko khó. Chỉ cần biết các kiến thức cơ bản về thread, socket, timer. Bạn có thể làm từng bước, khi chạy ổn định rồi thì tiếp tục “cải tiến” lên bước tiếp theo, như vậy công việc được chia nhỏ ra sẽ dễ làm hơn. Trong trường hợp cần giúp đỡ thì mình sẽ giúp bạn.

  23. Vấn đề từ luồn B hủy luồng A(hoặc từ A hủy B) em đã làm được rồi. Nhưng em chưa tìm được cách giao tiếp giữ 2 luông với nhau, anh có cách nào không vậy

  24. Các thread có thể đồng thời sử dụng cùng một tài nguyên tuy nhiên phải đồng bộ để tránh sai sót. Ví dụ timer của bạn là một biến đếm thì tại luồng A bạn có thể reset biến đếm này. Nếu ko, bạn có thể tạo một flag để B kiểm tra và reset timer.

    • Có nhiều kiểu đồng bộ và khác nhau dựa vào phạm vi, ngữ cảnh và mục đích sử dụng. Nếu bạn muốn thì có thể tự tìm hiểu từng kiểu trên mạng. Đối với trường hợp này thì chỉ dùng lock là được.

  25. Ah anh ơi anh cho em hỏi thêm là làm cách nào để đồng bộ được cả command khi mình execute nó, đồng bộ cả transaction nữa. Anh giúp em nhé em cảm ơn.

  26. Anh nhận xét dùm em 2 là cái phương thức dưới đây đã đủ chặt chẽ chưa. Em cảm ơn nhé

    public static DataSet execommand(SqlCommand strcom)
            {
                using (SqlConnection sqlcon = new SqlConnection(pathconnectstring))
                {
                    SqlDataAdapter sqlad;
                    DataSet objds = new DataSet();
                    // kết nối
                    lock (objlockselect)
                    {
                        try
                        {
                            // mở kết nối
                            sqlcon.Open();
                            // thiết lập command sẽ sử dụng kết nối
                            strcom.Connection = sqlcon;
                            // tạo Adapter lấy dữ liệu
                            sqlad = new SqlDataAdapter();
                            // Adapter sẽ lấy dữ liệu từ command trên
                            sqlad.SelectCommand = strcom;
                            // tạo Dataset để truyền dữ liệu từ Adapter
                            //objds = new DataSet();
                            try
                            {
                                if (sqlcon.State != ConnectionState.Closed)
                                {
                                    if (sqlcon.State == ConnectionState.Open)
                                    {
                                        int countline = strcom.ExecuteNonQuery();
                                        // đổ dữ liệu từ Adapter vào Dataset
                                        sqlad.Fill(objds);
                                        try
                                        {
                                            // đóng kết nối
                                            sqlcon.Close();
                                        }
                                        catch (SqlException ex)
                                        {
    
                                        }
                                        return objds;
                                    }
                                }
                            }
                            catch (InvalidOperationException ex1)
                            {
                                Console.WriteLine("Co loi khi thuc thi command select data:==InvalidOperationException ex1== " + ex1.ToString());
                                Program.ghifile("Co loi khi thuc thi command select data:==InvalidOperationException ex1== " + ex1.ToString());
                            }
                            catch (Exception ex1)
                            {
                                Console.WriteLine("Co loi khi thuc thi command select data:==Exception ex12== " + ex1.ToString());
                                Program.ghifile("Co loi khi thuc thi command select data:==Exception ex12== " + ex1.ToString());
                            }
    
                        }
                        catch (InvalidOperationException ex2)
                        {
                            Console.WriteLine("Co loi khi thuc thi command select data:==InvalidOperationException ex2== " + ex2.ToString());
                            Program.ghifile("Co loi khi thuc thi command select data:==InvalidOperationException ex2== " + ex2.ToString());
                        }
                        catch (Exception ex2)
                        {
                            Console.WriteLine("Co loi khi thuc thi command select data:==Exception ex2== " + ex2.ToString());
                            Program.ghifile("Co loi khi thuc thi command select data:==Exception ex2== " + ex2.ToString());
                        }
                        try
                        {
                            if (sqlcon.State == ConnectionState.Open)
                                sqlcon.Close();
                        }
                        catch (SqlException ex)
                        {
    
                        }
                        return objds;
                    }
                }
            }
    
    public static int execommand(SqlCommand strcom, string typecommand)
            {
                
                using (SqlConnection sqlcon = new SqlConnection(pathconnectstring))
                {
                    SqlDataAdapter sqlad;
                    lock (objlockedit)
                    {
                        //tạo 1 một transaction
                        SqlTransaction transaction;
                        int countline = 0;
                        try
                        {
                            // mở kết nối
                            try
                            {
                                sqlcon.Open();
                            }
                            catch (InvalidOperationException)
                            {
    
                            }
                            catch (SqlException exa)
                            {
                                Console.WriteLine("Co loi mo ket noi" + exa.Message);
                            }
                            try
                            {
                                //bắt đầu 1 transaction
                                if (sqlcon.State == ConnectionState.Open)
                                {
                                    transaction = sqlcon.BeginTransaction();
                                    strcom.Transaction = transaction;
                                }
                                else
                                {
                                    return 0;
                                }
                            }
                            catch (InvalidOperationException ex2)
                            {
                                Console.WriteLine("Co loi khi tao transaction InvalidOperationException ex2 " + ex2.Message);
                                Program.ghifile("Co loi khi tao transaction InvalidOperationException ex2 " + ex2.Message + " ex2.InnerException = " + ex2.InnerException);
                                try
                                {
                                    if (sqlcon.State == ConnectionState.Open)
                                        sqlcon.Close();
                                }
                                catch (SqlException ex23)
                                {
                                    return 0;
                                }
    
                                return 0;
                            }
                            catch (SqlException ex1)
                            {
                                Console.WriteLine("Co loi khi tao transaction SqlException ex1 " + ex1.Message);
                                Program.ghifile("Co loi khi tao transaction SqlException ex1 " + ex1.Message + " ex2.InnerException = " + ex1.InnerException);
                                try
                                {
                                    sqlcon.Close();
                                }
                                catch (SqlException ex2)
                                {
                                    return 0;
                                }
    
                                return 0;
                            }
                            try
                            {
                                // thiết lập command sẽ sử dụng kết nối
                                strcom.Connection = sqlcon;
                            }
                            catch (InvalidOperationException ex)
                            {
                                return 0;
                            }
                            // tạo Adapter lấy dữ liệu
                            sqlad = new SqlDataAdapter();
                            // Adapter sẽ lấy dữ liệu từ command trên
                            if (typecommand == "update")
                            {
                                sqlad.UpdateCommand = strcom;
                            }
                            else if (typecommand == "insert")
                            {
                                sqlad.InsertCommand = strcom;
                            }
                            else if (typecommand == "delete")
                            {
                                sqlad.DeleteCommand = strcom;
                            }
                            // chạy command
                            try
                            {
                                if (sqlcon.State != ConnectionState.Closed)
                                {
                                    if (sqlcon.State == ConnectionState.Open)
                                    {
                                        countline = strcom.ExecuteNonQuery();
                                        transaction.Commit();
                                    }
                                }
                            }
                            catch (InvalidOperationException ex)
                            {
                                Console.WriteLine(strcom.CommandText + " Co loi khi thuc thi command InvalidOperationException ex:" + ex.ToString());
                                Program.ghifile(strcom.CommandText + " Co loi khi thuc thi command InvalidOperationException ex:" + ex.ToString());
                                try
                                {
                                    transaction.Rollback();
                                }
                                catch (InvalidOperationException ex1)
                                {
                                    return 0;
                                }
                                catch (Exception ex2)
                                {
                                    return 0;
                                }
                                finally
                                {
                                    try
                                    {
                                        sqlcon.Close();
                                    }
                                    catch (SqlException ex13)
                                    {
                                    }
                                }
                            }
                            catch (SqlException ex)
                            {
                                Console.WriteLine(strcom.CommandText + " Co loi khi thuc thi command SqlException ex:" + ex.ToString());
                                Program.ghifile(strcom.CommandText + " Co loi khi thuc thi command SqlException ex:" + ex.ToString());
                                try
                                {
                                    transaction.Rollback();
                                }
                                catch (InvalidOperationException ex1)
                                {
                                    return 0;
                                }
                                catch (Exception ex2)
                                {
                                    return 0;
                                }
                                finally
                                {
                                    try
                                    {
                                        sqlcon.Close();
                                    }
                                    catch (Exception ex43)
                                    {
    
                                    }
                                }
    
                            }
                            catch (Exception ex4)
                            {
    
                            }
                            finally
                            {
                                try
                                {
                                    sqlcon.Close();
                                }
                                catch (SqlException ex)
                                {
    
                                }
                            }
                        }
                        catch (SqlException ex1)
                        {
                            Console.WriteLine("Co loi khi thuc thi command edit data==SqlException ex==: " + ex1.ToString());
                            Program.ghifile("Co loi khi thuc thi command edit data==SqlException ex==: " + ex1.ToString());
                        }
                        // đóng kết nối
                        try
                        {
                            sqlcon.Close();
                        }
                        catch (SqlException ex)
                        {
    
                        }
                        return countline;
                    }
                }
            }
    
    • Mình có 1 số góp ý sau:
      – Bạn hạn chế các try catch ko cần thiết và thay vào đó là việc kiểm tra điều kiện chặt chẽ hơn (như phần đóng kết nối). Sử dụng nhiều try catch sẽ giảm performance của ứng dụng).
      – Các thao tác kết nối hoặc transaction ko cần phải đồng bộ vì các database sẽ tự động thực hiện điều này. Cho dù bạn ứng dụng của bạn là multithread thì cũng vậy.
      – Bạn chỉ cần sử dụng đồng bộ khi thay đổi dữ liệu trong Dataset. Tuy nhiên nên hạn chế việc sử dụng multithread trong một ứng dụng ADO.NET vì đa số chúng ko cần thiết và có thể gây ra những rắc rối. Trong trường hợp cần, bạn hãy sử dụng property SynRoot. thay vì các đối tượng tự tạo.
      – Cuối cùng là bạn nên để comment trong bài viết nào mà bạn thấy hợp lý nhất và hạn chế post code quá dài.

Đã đóng bình luận.