Tut C#: Lập trình mạng và làm việc với thread, demo chương trình chat qua mạng Lan

Trong lập trình,nếu đòi hỏi chương trình xử lý nhiều công việc một lúc mà với kiểu lập trình hàm, thủ tục bình thường thì không thể giải quyết. Ví dụ bạn làm một game hứng trứng. Nếu bạn di chuột để điểu khiển giỏ đựng trứng thì quả trứng sẽ bị đứng yên, vì khi đó máy tính đang phải xử lý để hiển thị, di chuyển giỏ trứng.

Vậy phải làm sao để xử lý vấn đề này. đó là sử dụng thread( tạm hiểu là luồng, xử lý các công việc trên các luồng khác nhau). C# và .NET hỗ trợ vấn đề này rất tốt và xử lý cực kỳ đơn giản. bạn chỉ cần khởi tạo và chạy thread.(khai báo sử dụng thử viện System.Threading)

Khởi tạo: thread a=new thread(anObject.AMethod);

Chạy thread: a.start();

(về việc sử dụng thread, bạn sẽ hiểu kỹ hơn qua ví dụ ở cuối bài viết này).

Lập trình mạng, hiểu đơn giản là chúng ta tạo các kết nối, gửi và nhận dữ liệu giữa các máy tính trong mạng Lan hay mạng internet. Những kiến thức tôi chia sẻ ở đây lấy trong cuốn “Bài giảng lập trình mạng với C#”( link download ở cuối bài viết).

Các kiến thức cơ bản về mạng máy tính, các giao thức truyền dữ liệu, Mô hình 7 tầng,… các bạn đã(sẽ được học trong môn mạng máy tính hoặc đọc phần đầu tiên trong cuốn sách “Bài giảng lập trình mạng với C#”, tôi sẽ không đề cập ở đây nữa. Các bạn sẽ tìm hiểu về lập trình socket qua ví dụ sau đây, hoặc ở ví dụ trong cuốn sách “Bài giảng lập trình mạng với C#”. Ví dụ sau đây sẽ ứng dụng việc sử dụng thread vào lập trình mạng để xử lý các công việc, lỗi thường gặp phải và cách khắc phục:, (chương trình chỉ là demo và chưa bắt các biệt lệ, và vì không có 2 máy để test nên tôi sử dụng 1 máy, chạy 2 trương chình cùng lúc, và thay địa chỉ Ip của server là 127.0.0.1)

Ví dụ: demo chương trình chat qua mạng Lan:

Giao diện đơn giản cho chương trình

(ở cả 2 chương trình phía client và server, textbox để hiển thị là txtHienThi, textbox để nhập là txtNhap, nút gửi là btnSend)

viết trương trình cho phía server:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace WindowsFormsApplication8
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
static IPEndPoint iep;
static Socket sever, client;
Thread a;

private void Form1_Load(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false;//Chỗ này giải thích ở dưới
a = new Thread(ketnoi);                                   //Khởi tạo một Thread, Thread này sẽ chạy hàm ketnoi được định nghĩa ở dưới
a.Start();                                                                  // bắt đầu chạy Thread
}

private void btnSend_Click(object sender, EventArgs e)
{
byte[] data = Encoding.Unicode.GetBytes(txtNhap.Text);//Chuyển text nhập vào thành mảng byte để gửi
client.Send(data,data.Length,SocketFlags.None);//gửi dữ liệu đến client
txtHienThi.Text += “\n” + txtNhap.Text;//Hiển thị đoạn chat lên màn hình của mình

txtNhap.text=””;

}
void ketnoi()//Hàm nà chạy trong thread a
{
iep = new IPEndPoint(IPAddress.Parse(“127.0.0.1″), 100);//Định nghĩa điểm kết nối của server, mở cổng 100
sever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//định nghĩa kiểu socket
sever.Bind(iep);//Tạo socket server với ip 127.0.0.1 port 100
sever.Listen(10);//server lắng nghe các kết nối
txtHienThi.Text = “Đang kết nối”;
client = sever.Accept();//Khi có client nhận kết nối, tạo 1 socket khác, đặt tên là client để gửi, nhận dữ liệu từ client
txtHienThi.Text = “Chấp nhận kết nối từ” + client.RemoteEndPoint.ToString()
+ “\n Bạn có thể bắt đầu chat”;
while (true)//while(true) để luôn luôn nhận dữ liệu được gửi từ client và hiển thị lên text, nếu không đặt vòng lặp này thì
{                     // nhận dữ liệu được 1 lần
byte[] nhan = new byte[1024];
client.Receive(nhan);//nhận dữ liệu được gửi đến và đổ vào mảng byte nhan
txtHienThi.Text +=”\n”+ Encoding.Unicode.GetString(nhan);//chuyển dữ liệu nhận được thành text và hiển thị
}
}

}

}

