C# – Simple Multi-threaded/Multi-connection TCP Server

Trong bài này bạn sẽ thấy hai phiên bản TCP Server sử dụng Thread cho phép nhiều kết nối cùng một lúc. Sau đó bạn có thể tạo một client như trong bài ‘C# – Lập trình Socket giao tiếp TCP client/server’ để kiểm tra. Tuy nhiên trong bài này tôi sẽ sử dụng telnet để tạo nhiều kết nối đến server.

Hướng tiếp cận 1: Chấp nhận kết nối lần lượt

Phiên bản này cho phép các kết nối diễn ra lần lượt bằng cách đợi kết nối từ client và sẽ tạo ra thread sau khi kết nối được hình thành. Server sẽ nhận dữ liệu từ client là một chuỗi id, dựa vào đó server sẽ tìm và trả về tên tương ứng với id đó lấy trong dữ liệu là một Dictionary. Để hiểu thêm bạn có thể coi lại các bài viết trước mà tôi đã giới thiệu (xem phần Related articles ở cuối).

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class StudentTCPServer{

	const int MAX_CONNECTION = 10;
	const int PORT_NUMBER=9999;
	static int _connectionsCount=0;
	static TcpListener listener;

	static Dictionary<string, string> _data =
		new Dictionary<string, string>
	{
		{"1","Thomas"},
		{"2","John"},
		{"3","Joseph"},
		{"4","Paul"},
		{"5","Matthew"},
	};

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

		listener = new TcpListener(address,PORT_NUMBER);
		Console.WriteLine("Waiting for connection...");
		listener.Start();

		while(_connectionsCount<MAX_CONNECTION || MAX_CONNECTION==0)
		{
			Socket soc = listener.AcceptSocket();
			_connectionsCount++;

			Thread t = new Thread((obj)=>
			                      {
			                      	DoWork((Socket)obj);
			                      });
			t.Start(soc);
		}
	}

	static void DoWork(Socket soc)
	{
		Console.WriteLine("Connection received from: {0}",
		                  soc.RemoteEndPoint);
		try{
			var stream = new NetworkStream(soc);
			var reader = new StreamReader(stream);
			var writer = new StreamWriter(stream);
			writer.AutoFlush = true;

			writer.WriteLine("Welcome to Student TCP Server");
			writer.WriteLine("Please enter the student id");

			while(true)
			{
				string id = reader.ReadLine();

				if(String.IsNullOrEmpty(id))
					break; // disconnect

				if(_data.ContainsKey(id))
					writer.WriteLine("Student's name: '{0}'",_data[id]);
				else
					writer.WriteLine("Can't find name for student id '{0}'",id);
			}
			stream.Close();
		}
		catch(Exception ex)
		{
			Console.WriteLine("Error: "+ex);
		}

		Console.WriteLine("Client disconnected: {0}",
		                  soc.RemoteEndPoint);
		soc.Close();
	}
}

Hướng tiếp cận 2: Chấp nhận kết nối đồng thời

Điểm khác biệt của phiên bản này so với phiên bản 1 là ta sẽ tạo sẵn một số lượng thread (MAX_CONNECTION) và mỗi thread sẽ có nhiệm vụ chờ đợi, tạo kết nối cũng như tương tác với client. Bạn có thể thấy điểm khác biệt chính là do vị trí đặt phương thức TCPListener.AcceptSocket() bên trong hay ngoài thread.

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class StudentTCPServer{

	const int MAX_CONNECTION = 10;
	const int PORT_NUMBER=9999;

	static TcpListener listener;

	static Dictionary<string, string> _data =
		new Dictionary<string, string>
	{
		{"1","Thomas"},
		{"2","John"},
		{"3","Joseph"},
		{"4","Paul"},
		{"5","Matthew"},
	};

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

		listener = new TcpListener(address,PORT_NUMBER);
		Console.WriteLine("Waiting for connection...");
		listener.Start();

		for(int i = 0;i < MAX_CONNECTION;i++){
			new Thread(DoWork).Start();
		}
	}

	static void DoWork()
	{
		while(true)
		{
			Socket soc = listener.AcceptSocket();

			Console.WriteLine("Connection received from: {0}",
			                  soc.RemoteEndPoint);
			try{
				var stream = new NetworkStream(soc);
				var reader = new StreamReader(stream);
				var writer = new StreamWriter(stream);
				writer.AutoFlush = true;

				writer.WriteLine("Welcome to Student TCP Server");
				writer.WriteLine("Please enter the student id");

				while(true)
				{
					string id = reader.ReadLine();

					if(String.IsNullOrEmpty(id))
						break; // disconnect

					if(_data.ContainsKey(id))
						writer.WriteLine("Student's name: '{0}'",_data[id]);
					else
						writer.WriteLine("Can't find name for student id '{0}'",id);
				}
				stream.Close();
			}
			catch(Exception ex)
			{
				Console.WriteLine("Error: "+ex);
			}

			Console.WriteLine("Client disconnected: {0}",
			                  soc.RemoteEndPoint);
			soc.Close();
		}
	}
}

