Giáo trình Lập trình mạng (Phần 1)
Tóm tắt Giáo trình Lập trình mạng (Phần 1): ... = Encoding.ASCII.GetString(data, 0, rec); Console.WriteLine(s); } client.Close(); } } Chương trình Server sử dụng NetworkStream: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Sockets; class Prog...tSend.Clear(); if (txtSend.Text.ToUpper().Equals("QUIT")) this.Dispose(); } private void btConnect_Click(object sender, EventArgs e) { Thread tuyen = new Thread(new ThreadStart(NhanDl)); tuyen.Start(); } private void NhanDl() { UdpClient receiver = new UdpClient(int.Parse(txt...eceive(data); if (recv == 0) break; Console.WriteLine( Encoding.ASCII.GetString(data, 0, recv)); client.Send(data, recv, 0); } } Console.WriteLine("Disconnected from {0}", newclient.Address); client.Close(); newsock.Close(); } } Sau đây chúng ta sẽ viết một c...
ngData);
}
sock.Close();
}
}
2.5. Sử dụng Thread trong các ứng dụng mạng
40
Một số khái niệm
- ða nhiệm (Multitasking): Là khả năng hệ điêu hành làm nhiều cơng việc tại một thời
điểm
- Tiến trình (Process): Khi chạy một ứng dụng, hệ điều hành sẽ cấp phát riêng cho ứng
dụng đĩ bộ nhớ và các tài nguyên khác. Bộ nhớ và tài nguyên vật lý riêng biệt này
được gọi là một tiến trình. Các tài nguyên và bộ nhớ của một tiến trình thì chỉ tiến
trình đĩ được phép truy cập.
- Tuyến (Thread): Trong hệ thống, một tiến trình cĩ thể cĩ một hoặc nhiều chuỗi thực
hiện tách biệt nhau và cĩ thể chạy đồng thời. Mỗi chuỗi thực hiện này được gọi là một
tuyến (Thread). Trong một ứng dụng, Thread khởi tạo đầu tiên gọi là Thread sơ cấp
hay Thread chính.
2.5.1. Sử dụng Thread trong chương trình .Net
ðể sử dụng Thread trong .Net ta sử dụng NameSpace System.Threading
- Một số phương thức thường dùng
Public Method
Name
Mơ tả
Abort() Kết thúc Thread
Join() Buộc chương trình phải chờ cho thread kết thúc (Block) thì mới thực hiện
tiếp (các câu lệnh đứng sau Join).
Resume() Tiếp tục chạy thread đã bị tạm ngưng - suspended.
Sleep() Static method : Tạm dừng thread trong một khoảng thời gian.
Start() Bắt đầu chạy (khởi động) một thread. Sau khi gọi phương thức này, trạng
thái của thread chuyển từ trạng thái hiện hành sang Running.
Suspend() Tạm ngưng (nghỉ) thread. (Phương thức này đã bị loại khỏi phiên bản
VS.NET 2005)
- Một số thuộc tính thường dùng:
Public Property
Name
Mơ tả
CurrentThread This static property: Trả về thread hiện hành đang chạy.
IsAlive Trả về giá trị cho biết trạng thái thực thi của thread hiện hành.
IsBackground Sets or gets giá trị cho biết là thread là background hay foreground
thread.
IsThreadPoolThread Gets a value indicating whether a thread is part of a thread pool.
Priority Sets or gets giá trị để chỉ định độ ưu tiên (dành nhiều hay ít CPU cho
thread). Cao nhất là 4, thấp nhất là 0.
41
Public Property
Name
Mơ tả
ThreadState Lấy về trạng thái của thread (đang dừng, hay đang chạy)
- Tạo một tuyến trong C#
Thread newThread=newThread(new ThreadStart(newMethod));
.
}
void newMethod() {
. . .
}
2.5.2. Sử dụng Thread trong các chương trình Server
- ða tuyên hay được ứng dụng trong các chương trình Server, các chương trình
địi hỏi tại một thời điểm chấp nhận nhiều kết nối đến từ các Client.
- ðể các chương trình Server cĩ thể xử lý nhiều Client tại một thời điểm ta cĩ
mơ hình ứng dụng đa tuyến như sau:
Sau đây chúng ta viết lại chương trình DateTimeServer cĩ sử dụng Thread như sau:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
class Program {
static void Main(string[] args) {
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2009);
TcpListener server = new TcpListener(iep);
server.Start();
while (true) {
42
//chap nhan ket noi
TcpClient client= server.AcceptTcpClient();
//Tao ra tuyen moi de xu ly moi Client
new ClientThread(client);
}
server.Stop();
}
}
class ClientThread {
private Thread tuyen;
private TcpClient client;
public ClientThread(TcpClient client) {
this.client = client;
tuyen = new Thread(new ThreadStart(GuiNhanDL));
tuyen.Start();
}
private void GuiNhanDL() {
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw= new StreamWriter(client.GetStream());
string kq="";
while(true)
{
string s=sr.ReadLine();
s=s.ToUpper();
if(s.Equals("QUIT")) break;
if(s.Equals("GETDATE"))
kq=DateTime.Now.ToString("dd/MM/yyyy");
else
if(s.Equals("GETTIME"))
kq=DateTime.Now.ToString("hh:mm:ss");
else
kq="Khong hieu lenh";
sw.WriteLine(kq);
sw.Flush();
}
client.Close();
}
}
2.5.3. Sử dụng Thread để gửi/nhận dữ liệu
Ứng dụng đa tuyến trong việc gửi nhận dữ liệu chúng ta viết chương trình Chat
theo giao thức TCP như sau:
43
Ứng dụng mơ hình xử lý sự kiện của Windows và đa tuyến và Socket khơng
đồng bộ ta chỉ cần viết một chương trình sau đĩ dịch ra, ta chạy ứng dụng nhấn Listen
nĩ sẽ lắng nghe trong vai trị Server cịn khi ta chạy và nhấn Connect nĩ sẽ đĩng vai
trị Client và kết nối tới Server.
Văn bản chương trình như sau:
using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
44
using System.Windows.Forms;
class TcpChat:Form
{
private static TextBox newText;
private static ListBox results;
private static Socket client;
private static byte[] data = new byte[1024];
public TcpChat()
{
Text = "TCP Chat Program";
Size = new Size(400, 380);
Label label1 = new Label();
label1.Parent = this;
label1.Text = "Enter text string:";
label1.AutoSize = true;
label1.Location = new Point(10, 30);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
results = new ListBox();
results.Parent = this;
results.Location = new Point(10, 85);
results.Size = new Size(360, 18 * Font.Height);
Button sendit = new Button();
sendit.Parent = this;
sendit.Text = "Send";
sendit.Location = new Point(220,52);
sendit.Size = new Size(5 * Font.Height, 2 * Font.Height);
sendit.Click += new EventHandler(ButtonSendOnClick);
Button connect = new Button();
connect.Parent = this;
connect.Text = "Connect";
connect.Location = new Point(295, 20);
connect.Size = new Size(6 * Font.Height, 2 * Font.Height);
connect.Click += new EventHandler(ButtonConnectOnClick);
Button listen = new Button();
listen.Parent = this;
listen.Text = "Listen";
listen.Location = new Point(295,52);
listen.Size = new Size(6 * Font.Height, 2 * Font.Height);
listen.Click += new EventHandler(ButtonListenOnClick);
}
void ButtonListenOnClick(object obj, EventArgs ea)
{
results.Items.Add("Listening for a client...");
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
45
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
newsock.Bind(iep);
newsock.Listen(5);
newsock.BeginAccept(new AsyncCallback(AcceptConn), newsock);
}
void ButtonConnectOnClick(object obj, EventArgs ea)
{
results.Items.Add("Connecting...");
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
client.BeginConnect(iep, new AsyncCallback(Connected), client);
}
void ButtonSendOnClick(object obj, EventArgs ea)
{
byte[] message = Encoding.ASCII.GetBytes(newText.Text);
newText.Clear();
client.BeginSend(message, 0, message.Length, 0,
new AsyncCallback(SendData), client);
}
void AcceptConn(IAsyncResult iar)
{
Socket oldserver = (Socket)iar.AsyncState;
client = oldserver.EndAccept(iar);
results.Items.Add("Connection from: " + client.RemoteEndPoint.ToString());
Thread receiver = new Thread(new ThreadStart(ReceiveData));
receiver.Start();
}
void Connected(IAsyncResult iar)
{
try
{
client.EndConnect(iar);
results.Items.Add("Connected to: " + client.RemoteEndPoint.ToString());
Thread receiver = new Thread(new ThreadStart(ReceiveData));
receiver.Start();
} catch (SocketException)
{
results.Items.Add("Error connecting");
}
}
void SendData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
}
void ReceiveData()
{
int recv;
46
string stringData;
while (true)
{
recv = client.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
if (stringData == "bye")
break;
results.Items.Add(stringData);
}
stringData = "bye";
byte[] message = Encoding.ASCII.GetBytes(stringData);
client.Send(message);
client.Close();
results.Items.Add("Connection stopped");
return;
}
public static void Main()
{
Application.Run(new TcpChat());
}
}
2.5.4. Sử dụng ThreadPool trong các chương trình .Net
Method Description
BindHandle() Binds an operating system handle to the
thread pool
GetAvailableThreads() Gets the number of worker threads
available for use in the thread pool
GetMaxThreads() Gets the maximum number of worker
threads available in the thread pool
QueueUserWorkItem() Queues a user delegate to the thread
pool
RegisterWaitForSingleObject() Registers a delegate waiting for a
WaitHandle object
UnsafeQueueUserWorkItem() Queues an unsafe user delegate to the
thread pool but does not propagate the
calling stack onto the worker thread
UnsafeRegisterWaitForSingleObject() Registers an unsafe delegate waiting for
a WaitHandle object
using System;
using System.Threading;
class ThreadPoolSample
{
47
public static void Main()
{
ThreadPoolSample tps = new ThreadPoolSample();
}
public ThreadPoolSample()
{
int i;
ThreadPool.QueueUserWorkItem(new WaitCallback(Counter));
ThreadPool.QueueUserWorkItem(new WaitCallback(Counter2));
for(i = 0; i < 10; i++)
{
Console.WriteLine("main: {0}", i);
Thread.Sleep(1000);
}
}
void Counter(object state)
{
int i;
for (i = 0; i < 10; i++)
{
Console.WriteLine(" thread: {0}", i);
Thread.Sleep(2000);
}
}
void Counter2(object state)
{
int i;
for (i = 0; i < 10; i++)
{
Console.WriteLine(" thread2: {0}", i);
Thread.Sleep(3000);
}
}
}
2.5.5. Sử dụng ThreadPool trong các chương trình Server
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
class ThreadPoolTcpSrvr
{
private TcpListener client;
public ThreadPoolTcpSrvr()
{
client = new TcpListener(9050);
client.Start();
Console.WriteLine("Waiting for clients...");
while(true)
{
while (!client.Pending())
{
Thread.Sleep(1000);
}
ConnectionThread newconnection = new ConnectionThread();
newconnection.threadListener = this.client;
ThreadPool.QueueUserWorkItem(new
WaitCallback(newconnection.HandleConnection));
}
}
public static void Main()
{
ThreadPoolTcpSrvr tpts = new ThreadPoolTcpSrvr();
48
}
}
class ConnectionThread
{
public TcpListener threadListener;
private static int connections = 0;
public void HandleConnection(object state)
{
int recv;
byte[] data = new byte[1024];
TcpClient client = threadListener.AcceptTcpClient();
NetworkStream ns = client.GetStream();
connections++;
Console.WriteLine("New client accepted: {0} active connections",
connections);
string welcome = "Welcome to my test server";
data = Encoding.ASCII.GetBytes(welcome);
ns.Write(data, 0, data.Length);
while(true)
{
data = new byte[1024];
recv = ns.Read(data, 0, data.Length);
if (recv == 0)
break;
ns.Write(data, 0, recv);
}
ns.Close();
client.Close();
connections—;
Console.WriteLine("Client disconnected: {0} active connections",
connections);
}
}
2.6. Kỹ thuật IP Multicasting
2.6.1. Broadcasting là gì?
Broadcast, tiếng Việt gọi là quảng bá. Trong hệ thống mạng hữu tuyến, quảng bá là
thuật ngữ dùng để chỉ việc gửi một gĩi thơng tin đến tất các nút mạng trong mạng. ðể
thực hiện hình thức quảng bá, địa chỉ đến của gĩi tin sẽ là địa chỉ quảng bá.
Cĩ hai loại là: Local Broadcast và Global Broadcast
2.6.2. Sử dụng Broadcasting để gửi dữ liệu đến nhiều máy trong mạng cục bộ
Gửi gĩi dữ liệu Broadcast
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class BadBroadcast {
public static void Main() {
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, 9050);
byte[] data = Encoding.ASCII.GetBytes("This is a test message");
sock.SendTo(data, iep);
sock.Close();
}
49
}
Chúng ta phải thiết lập như sau:
class Broadcst {
public static void Main() {
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
IPEndPoint iep1 = new IPEndPoint(IPAddress.Broadcast, 9050);
IPEndPoint iep2 = new IPEndPoint(IPAddress.Parse("192.168.1.255"), 9050);
string hostname = Dns.GetHostName();
byte[] data = Encoding.ASCII.GetBytes(hostname);
sock.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Broadcast, 1);
sock.SendTo(data, iep1);
sock.SendTo(data, iep2);
sock.Close();
}
}
Nhận gĩi dữ liệu Broadcast
class RecvBroadcst {
public static void Main() {
Socket sock = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,
ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
sock.Bind(iep); EndPoint ep = (EndPoint)iep;
Console.WriteLine("Ready to receive"); byte[] data = new byte[1024];
int recv = sock.ReceiveFrom(data, ref ep);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine("received: {0} from: {1}", stringData, ep.ToString());
data = new byte[1024]; recv = sock.ReceiveFrom(data, ref ep);
stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine("received: {0} from: {1}",stringData, ep.ToString());
sock.Close();
}
}
2.6.3. Multicasting là gì?
Một địa chỉ multicast cho phép thiết bị gửi dữ liệu tới một tập xác định trước các host,
được biết đến như các nhĩm multicast, trong các mạng con khác nhau.
Một số địa chỉ Multicast
ðịa chỉ multicast Chức năng
224.0.0.0 ðịa chỉ cơ sở
224.0.0.1 Tất cả các hệ thống trên mạng con này
224.0.0.2 Tất cả các Router trên mạng con này
50
224.0.0.5 Các DR trong OSPF
224.0.1.9 Nhĩm địa chỉ RIPv2
224.0.1.24 Nhĩm địa chỉ WINS server
Cĩ 2 kỹ thuật Multicast được sử dụng
+ Peer to Peer
+ Central Server
2.6.4. Socket Multicasting trong .Net
Sử dụng phương thức SetSocketOption()
51
Socket option cĩ thể được sử dụng để
Thêm một Socket vào nhĩm Multicast
Loại một Socket khỏi nhĩm Multicast
SetSocketOption(SocketOptionLevel,SocketOptionName, optionValue)
SocketOptionName
AddMembership
DropMembership
Sử dụng phương thức SetSocketOption()
Socket option cĩ thể được sử dụng để
optionValue là một đối tượng của lớp MulticastOption
MulticastOption(IPAddress) MulticastOption(IPAddress,IPAddress)
Ví dụ thêm một Socket vào nhĩm Multicast 224.100.0.1
sock.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership, new
MulticastOption(IPAddress.Parse("224.100.0.1"));
Gửi dữ liệu Multicast
class MultiSend{
public static void Main() {
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("224.100.0.1"), 9050);
byte[] data = Encoding.ASCII.GetBytes("This is a test message");
server.SendTo(data, iep);
server.Close();
}
}
Nhận dữ liệu Multicast
class MultiRecv{
public static void Main() {
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
Console.WriteLine("Ready to receive");
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
EndPoint ep = (EndPoint)iep;
sock.Bind(iep);
sock.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse("224.100.0.1")));
byte[] data = new byte[1024];
int recv = sock.ReceiveFrom(data, ref ep);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine("received: {0} from: {1}", stringData, ep.ToString());
sock.Close();
}
}
52
Gửi dữ liệu Multicast với TTL
class NewMultiSend{
public static void Main() {
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9051);
IPEndPoint iep2 = new IPEndPoint(IPAddress.Parse("224.100.0.1"), 9050);
server.Bind(iep);
byte[] data = Encoding.ASCII.GetBytes("This is a test message");
server.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse("224.100.0.1")));
server.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.MulticastTimeToLive, 50);
server.SendTo(data, iep2);
server.Close();
}
}
Multicast với lớp UdpClient
JoinMulticastGroup()
DropMulticastGroup()
JoinMulticastGroup() là phương thức overload
JoinMulticastGroup(IPAddress)
JoinMulticastGroup(IPAddress, int)
class UdpClientMultiSend{
public static void Main() {
UdpClient sock = new UdpClient();
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("224.100.0.1"), 9050);
byte[] data = Encoding.ASCII.GetBytes("This is a test message");
sock.Send(data, data.Length, iep);
sock.Close();
}
}
class UdpClientMultiRecv
{
public static void Main()
{
UdpClient sock = new UdpClient(9050);
Console.WriteLine("Ready to receive");
sock.JoinMulticastGroup(IPAddress.Parse("224.100.0.1"), 50);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
byte[] data = sock.Receive(ref iep);
string stringData = Encoding.ASCII.GetString(data, 0, data.Length);
Console.WriteLine("received: {0} from: {1}", stringData, iep.ToString());
sock.Close();
}
}
53
2.7 Bài tập áp dụng
class MulticastChat : Form{
TextBox newText;
ListBox results;
Socket sock;
Thread receiver;
IPEndPoint multiep = new IPEndPoint(IPAddress.Parse("224.100.0.1"), 9050);
public MulticastChat() {
Text = "Multicast Chat Program";
Size = new Size(400, 380);
Label label1 = new Label();
label1.Parent = this;
label1.Text = "Enter text string:";
label1.AutoSize = true;
label1.Location = new Point(10, 30);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
results = new ListBox();
results.Parent = this;
results.Location = new Point(10, 85);
results.Size = new Size(360, 18 * Font.Height);
Button sendit = new Button();
sendit.Parent = this;
sendit.Text = "Send";
sendit.Location = new Point(220, 52);
sendit.Size = new Size(5 * Font.Height, 2 * Font.Height);
sendit.Click += new EventHandler(ButtonSendOnClick);
Button closeit = new Button();
closeit.Parent = this;
closeit.Text = "Close";
closeit.Location = new Point(290, 52);
closeit.Size = new Size(5 * Font.Height, 2 * Font.Height);
closeit.Click += new EventHandler(ButtonCloseOnClick);
sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
sock.Bind(iep);
sock.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership,
54
new MulticastOption(IPAddress.Parse("224.100.0.1")));
receiver = new Thread(new ThreadStart(packetReceive));
receiver.IsBackground = true;
receiver.Start();
}
void ButtonSendOnClick(object obj, EventArgs ea) {
byte[] message = Encoding.ASCII.GetBytes(newText.Text);
newText.Clear();
sock.SendTo(message, SocketFlags.None, multiep);
}
void ButtonCloseOnClick(object obj, EventArgs ea) {
receiver.Abort();
sock.Close();
Close();
}
void packetReceive() {
EndPoint ep = (EndPoint)multiep;
byte[] data = new byte[1024];
string stringData;
int recv;
while (true) {
recv = sock.ReceiveFrom(data, ref ep);
stringData = Encoding.ASCII.GetString(data, 0, recv);
results.Items.Add("from " + ep.ToString() + ": " + stringData);
}
}
public static void Main() {
Application.Run(new MulticastChat());
}
}
File đính kèm:
giao_trinh_lap_trinh_mang_phan_1.pdf