Có thể các bạn sẽ thắc mắc, tại sao phải sử dụng thread để chạy tạo kết nối và nhận dữ liệu?? Bởi nếu bạn đặt hàm kết nối vào trong hàm page_load, thì khi khởi động chương trình, nếu hàm pageload chưa được thực hiện xong thì from chưa hiện lên. Mà hàm sever.Listen(10) chỉ thực hiện xong khi có client kết nối tới. Vậy, nếu bạn để hàm kết nối vào trong hàm page_load thì from sẽ không được hiển thị cho đến khi có chương trình client kết nối tới. Giải pháp là đặt hàm đó vào trong 1 thread khác, nó sẽ làm việc của nó, đồng thời from sẽ hiển thị, mà không ảnh hưởng gì đến nhau. và nếu bạn không đặt code xử lý việc nhận dữ liệu từ client vào thread riêng thì nó sẽ không bao giờ thực hiện xong( vòng lặp vô hạn while(true)), và bạn không thể gửi dữ liệu đến client được.

Khi bắt đầu viết chương trình demo này, tôi đã gặp một lỗi, đó là thread a không được dùng đến control đã khai báo ở hàm main( txthienthi). Sau một hổi nhờ đến google, tôi tìm ra giải pháp dùng câu lệnh “CheckForIllegalCrossThreadCalls = false”. C# mặc định thread này không được dùng biến được tạo bởi thread khác, và câu lệnh “CheckForIllegalCrossThreadCalls = false” để cho phép làm điều đó.

chương trình bên phía client thì tương tự, các bạn có thể tìm hiểu code trong chương trình demo,  trương chình phía client không cần phải tạo socket server và lắng nghe kết nối nữa, thay vào đó, sẽ tạo 1 socket để kết nối đến server, phần còn lại hoàn toàn giống nhau.

Và lưu ý,vì chưa bắt các biệt lệ, các bạn phải chạy chương trình server trước chương  trình client(WindowsFormsApplication8 là server, WindowsFormsApplication9 là client).

Link download code 2 chương trình và ebook: http://www.mediafire.com/?r48a4v9qpg3bgb1

About these ads