Kiểm tra bằng telnet

Bạn mở Command Prompt lên (Run>cmd) và gõ vào lệnh

telnet localhost 9999

Nếu kết nối thành công bạn sẽ nhận được thông điệp từ server:

Welcome to Student TCP Server
Please enter the student id

Trong Windows Seven, nếu Telnet Client chưa được kích hoạt, bạn vào Control Panel>Programs and Feautures>Turn Windows features on or off. Trong bảng Windows Features, đánh chọn vào mục Telnet Client (như hình sau):

Bạn có thể thay đối giá trị của hằng MAX_CONNECTION để thay đổi số lượng kết nối tới server.

49 bình luận về “C# – Simple Multi-threaded/Multi-connection TCP Server

  1. Mình đang xây dựng chương trình nhận video từ Client về. Server chứa nhiều picturebox mỗi picture box sẽ dùng để hiển thị video của Client gửi về, trong trường hợp 1 client thì không sao. Trong trường hợp nhiều client có cách nào để phân biệt kết nối của mỗi client cũng như dữ liệu của từng client, hoặc có cách nào để mỗi client sau khi gửi dữ liệu sẽ lưu dữ liệu trên 1 picture box riêng ko bạn.

  2. Chào bạn , mình có 1 vấn đề với server này 😀
    Đó là khi server của mình ngừng lắng nghe , mình đã đóng cổng Socket , và đóng Thread bằng lệnh thread.Abort();
    Nhưng khi đóng form Server , tại Task Manager thì Server của mình vẫn chạy , almf cho không thể bật lại mà không tắt nó đi.
    Bnaj cso thể hướn dẫn để làm sao tắt triệt để cái server đi ko , thanks 😀

    • Có thể chương trình của bạn còn tài nguyên nào đó chưa được giải phóng. Bạn có thể sử dụng cách Application.Exit() hoặc Environment.Exit(). Tuy nhiên nếu có thể tìm ra vấn đề thì bạn nên sử dụng cách tắt chương trình thông thường.

      • mình sử dụng cách thứ 2 của bạn , đó là tạo ra 1 số thread nhất định rồi chờ đợi kết nối từ phía client , mình đoán là do khi đóng chương trình, 1 số thread vẫn chưa đóng và mình đã thử đóng các thread trong hàm Dowork() tuy nhiên vẫn không được . Mình cũng đã thử Application.Exit() và Environment.Exit() vẫn vậy .
        Khi mình thử dùng listener.Stop(); thì chương trình báo lỗi , ko biết sao :-s

  3. chào các anh, e đang làm một ứng dụng có sử dụng socket, trong khi server đang chờ kết nối từ clients thì cửa sổ server cứ đơ ra, nó để là “not responding” và không update được giao diện đồ họa ạ.. có cách nào để nó không còn “not responding” và update được giao diện ngay lập tức khi có thay đổi được không ạ.. em cảm ơn

  4. Lập trình mạng với giao thức TCP thì 1 server có kết nối được với nhiều client không các bạn. Nếu được thì phải lập trình theo kểu nào. Nếu cóc code thì càng tốt 😀

  5. Bác chủ ơi, e đang phải làm bt làm xây dựng game bắn máy bay sử dụng RPC, bác có thể xây dựng 1 bài trên blog của bác dc ko ạh?sử dung rpc để xây dựng mô hình clien/server.hì

  6. bạn ơi cho mình hỏi là có thể show một form mới trong thread background không? mình làm thì nó cứ báo lỗi không thể khởi tạo còn show messagebox thì vẫn bình thường (mình làm bằng Wpf c#)!có cách nào để khắc phục không ạ.

      • Trước khi start Thread, bạn có thể thử gọi phương thức sau:
        t.SetApartmentState(ApartmentState.STA);

        Bạn nên cung cấp rõ và đúng thông tin ngay từ câu hỏi đầu tiên để người khác không phải hỏi lại bạn.

        Thân!

  7. Em đang làm 1 project chat internet nhưng em mới làm đc chat lan còn chat internet thì ko bít phải config thế nào ?. anh giúp em với ạ, em tìm trên mạng cũng ko thấy.

    • Việc chat qua internet đòi hỏi bạn cần có server với IP tĩnh để nhận và gửi các thông điệp giữa các client. Cách lập trình thì cũng tương tự như chat trong mạng LAN nên cps lẽ là người ta sẽ không tạo các ví dụ này.

  8. bài viết rất hữu ích!
    mình có vấn đề này muốn hỏi. bài trên chỉ là truyền dữ liệu dạng byte thui phải không ạ? mình muốn truyền dữ liệu dạng hình ảnh từ form Server sang form Client thì làm thế nào? (cả 2 form đều có picturebox)

  9. Chào bạn! mình đang lập trình trên c# để nhận dữ liệu từ client ( client của mình là một phần cứng rời có chức năng như 1 máy con có thể truy cập vào nó như 1 web), trên giao diện chính là server của mình mình muốn đọc dữ liệu từ nó để hiển thị lên datagridview thì làm thế nào.Sử dụng ví dụ của bạn dc k?

  10. chào Yin Yang, bạn cho mình hỏi là : mình có 1 from server muốn nhận dữ liệu từ nhiều from client khác,bên server mình có 1 button để start server, nhưng khi minh ấn start server thì from server bị đơ luôn( vẫn báo k có lỗi nào cả ) .Minh cũng chỉ mới bắt đầu tìm hiểu, mong sớm nhận được hồi đáp của bạn

    • Khi một client kết nối đến thì bạn chỉ cần lưu nó lại vào một collection. Khi server gửi dữ liệu thì sẽ lặp qua collection đó và gửi cho từng client. Chỉ khác nhau ở cấu trúc dữ liệu dùng để lưu trữ client thôi.

  11. làm phiền Yin Yang, bạn có thể cho thể chạy timer trong thread trong được không.
    Không hiểu sao mình dùng timer để chạy để cập nhật thời gian trên client, khi client kết nói được với server rồi, thì mình cho timer chạy nhưng không có tác dụng gì cả?
    Cảm ơn nhiều.

  12. Anh ơi, khi nào co thời gian anh co thể viết 1 bài về ASYNCCALLBACK được không anh. Thấy nó cũng hay nhưng đọc hoài không hiểu hết được. Chỉ có đọc bài của anh mới hiểu thôi 🙂

  13. Chào bạn, mình thiết lập 1 hệ thống 4 client vs 1 sever kết nối với nhau tuần tự như trường hợp đầu tiên của bạn. Nhưng trong client mặc dù đóng form client thì tiến trình vẫn còn chạy trong task manager mặc dù mình đã đóng kết nối trên client khi close form.

  14. Chào Yin Yang
    Bạn cho mình hỏi, phương thức nào có thể cho phép người quản lý bên Server copy (Ctrl + C) một file ảnh(jpg) ở máy Client và Paste (Ctrl +V) về máy Server. hoặc Server có thể nghe nhạc Windown Media được bên máy Client mở.

  15. Chào bạn Yin yang, mình đang làm một phần mềm liên kết với máy xét nghiệm của bệnh viện và gặp vấn đề sau. máy xét nghiệm kết nối với máy chủ thông qua host. Bây giờ mình muốn lấy được tập tin mà máy xét nghiệm trả về thì phải làm thế nào hả bạn!

  16. chào Yin Yang !
    mình đang làm bài tập chat giữa client với client thông qua server. mà đề thì phải sử dụng ngôn ngữ C++. mong bạn giúp đỡ . mình chỉ ko biết cách để server phân biết các client với nhau như thế nào(tạo mãng mình ko hiễu @@), tạo luồng cho từng clien kết nối đến thì mình ko làm dc . kiến thức mình còn non. mong bạn giúp đỡ . mình đã có thể viết code cho server chat với client nhưng client với client thông qua sever thì mình làm ko dc!!

  17. Chào bạn . Mình đang muốn làm một demo để hiểu thêm về socket, demo của mình là server sẽ gửi liên tục các gói N bytes về client, client sẽ nhận các gói đó, với mỗi gói client sẽ có xữ lý dựa trên dữ liệu là gói nhận được . Mình không biết làm sao để chạy mượt quá trình này, mong bạn gợi ý . cảm ơn bạn 😀

  18. Ad ơi giờ mình làm cơ chế đồng bộ dữ liệu phân tán, cơ chế 1 database nằm ở trung tâm và có nhiều database local tại mọi nơi. Bài toán là khi ở local phát sinh thì dữ liệu ấy phải được đồng bộ về database ở trung tâm. Có thể gửi mail giúp em với nhé:quangkhai21038@gmail.com. Help me

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