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 bình luận về “C# – Lập trình Socket giao tiếp TCP client/server

  1. Chào bạn cho mình hỏi vấn đề lập trình Socket nhưng mình lập trình bên java mong bạn giải đáp giúp mình
    Code của mình thế này
    Server

    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    
    import javax.swing.*;
    public class Server extends JFrame implements ActionListener
    {
    	JTextArea ta;
    	JTextField tf;
    	JButton send;
    	ServerSocket sk;
    
    	DataInputStream dis;
    	DataOutputStream dos;
    	ArrayList list=new ArrayList();
    	public Server()
    	{
    		super("Server");
    		setLayout(new FlowLayout(FlowLayout.CENTER));
    		ta=new JTextArea();
    		JScrollPane sc=new JScrollPane(ta);
    		sc.setPreferredSize(new Dimension(400,400));
    		add(sc);
    		add(tf=new JTextField(30));
    		add(send=new JButton("Send"));
    		send.addActionListener(this);
    		this.setSize(500,500);
    		this.setVisible(true);
    	}
    	public void actionPerformed(ActionEvent e)
    	{
    		try
    		{
    			ta.append("\nServer : "+tf.getText());
    			for(Socket client:list)
    			{
    				DataOutputStream pw=new DataOutputStream(client.getOutputStream());
    				pw.writeUTF(tf.getText());
    			}
    			tf.setText("");
    		}catch (Exception ae) {
    			ae.getMessage();
    		}
    	}
    	public void go()
    	{
    		try
    		{
    		ta.append("Server is opening ");
    		sk=new ServerSocket(8080);
    		while(true)
    		{
    			Socket socket=sk.accept();
    			list.add(socket);
    			ta.append("\n Client connected ");
    			new ThreadClient(socket).start();
    		}
    		}catch (Exception e) {
    			e.getMessage();
    		}
    	}
    	public static void main(String []args)
    	{
    		new Server().go();
    	}
    class ThreadClient extends Thread
    {
    	Socket client;
    	public ThreadClient(Socket socket)
    	{
    		try
    		{
    		client=socket;
    		dis=new DataInputStream(client.getInputStream());
    		dos=new DataOutputStream(client.getOutputStream());
    		}catch (Exception e) {
    			e.getMessage();
    		}
    	}
    	public void run()
    	{
    		try
    		{
    		while(true)
    		{
    			String s=dis.readUTF();
    			for(Socket client:list)
    			{
    				
    			}
    			ta.append("\n Client : "+s);
    		}
    		}catch (Exception e) {
    			e.getMessage();
    		}
    	}
       }
    }
    

    Client

    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.net.Socket;
    import javax.swing.*;
    
    public class Client extends JFrame implements ActionListener
    {
    	JTextArea ta;
    	JTextField tf;
    	JButton send;
    	Socket client;
    	DataInputStream dis;
    	DataOutputStream dos;
    	public Client()
    	{
    		super("Client");
    		setLayout(new FlowLayout(FlowLayout.CENTER));
    		ta=new JTextArea();
    		JScrollPane sc=new JScrollPane(ta);
    		sc.setPreferredSize(new Dimension(400,400));
    		add(sc);
    		add(tf=new JTextField(30));
    		add(send=new JButton("Send"));
    		send.addActionListener(this);
    		this.setSize(500,500);
    		this.setVisible(true);
    	}
    	public void actionPerformed(ActionEvent e)
    	{
    		try
    		{
    			ta.append("\nClient : "+tf.getText());
    			dos.writeUTF(tf.getText());
    			tf.setText("");
    		}catch (Exception ae) {
    		ae.getMessage();
    		}
    	}
    	public void go()
    	{
    		try
    		{
    		client=new Socket("localhost",8080);
    		dis=new DataInputStream(client.getInputStream());
    		dos=new DataOutputStream(client.getOutputStream());
    		while(true)
    		{
    			String s=dis.readUTF();
    			ta.append("\nServer : "+s);
    		}
    		}catch (Exception e) {
    			e.getMessage();
    		}
    	}
    	public static void main(String []args)
    	{
    		new Client().go();
    	}
    }
    

    Vấn đề mình gặp là lần đầu tiên nếu có 1 client A kết nối đến server thì ban đầu giữa client A và server có thể gửi thông tin được với nhau nhưng khi có 1 client B nữa kết nối vào và gửi tin thì client A cũ gửi tin đến server không nhận được nữa,bạn xem giúp mình nhé,cho mình hỏi vấn đề nữa là làm sao 1 client gửi đến server thì nó gửi đến tát cả client còn lại lun,cảm ơn bạn nhé

  2. Chào bạn, mình nhận thấy vấn đề duy nhất mà bạn gặp phải là sử dụng chung 2 biến DataInputStream disDataOutputStream dos cho tất cả các client truy cập đến server. Mỗi khi một client mới kết nối đến bạn lại tạo các luồng đọc/ghi mới và gán cho 2 biến này nên chỉ có client truy cập sau cùng mới tương tác được với server. Ngoài ra bạn có thể chú ý đến 1 số vấn đề sau:

    – Trong ThreadClient bạn ko cần phải tạo đối tượng DataOutputStream vì nó chỉ có mục đích nhận dữ liệu thôi. Tức là Server của bạn cần duy nhất một DataOutputStream và nhiều DataInputStream.

    – Bạn nên đặt tên cho các Client để dễ phân biệt. Ví dụ:

    // ... 
    Socket socket=sk.accept();
    list.add(socket);
    
    ta.append("\n Client "+list.size()+" connected ");
    new ThreadClient(socket,list.size()).start();
    
    // ... ThreadClient:
    Socket client;
    int index;
    DataInputStream receiver;
    
    public ThreadClient(Socket socket,int index)
    {
    	this.index = index;
    	try
    	{
    	client=socket;
    	receiver=new DataInputStream(client.getInputStream());
    	}catch (Exception e) {
    		e.getMessage();
    	}
    }
    // ...
    

    – Bạn nên post comment trong đúng chuyên mục (ví dụ như comment này nên nằm trong mục Java) để tiện theo dõi và phân loại. Cảm ơn bạn!

  3. Chào Yin Yang!mình đã xem ví dụ củ bạn, nhưng khi chạy trên hai máy khác nhau có kết nối Lan thì không được, cho mình làm thế nào để giao tiếp giữa hai máy qua mạng được?(xin lỗi vì mình đang tìm hiểu về giao tiếp mạng nên hơi bị stupid!)

  4. chào bạn,
    mình có một bài tập lớn ” quản lí phòng máy”
    cũng về lập trình socket
    nhưng mình ko rõ về cái này lắm
    bạn có thể giúp mình ko
    mail của mình là: anhtran0000@gmail.com
    bởi vì trong bài tập của mình có một số yêu cầu mà mình ko biết phải làm thế nào
    nếu bạn có thể giúp mình thì nhắn vào mail của nhá
    cảm ơn bạn

  5. Thực sự thì em kô biết gửi ý kiến này ở Chủ đề nào trong Blog của anh nữa:D nhưng mong anh trợ giúp, nếu có thể anh có thể cho em xin địa chỉ mail để tiện cho việc học hỏi.
    Em có 2 đoạn code sau ạ
    Code 1:

    #region Singleton
            private static volatile Website_Sprider instance;
            private static object syncRoot = new Object();
    
            public Website_Sprider() 
            {
                
    
            }
            ~Website_Sprider()
            {
                
            }
            public void Dispose()
            {
                ClearCookies();
            }
            public static Website_Sprider Instance
            {
                get
                {
                    if (instance == null)
                    {
                        lock (syncRoot)
                        {
                            if (instance == null)
                                instance = new Website_Sprider();
                        }
                    }
    
                    return instance;
                }
            }
            #endregion
    
            //* Setting CURL
            public String c_referer = "";
            private static Random rand = new Random();
            //private static string CookieFile = AppDomain.CurrentDomain.BaseDirectory + "cookie" + rand.Next(0, 9) + rand.Next(0, 9) + rand.Next(0, 9) + rand.Next(0, 9) + rand.Next(0, 9) + rand.Next(0, 9) + rand.Next(0, 9) + ".txt";
            private static string CookieFile = AppDomain.CurrentDomain.BaseDirectory + "cookie.txt";
            //*** Setting CURL
    
            //* Content & Header
            public static Dictionary<string, string> Header = new Dictionary<string, string>();
            public static String Content = "";
            //*** Content & Header
    
            /// <summary>
            /// Login to TamQuocTruyenKy
            /// </summary>
            /// <param name="username">username</param>
            /// <param name="password">password</param>
            /// <returns></returns>
            public String Auto_Login(String username , String password)
            {
                String POST = "username=" + username + "&password=" + password;
    
                //Setting CURL
                this.c_referer = "http://tamquoctruyenky.com/home/login/s3.html";//https://loginme.zing.vn/login/act
                String Content2 = this.ReadHTMLCode("http://tamquoctruyenky.com/home/login/s3.html", POST);
                //Console.WriteLine(Content2);
                
                //Kiem tra co dang nhap hay ko
                if (Header.ContainsKey("Location"))
                {
                    Regex regular = new Regex("^(me.zing.vn)");
    
                    //Kiem tra dang nhap thanh cong hay ko
                    if (regular.IsMatch(Header["Location"].ToString().Trim()))
                    {
                        //Console.WriteLine("Test Location '"+Header["Location"].ToString().Trim()+"'");
                        return "Đã Thực Hiện nhưng kô đăng nhập đc";
                    } 
                }
                else
                {
                    return "Đã thực hiện nhưng cũng kô đăng nhập đc";
                }
                string str = "";
                foreach (var s in Header) {
                    
                    str = str  + s + "\n";
                }
                return str;
            }
    
            /// <summary>
            /// CURL - Gui va lay thong tin tu Website
            /// </summary>
            /// <param name="URL">Link</param>
            /// <param name="POST">POST</param>
            /// <param name="c_ssl">Booelean : Có SSL hay ko</param>
            /// <returns></returns>
            public String ReadHTMLCode(String URL, String POST)
            {
                try
                {
                    Content = "";
                    //Console.WriteLine("---------------------------------------------------------------------");
                    Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);
                    Easy easy = new Easy();
    
                    //* Get Content & Header
                    Easy.HeaderFunction hf = new Easy.HeaderFunction(OnHeaderData);
                    Easy.WriteFunction wf = new Easy.WriteFunction(OnWriteData);
    
                    easy.SetOpt(CURLoption.CURLOPT_HEADERFUNCTION, hf);
                    easy.SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, wf);
                    //*** Get Content & Header
    
                    easy.SetOpt(CURLoption.CURLOPT_COOKIEFILE, CookieFile);
                    easy.SetOpt(CURLoption.CURLOPT_COOKIEJAR, CookieFile);
                    easy.SetOpt(CURLoption.CURLOPT_REFERER, this.c_referer);
                    easy.SetOpt(CURLoption.CURLOPT_FOLLOWLOCATION, true);
                    easy.SetOpt(CURLoption.CURLOPT_AUTOREFERER, true);
                    easy.SetOpt(CURLoption.CURLOPT_ENCODING, "gzip,deflate");
                    easy.SetOpt(CURLoption.CURLOPT_URL, URL);
                    //Setting Spider
                    easy.SetOpt(CURLoption.CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 Safari/534.13");
                    Slist sl = new Slist(); 
                    sl.Append("Accept:application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
                    sl.Append("Accept-Charset:windows-1258,utf-8;q=0.7,*;q=0.3");
                    sl.Append("Accept-Encoding:gzip");
                    sl.Append("Accept-Language:vi-VN,vi;q=0.8,fr-FR;q=0.6,fr;q=0.4,en-US;q=0.2,en;q=0.2");
                    sl.Append("content-type:application/x-www-form-urlencoded");
                    sl.Append("Connection:keep-alive");
                    easy.SetOpt(CURLoption.CURLOPT_HTTPHEADER, sl);
    
                    //Send Post
                    if (!POST.Equals(""))
                    {
                        easy.SetOpt(CURLoption.CURLOPT_POSTFIELDS, POST);
                        easy.SetOpt(CURLoption.CURLOPT_POST, true);
                    }
                    
                    //Check SSL
                    if (URL.Contains("https"))
                    {
                        easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYHOST, 1);
                        easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, 0);
                    }
    
                    easy.Perform();
    
                    easy.Cleanup();
                    Curl.GlobalCleanup();
    
                    return Content;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                    return ex.ToString();
                }
            }
    
            public static Int32 OnWriteData(Byte[] buf, Int32 size, Int32 nmemb, Object extraData)
            {
                Content += System.Text.Encoding.UTF8.GetString(buf);
                return size * nmemb;
            }
            public static Int32 OnHeaderData(Byte[] buf, Int32 size, Int32 nmemb, Object extraData)
            {
                try
                {
                    
                    //Console.WriteLine(System.Text.Encoding.UTF8.GetString(buf));
                    //Get Header
                    String[] s = System.Text.Encoding.UTF8.GetString(buf).Split(':');
                    if (s.Length == 1)
                    {
                        Header.Add("status", s[0]);
                    }
                    else
                    {
                        String s2 = "";
                        for (int i = 1; i < s.Length; i++)
                        {
                            s2 += s[i];
                            if (i < s.Length - 1)
                                s2 += ":";
                        }
                        s2 = s2.Trim();
                        //Kiem tra da co Header hay chua ?
                        if( Header.ContainsKey(s[0]) )
                        {
                            s2 = Header[s[0]].TrimStart() + ";" + s2;
                            Header.Remove(s[0]);
                            Header.Add(s[0], s2);
                        }
                        Header.Add(s[0], s2);
                    }
                }
                catch (Exception) { }
                return size * nmemb;
            }
            /// <summary>
            /// Clear Cookies
            /// </summary>
            public void ClearCookies()
            {
                if (File.Exists(CookieFile))
                {
                    File.Delete(CookieFile);
                }
            }
    

    Đoạn code thứ 2:

    private void btThuchien_Click(object sender, EventArgs e)
            {
                string remoteUrl = "http://tamquoctruyenky.com/home/login/s3.html";
                string username = txtId.Text;
                string password = txtPassword.Text;
                string contentHTML = "";
                ASCIIEncoding encoding = new ASCIIEncoding();
                string data = string.Format("username="+username+"&password="+password);
    
                //Tạo các thông số httpRequest
                byte[] bytes = encoding.GetBytes(data);
                HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(remoteUrl);
                httpRequest.Method = "POST";
                httpRequest.KeepAlive = true;
                httpRequest.UserAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.83 Safari/535.11";
                httpRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
                httpRequest.Referer = "http://tamquoctruyenky.com/home/login/s3.html";
                httpRequest.ContentType = "application/x-www-form-urlencoded";
                httpRequest.ContentLength = bytes.Length;        
                Stream dataStream = httpRequest.GetRequestStream();
    
                dataStream.Write(bytes, 0, bytes.Length);
                
                dataStream.Close();
    
                HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();
                StreamReader reader = new StreamReader(res.GetResponseStream(), Encoding.UTF8);
                contentHTML = reader.ReadToEnd();
                //HttpResponseHeader header = new HttpResponseHeader();
                txtKetqua.Text = res.Headers.ToString() + "\n\n\n"+ httpRequest.Headers.ToString();
                reader.Close();
            }
    

    Kết quả em thu đc khi xem thông tin header của 2 code như sau ạ:
    Kết quả Code 1: Sau khi gọi hàm Auto_login(username,password);

    [status, HTTP/1.1 302 Found]
    [Date, Mon, 26 Mar 2012 04:43:56 GMT;Mon, 26 Mar 2012 04:43:57 GMT;Mon, 26 Mar 2012 04:43:58 GMT]
    [Server, Apache/2.2.3 (CentOS);nginx/0.8.55;nginx/0.8.55]
    [X-Powered-By, PHP/5.3.8]
    [Expires, Thu, 19 Nov 1981 08:52:00 GMT]
    [Cache-Control, no-store, no-cache, must-revalidate, post-check=0, pre-check=0]
    [Pragma, no-cache]
    [location, http://api.tamquoctruyenky.com/login?Time=1332737036&Uname=lapnickthu&userid=1009145&ServerId=s3&Sign=6aad33fd98fe0f05d41d95f5ff3fed33&al=0&from=http://tamquoctruyenky.com/home/login/s3.html&siteurl=tamquoctruyenky.com&GameId=12]
    [Content-Length, 5947;138]
    [Connection, close;keep-alive;keep-alive]
    [Content-Type, text/html; charset=UTF-8;text/html;charset=UTF-8]
    [Location, http://s3.tamquoctruyenky.com/login?uid=1009145&time=1332737037&key=511c5fb2b280a2a436c5475849f2a959]
    [Transfer-Encoding, chunked]
    
    

    Kết quả code 2: Sau khi hàm btThuchien_click() đc thực hiện

    Transfer-Encoding: chunked
    Connection: keep-alive
    Cache-Control: no-cache
    Content-Type: text/html;charset=UTF-8
    Date: Mon, 26 Mar 2012 04:49:05 GMT
    Expires: Thu, 01 Dec 1994 16:00:00 GMT
    Set-Cookie: JSESSIONID=abc7uL0GI05weeYzGvnzt; path=/
    Server: nginx/0.8.55
    
    
    User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.83 Safari/535.11
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Referer: http://tamquoctruyenky.com/home/login/s3.html
    Content-Type: application/x-www-form-urlencoded
    Host: s3.tamquoctruyenky.com
    Connection: Keep-Alive
    

    Theo em thấy thì 2 code trên
    +Code 1 dùng hàm ReadHTMLCode(url) để lấy mã nguồn HTML của web
    +Code 2 dùng Stream reader .. cũng để lấy mã nguồn HTML của web đó
    thì đều thu đc Nội dung của html là như nhau (em đã test) nhưng sao kết quả của Header lại khác nhau vậy ạ. Code 2 thì em hiểu cách lấy header còn code 1 thì em chưa hiểu rõ lắm, anh có thể giải thik cho em cách lấy header của code 1 đc kô ạ:D (Code 1 là Code mở Auto Zing Farm trên cộng đồng C việt đó ạ, em đang tìm hiểu nó mong anh giúp:D)

    • Chào bạn . MÌnh cũng đang nghiên cứu cái như bạn. Theo như mình thấy, sở dĩ 2 header khác nhau, do chính bạn làm nó khác nhau. Ở cái trên bạn dùng httpRequest.Header.toString(). Cái này sẽ trả về chính cái Header mà bạn set cho nó. Còn ở code 2 thì do việc bạn bắt header từ webbrowser nên nó khác cách bạn set
      rồi.

  6. Mình ko chuyên trong lĩnh vực này nên cũng không rành lắm. cả 2 header trả về đều có kết quả tương tự nhau. Bạn chỉ cần coi các cặp giá trị trả về chứ không cần quan tâm đến vị trí hay định dạng. Ngoài ra thì đoạn code 1 sử dụng thư viện ngoài (cURL) để thực hiện request và thêm các option nên header trả về sẽ khác nhau.
    Cách tốt nhất là bạn nên hỏi tác giả để biết thêm chi tiết.

    • Cám ơn anh Yin Yang^^ em cũng dùng 1 số công cụ Sniffer để lấy gói tin ra phân tích ra header nhưng vẫn kô hiểu sao Code 1 lại thu đc 1 số header mà kô thấy xuất hiện ở Code 2^^ Tại em cũng tìm hiểu hơn 1 tuần, đã send mail cho Tác giả, nhưng kô nhận đc phản hồi, nên mới nhờ đến anh trợ giúp:D Cám ơn anh rất nhiều, đọc các chủ đề trong blog của anh đúng là gặt hái đc thêm nhiều kiến thức thật^^ Sau này có những thắc mắc mong anh giải đáp dùm:D

  7. Chào a, đầu tiên cho e xin cảm ơn a dành thời gian viết những bài hiết rất bổ ích
    Thứ 2 a cho e chút 🙂 E đang dự định làm phần mềm quản lý mail (cụ thể là Gmail) thế thì e phải bắt đầu từ đâu ạ

  8. Bạn ơi , nếu dữ liệu là âm thanh và hình ảnh thì sao , VD như muốn chat voice hay webcam trong một chương trình chat mạng LAN ấy ,bạn có thể chỉ cho mình cách thu và phát các loại dữ liệu đó được không

  9. bạn có thể đưa ra demo giao tiếp giữa 2 máy tính có địa chỉ IP khác nhau bằng socket được không ( không phải trên mạng LAN ), mình tìm trên mạng vật vã mà không thấy nói tới cái này
    cám ơn trước

  10. mình làm đồ án gps va sử dụng giao thức gprs thông qua modul sim 548c nên cần làm một wed server có ip tỉnh nhưng không biết làm sao.mong ying yang và các ban giup dùm

  11. Bạn ơi, mình làm bài cho chat giữa 2 máy tính. bài chạy k báo lỗi nhưng khi chát mình chỉ gủi đc mà k nhận được tin nhắn, mình dùng mạng wifi cho 2 máy, bài mình copy sang máy khác thì chạy được. bạn đã bị mắc lỗi này chưa, bạn chỉ giúp mình với, cảm ơn bạn trước nhé
    public Server()
    {
    InitializeComponent();
    CheckForIllegalCrossThreadCalls = false;
    }

    private void btnsend_Click(object sender, EventArgs e)
    {
    UdpClient send = new UdpClient();
    IPEndPoint iepRemote = new IPEndPoint(IPAddress.Parse(txtip.Text), int.Parse(txtremote.Text));
    byte[] data = new byte[1024];
    data = Encoding.UTF8.GetBytes(txtsend.Text);
    send.Send(data, data.Length, iepRemote);
    txthienthi.Text += “Sender:” + txtsend.Text + “\r\n”;
    txtsend.Clear();
    if (txtsend.Text.ToUpper().Equals(“Quit”)) this.Dispose();
    }

    private void btnconnect_Click(object sender, EventArgs e)
    {
    Thread tuyen=new Thread(new ThreadStart(NhanD1));
    tuyen.Start();
    MessageBox.Show(“Ket noi thanh cong”);
    txtsend.Focus();
    }
    private void NhanD1()
    {
    UdpClient receiver = new UdpClient(int.Parse(txtlocal.Text));
    IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
    while (true)
    {
    byte[] data = new byte[1024];
    data = receiver.Receive(ref iep);
    string s = Encoding.UTF8.GetString(data);
    if (s.Trim().ToUpper().Equals(“QUIT”)) break;
    txthienthi.Text += “Receiver: ” + s + “\r\n”;
    }
    }

    private void button1_Click(object sender, EventArgs e)
    {
    this.Close();
    }

  12. chào bạn! mình cũng đang lập trình về socket nhưng của mình làm trên form thì có như trên console ko? bài của mình là game show giống như là “đường lên đỉnh Olympia” vậy. mình muốn tạo kết nối giữa nhiều client với 1 server. mong bạn chỉ giúp! mình ko giỏi về lập trình lắm 😀

  13. I hardly write comments, however after browsing a few of the responses here C# – Lập trình Socket giao
    tiếp TCP client/server | YinYang’s Programing Blog. I actually do have a few questions for you if you do not mind. Is it just me or do some of the responses look like they are written by brain dead individuals? 😛 And, if you are writing at additional sites, I would like to keep up with you. Could you post a list of all of all your social networking pages like your linkedin profile, Facebook page or twitter feed?

  14. Pingback: C# – Lập trình Socket giao tiếp TCP client/server | Dương Tân ICT's Blog

  15. Em chào anh.
    Em đang xây dựng 1 file service sử dụng bất đồng bộ để nhận và gửi dữ liệu.
    Nhưng có 1 điều lạ là client gửi dữ liệu lên server thì server chỉ nhận được các gói tin có thứ tự là lẻ, còn các gói tin có thứ tự là chẵn thì lại không nhận được cho dù client báo gửi thành công.
    Ko biết anh đã gặp trường hợp này bao h chưa.
    Anh có đoán được nguyên nhân tại sao ko em cảm ơn anh đã đọc phản hồi của em.

  16. anh ơi giúp em với,em đang xây dựng phần mềm quản lí phòng máy giờ em muốn bên server có thể load đc ip và tên máy của tất cả các client thì phải làm thế nào ah?

  17. Các anh cho em hỏi làm sao để kết nối client tới server thông qua internet được?
    Em đang làm theo mô hình client-server như a Yin Yang hướng dẫn, nhưng khi copy client qua máy khác lại không kết nối được tới server.
    Ai biết chỉ em với.
    Thks all !

    • Em đang dùng máy của e làm 1 server với địa chỉ IP của máy (lấy từ trang này : whatismyip.com). Nhưng nếu e thay địa chỉ IP của máy e vào address trong đoạn code:
      TcpListener listener=new TcpListener(address,PORT_NUMBER);
      listener.Start();

      thì dòng listener.Start() không thực hiện được. Em đọc thấy dòng code này: TcpListener listener=new TcpListener(address,PORT_NUMBER); chỉ áp dụng với IP LAN phải không ạ??
      Mong a giúp sớm ^^

  18. Chào YinYang.. mình có 1 bài tập về sever và client, Giải thích như ở dưới, , phần sever
    mình không biết Code ở chỗ:
    – server -port -data -proc_count
    – và chỗ default port if 3000

    Mong bạn giải thích dùm mình.

    Create a headless server application in Java which listens on a configurable TCP port. The commandline options to start the server are:
    server -port -data -proc_count
    -port option is optional. Default port is 3000. A valid TCP port number is between 1 and 65535.
    -data option is the location of the folder where server keeps its data store. This is optional as well. If not specified, server stores
    data in “serverdata” folder in user’s home directory. If the directory does not exist, it is created. If it exists, it is used. Server must
    deal with permissions to write to this folder and exit gracefully if it cannot write to, read from or create files/folders under the
    specified folder.
    proc_count is a positive integer and is optional. If it is not provided, the default value is 2. Its usage will become clearer further
    down in the specification.
    When server starts, it checks if it can access data folder and can bind to the specified port. If it can, it creates initial_proc_count number
    of worker threads to process requests from the clients.
    Server’s job is to maintain a database of birds, their characteristics and sightings. In order to achieve this, server stores data in two XML
    files: ## birds.xml which contains a list of birds with their 4 characteristics: name, color, weight and height
    sightings.xml which contains the location (string) and time (date & time) for each bird sighting.
    Server at start up loads the two files and stores data in any data-structure of programmer’s choice.
    Server has a thread that periodically saves the contents of in-memory data-structure to two XML files.
    Server also saves any data on shutdown.

  19. Chào bạn ! Mình đã đọc bài của liên quan đến tcplistener . Như mình đang viết dự án Silverlight nhưng mình imports System.net.sockets nhưng không thể tìm thấy tcplistener trên axml.vb, vậy tcplistener có liên quan gì đến Silverlight 4 , Net Fram 4
    Mình đang tìm hiểu chỉ cần Client ngắt kế nối thì server nhận biết mà thôi , nhưng chưa tìm ra giải pháp nào . Mong bạn giúp mình nhé . Xin cảm ơn

  20. Chào bạn, hiện mình đang làm một đề tài về quản lý trường học bằng C# và có phần kết nối client-server. Mình có một vấn đề muốn hỏi: Ví dụ như hiện có 1 người dùng máy ở Client1 thêm học sinh A vào lớp 10A1, đồng thời cũng có một người dùng máy Client2 thêm học sinh A vào lớp 10A1 vậy thì có xảy ra xung đột như thế nào? CSDL trên server sẽ nhận thông tin như thế nào và mình làm sao để quản lý phần đó? Mình nghe bạn mình nói là viết transaction gì đó nhưng mình hoàn toàn mù tịt. Xin lỗi mình chỉ mới tìm hiểu về phần này nên ko rõ phải bắt đầu và làm như thế nào? Nếu bạn có tài liệu hay những bài liên quan có thể giúp đỡ mình ko? Thành thật cảm ơn bạn.

  21. Website thì lưu trữ dữ liệu trên Hosting, còn phần mềm thì lưu ở đâu ? Em muốn viết một phần mềm bán hàng hổ trợ nhiều client.. ở nhiều chi nhánh khác nhau.. nên cần có sự đồng bộ về database.. thì phải làm sao ? Mong các anh chỉ cho em một lối đi :))

  22. Các bạn cho mình hỏi 1 câu hơi cơ bản
    Server có IP là x.y.z.w và đang lắng nghe ở port 80,
    Client gửi 1 request lên sever đó với port 80 thì server sẽ accept qua 1 port khác rồi truyền nhận dữ liệu với client qua port accept riêng đó.

    Câu hỏi là client mở port nào để nhận dữ liệu của server ?

  23. Chào Yin, mình đang gặp 1 vấn đề về truyền dữ liệu, Mình viết chương trình console (.Net 4.0) gửi nhận dữ liệu giữa client/server.
    Process: Client kết nối server -> client gửi ID tới server > Server kiểm tra id trong database -> nếu ID tồn tại sẽ gửi lại interval cho client > Client nhận ID, thiết lập thời gian repeat gửi report = intereval nhận về.
    Sau khi build ra file binary trên máy mình thì chạy chính xác; gửi qua máy 2 người kế bên cũng chạy chính xác. Đến khi gửi sếp review thì chỉ accept connect mà ko gửi được dữ liệu từ client tới server. Máy mình và máy ổng cùng cấu hình win 7 – 64 bit
    Thông tin project:
    Tool: Visual studio 2012
    Invironment: .NET 4.0, Win 7 – 64 bits
    Database: SQLite
    Protocol: TCP/IP client multi-thread.
    Technique: Reflection.

  24. Chào bạn Yin Yang
    Cảm ơn bạn đã đăng bài viết: “C# – Lập trình Socket giao tiếp TCP client/server” – rất hay và bổ ích, có thể ứng dụng để giải quyết nhiều vấn đề công nghệ quan trọng. Mình thử chạy các ví dụ đều êm xuôi. Nhưng khi chuyển code sang chạy ở dạng WebForm thì không được. Bắt đầu từ lệnh: listener.Start(); là không thấy có phản ứng nào cả. Mục đích của tôi là muốn chạy được TCPListener, Socket (phía server). Rất mong bạn chỉ dẫn hoặc cho tôi biết một số tài liệu tham khảo.
    Xin trân trọng cảm ơn

  25. Chào bạn!
    Hiện tại mình làm chat qua Socket trên C# giữa 2 máy khác nhau, khác mạng thông qua IP Address. Nhưng khi tạo socket thì bị lỗi này nè bạn “The requested address is not valid in its context”
    Bạn có thể giúp mình được không.
    Code của mình:
    IPAddress ipAddress = IPAddress.Parse(ip);
    //IPAddress ipAddress = new IPAddress(new byte[] { 14, 161, 44, 25 });
    IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);

    // Create a TCP/IP socket.
    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    Console.WriteLine(“Server started…”);
    Console.WriteLine(“Waiting for a connection…”);

    // Bind the socket to the local endpoint and listen for incoming connections.
    try
    {
    listener.Bind(ipEndPoint);
    listener.Listen(100);

    while (true)
    {
    // Set the event to nonsignaled state.
    allDone.Reset();

    // Start an asynchronous socket to listen for connections.
    listener.BeginAccept(
    new AsyncCallback(AcceptCallback),
    listener);

    // Wait until a connection is made before continuing.
    allDone.WaitOne();
    }
    }
    catch (Exception e)
    {
    Console.WriteLine(e.ToString());
    }

    Mình cảm ơn.

  26. các bạn ơi cho mình hỏi chút: mình cũng làm tcp truyền nhận file giữa client và server tương tự như trên, khi chạy cả client và server trên 1 máy thì được, còn chạy client sang máy khác (cùng connect 1 wifi) nhưng lại không được.

  27. chào bạn, mình có làm ví dụ này với C#, phần gửi dữ liệu lên thì ok nhưng khi đẩy xuống thì lại lỗi chỗ kết nối! Đây là code của mình:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;

    namespace SLC
    {
    public partial class Form1 : Form
    {
    private const int BUFFER_SIZE = 1024;
    static ASCIIEncoding ecoding = new ASCIIEncoding();
    string str1, idtram, ip1;
    int port1;
    public Form1()
    {
    InitializeComponent();
    }

    private void btnketnoi_Click(object sender, EventArgs e)
    {
    try
    {
    ip1 = txtIP.Text;
    port1 = int.Parse(txtPORT.Text);
    IPAddress address = IPAddress.Parse(ip1);
    TcpListener listener = new TcpListener(address, port1);
    listener.Start();
    txtClient.Text = “Waiting…”;
    Socket socket = listener.AcceptSocket();
    string str = socket.RemoteEndPoint.ToString();
    txtClient.Text = str;
    var stream = new NetworkStream(socket);
    var reader = new StreamReader(stream);
    var writer = new StreamWriter(stream);
    str1 = reader.ReadLine();
    }
    catch
    {
    MessageBox.Show(“Nhập lại”);
    }

    }

    private void btnUpdate_Click(object sender, EventArgs e)
    {
    if (str1.Length >= 15)
    {
    idtram = str1.Substring(0, 5);
    txtTram.Text = idtram;
    string st;
    st = str1.Substring(10, 1);
    if (st == “X”) txt1.Text = “TAT”;
    else txt1.Text = “BAT”;
    st = str1.Substring(14, 1);
    if (st == “X”) txt2.Text = “TAT”;
    else txt2.Text = “BAT”;
    st = str1.Substring(18, 1);
    if (st == “X”) txt3.Text = “TAT”;
    else txt3.Text = “BAT”;
    st = str1.Substring(22, 1);
    if (st == “X”) txt4.Text = “TAT”;
    else txt4.Text = “BAT”;
    }
    else
    {
    txtTram.Text = “Error”;
    txt1.Text = “Error”;
    txt2.Text = “Error”;
    txt3.Text = “Error”;
    txt4.Text = “Error”;
    }
    }

    private void btnGui_Click(object sender, EventArgs e)
    {
    string bt = “”;
    bt = “TRAM” + idtram + “; “;
    if (chk1.Checked)
    bt = bt + “C.” + “Y “;
    else bt = bt + “C.” + “X “;
    if (chk2.Checked)
    bt = bt + “D.” + “Y “;
    else bt = bt + “D.” + “X “;
    if (chk1.Checked)
    bt = bt + “E.” + “Y “;
    else bt = bt + “E.” + “X “;
    if (chk1.Checked)
    bt = bt + “F.” + “Y “;
    else bt = bt + “F.” + “X”;
    IPAddress address = IPAddress.Parse(ip1); //bat dau bi loi
    TcpListener listener = new TcpListener(address, port1);
    Socket socket = listener.AcceptSocket();
    var stream = new NetworkStream(socket);
    var writer = new StreamWriter(stream);
    writer.WriteLine(bt);
    stream.Close();
    socket.Close();
    listener.Stop();
    }

    }
    }

  28. Chào anh.
    Em làm một cái liên quan tới cả phần cứng. Em có 4 server (Mỗi mạch là 1 server và nó là fixed). Form của e là 1 client. Anh cho em hỏi liệu em có thể kết nối từ 1 client tới nhiều server được không ạ ?
    Em xin cám ơn

  29. Pingback: C# – Lập trình Socket giao tiếp TCP client/server | Lập Trình Pro

  30. Chào bạn! Bạn cho mình hỏi nếu mình muốn gửi và nhận tin nhắn độc lập nhau giữa client và server (nghĩa là không theo tuần tự qua lại), giống như Zalo hay Messenger thì làm thế nào vậy ạ? Xin cảm ơn!

  31. Bạn cho mình hỏi mình mở 1 port tại Router của mình, mình gửi cho bạn mình ip router và port đó. Bạn mình có thể chat (KHÔNG LAN) với mình thông qua chat application dùng socket

    Mình cũng chat được với bản thân thông qua ip router với port service đã mở

    MÌnh xin hỏi là 1 phần mềm như skype hay web như facebook, có cần mở cồng nào đâu mà vẫn chat được, mình không biết làm sao để thực hiện, hi vọng bạn có thể cho mình xin ít thông tin hữu ích, thân

  32. Chào bạn, mình có vấn đề như sau hy vọng nhận được sự giúp đỡ từ bạn:
    Mình có máy in có portname là TS004 sử dụng WSD port . Mình muốn lấy dữ liệu gửi máy in đó, bạn có cách giải quyết nào giúp mình không? Thank you very much.

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