14 thoughts on “Tut C#: Lập trình mạng và làm việc với thread, demo chương trình chat qua mạng Lan

  1. Cho mình hỏi nêu có nhiều client cùng kết nối với server thì phải dùng thế nào để server chấp nhận tất cả kết nối và nếu server chọn từng kết nối để làm việc riêng biệt thì phải làm thế nào?

      • IP máy ảo mình để địa chỉ là 169.254.0.1, DNS: 127.0.0.1
        còn máy thật mình để động. lúc đấu mình để động hết nhưng không được. sau đó mình làm như trên cũng không được

      • quan trọng là ip của máy dùng làm server bạn ạ. server bạn nên để tĩnh, và thử ping từ máy ảo xem được không đã.
        iep = new IPEndPoint(IPAddress.Parse(“127.0.0.1″), 100);//Định nghĩa điểm kết nối của server, mở cổng 100
        với đoạn code này của server bạn có thể thay 127.0.0.1 bằng IPAddress.any để chấp nhận kết nối gửi đến từ mọi ip, làm như vậy thì ip của máy ảo có thể động. nhưng ip của server thì phải là tĩnh bạn nhé.
        bạn cấu hình tĩnh, sau đó ping thử, nếu được thì sửa ip của server ở chương trình client.
        chúc bạn thành công.

      • mình đã đặt ip của cả 2 máy đều tĩnh rùi. mình cũng thử ping giữa 2 máy thì vẫn bình thường
        lúc chạy server trên máy thật thì không vấn đề gì nhưng khi chạy client trên máy ảo thì gặp lỗi ở code client.Connect(iep) cùng với thông báo lỗi No connection could be made because the target machine actively refused it 127.0.0.1:100
        mình không biết phải sửa như thế nào nữa? cảm ơn bạn! :D

  2. 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 General;
    using System.Runtime.Remoting.Channels;
    using System.Collections;
    using System.Runtime.Remoting.Channels.Http;
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.Remoting;
    using System.Threading;

    namespace Client
    {
    public partial class FormLogin : Form
    {
    internal IRemote obj;
    private String Username;
    private String IP;
    private WrapperTransporterClass wtc,wtc1;
    public FormLogin()
    {
    InitializeComponent();
    AcceptButton = btnOk;
    wtc = new WrapperTransporterClass();

    }

    private void MakeConnectionToServer(string IP)
    {
    try
    {
    BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
    BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
    serverProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

    IDictionary props = new Hashtable();
    props["port"] = 0;
    string s = System.Guid.NewGuid().ToString();
    props["name"] = s;
    props["typeFilterLevel"] = TypeFilterLevel.Full;

    HttpChannel chan = new HttpChannel(props, clientProvider, serverProvider);

    int Port = 6123;
    ChannelServices.RegisterChannel(chan);
    obj = (IRemote)Activator.GetObject(typeof(IRemote), “http://” + IP + “:” + Port + “/RemoteObject.soap”, WellKnownObjectMode.SingleCall);

    }
    catch (Exception ex)
    {
    MessageBox.Show(“Unable to connect to server”);
    }
    }

    private void rdbtnLocalhost_CheckedChanged(object sender, EventArgs e)
    {
    if (rdbtnLocalhost.Checked == true)
    {
    txtIp.Enabled = false;
    }
    else
    {
    txtIp.Enabled = true;
    }
    }

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

    private void btnOk_Click(object sender, EventArgs e)
    {
    if (rdbtnLocalhost.Checked)
    {
    IP = “127.0.0.1″;
    }
    else
    {
    IP = txtIp.Text;
    }
    Username = txtDesiredName.Text;
    try
    {
    MakeConnectionToServer(IP);

    if (obj.SignIn(Username))
    {
    (new Thread(new ThreadStart(RunFormChat))).Start();
    this.Close();
    }
    }
    catch (Exception ex)
    {
    MessageBox.Show(“Unable to connect to server of IP ” + IP);
    }
    }

    private void RunFormChat()
    {
    Application.Run(new FormChat(obj, Username));
    }

    private void FormLogin_Load(object sender, EventArgs e)
    {

    }

    private void label1_Click(object sender, EventArgs e)
    {

    }

    private void txtIp_TextChanged(object sender, EventArgs e)
    {

    }

    private void txtDesiredName_TextChanged(object sender, EventArgs e)
    {

    }
    }
    }

Gửi phản hồi

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

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Thay đổi )

Twitter picture

You are commenting using your Twitter account. Log Out / Thay đổi )

Facebook photo

You are commenting using your Facebook account. Log Out / Thay đổi )

Google+ photo

You are commenting using your Google+ account. Log Out / Thay đổi )

Connecting to %s