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.
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.
xin lỗi gì bây giờ mới thấy comment của bạn. Đối với vấn đề này thì rất đơn giản vì mỗi client kết nối đến đều có ip và đối tượng socket riêng. Bạn chỉ cần dựa vào đó để phân biệt là đủ.
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
Bạn thử dùng phương pháp đặt property Thread.IsBackground = true xem sao.
Được rồi bạn à 😀 , cảm ơn bạn rất nhiều :).
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
Đó là do bạn chưa sử dụng Thread, bạn áp dụng cách trong bài viết trên để làm thử nhé.
cảm ơn anh, em đã làm thử và hết “not responding”, cập nhật dc giao diện tức thì nhưng trong lúc thread chưa kết thúc, cửa sổ bị vô hiệu hóa không di chuyển được ạ 😦
Điều này thì có lẽ mình phải xem code của bạn thì mới biết được.
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 😀
Trong bài viết đã trả lời rồi đó, bạn làm thử đi là thấy.
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ì
Để mình tìm hiểu thử, tuy nhiên không dám chắc là sẽ viết.
Mình tìm thấy một vài link, hy vọng có thể giúp ích cho bạn.
http://stackoverflow.com/questions/2878447/tutorial-simple-wcf-xml-rpc-client
http://stackoverflow.com/questions/8091389/need-rpc-c-sharp-net-framework-4-0-samples
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 ạ.
Thread thường hay background đều không khác nhau gì đến việc bạn tạo cửa sổ mới. Bạn có thể đưa code hoặc thông điệp lỗi lên đây không?
debug thì nó báo là:The calling thread must be STA, because many UI components require this.
tại mình tạo một thread show 1 cái window trong một thread khác.
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!
Cảm ơn bạn nhiều,mình sẽ rút kinh nghiệm!
Hi Ying Yang, hi all !
Nếu client chạy trên một máy khác trong mạng Lan thì IP sẽ như thế nào vậy ! Mình thử nhưng chưa được. Mong các bạn cho ý kiến.
Sorry Yin Yang mới đúng !
Ps: không có edit bài post thì phải.
Đa số các ví dụ về chat (bao gồm ví dụ này của mình) là thực hiện trên LAN. Bạn có thể vào cmd gõ ipconfig để xem ip của máy.
Đối với việc chat thông qua internet thì phải cần có server trung gian với ip tĩnh thì mới thực hiện được.
Thanks Yin Yang đã rely. Nguyên nhân là mình bị FireWall ! 🙂
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.
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)
Bạn chỉ cần chuyển hình ảnh thành dạng mảng byte rồi chuyển qua thôi.
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?
Tất nhiên là được, phần hiển thị dữ liệu và truyền dữ liệu client-server không ảnh hưởng gì nhau cả. Bạn cứ coi đó như hai phần riêng biệt.
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
Bạn có thể tìm hiểu thêm về sử dụng thread trong Window Forms để giải quyết vấn đề này. Các bài hướng dẫn chủ đề này rất nhiều trên google.
Mình nghĩ vấn đề của bạn, tác giả đã giải quyết bằng ví dụ ở trên rồi mà 😕
Chào Yinyang , mình muốn gữi tin nhắn cho tất cả các client đang kết nối đến thì dùng cách nào .
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.
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.
bạn có thể cho mình xem code ko?
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 🙂
Để bữa nào có thời gian mình sẽ viết thử 1 bài vậy.
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.
Một form không phải là một ứng dụng, nên khi bạn đóng form thì các tác vụ đang chạy dở sẽ tiếp tục. Nếu muốn ứng dụng đóng lại, bạn có thể dùng Application.Exit().
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ở.
mình đang viết chương trình Remote Desktop nên cần thêm phân đó
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!
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!!
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 😀
em đang làm về đề tài quản lý điểm sinh viên viết bằng java, có ai có không, share cho e với ạ
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