Upload
chaunguyen83
View
484
Download
2
Embed Size (px)
Citation preview
Buổi 1
LẬP TRÌNH MẠNG CĂN BẢN
Hồng Nghi - MMT
Nguồn Tham khảo
2
Network programming in NET with CSharp and VB.NET, Fiach Reid, Digital Press, 2004
C# Network Programming, Richard Blum, Sybex, 2003
Slide bài giảng lập trình mạng căn bản của Thầy Trần Bá Nhiệm – Khoa MMT&TT
Và một số trang Web khác
Mục tiêu
3
Cung cấp các kiến thức cơ bản về lập trình ứng dụng mạng
– Xây dựng ứng dụng Server.
– Xây dựng ứng dụng Client.
– Các kỹ thuật vào ra.
Cung cấp các kỹ năng cần thiết để thiết kế và xây dựng ứng dụng mạng
– Sử dụng thư viện, môi trường, tài liệu.
– Thiết kế, xây dựng chương trình
Tổng quan về mạng máy tính
4
Mô hình OSI
5
Được tổ chức tiêu chuẩn hóa ISO đưa ra nhằm cung cấp 1 mô hình chuẩn cho các nhà sản xuất và cung cấp sản phẩm viễn thông.
Ý tưởng mô hình hóa tạo ra hỗ trợ cho việc kết nối giữa các hệ thống và modun hóa các thành phần phục vụ mạng viễn thông
Mô hình OSI (Open Systems
Interconnection )
6
Tầng ứng dụng (Application layer): cung cấp các phương tiện cho người sử dụng sử dụng các dịch vụ của mạng.
Tầng trình bày (Presentation layer): quy định biểu diễn dữ liệu.
Tầng phiên (Session layer): quản lý các phiên của ứng dụng.
Tầng vận chuyển (Transport layer): quy định kết nối end-to-end .
Tầng mạng (Network layer): quy định địa chỉ mạng, định tuyến, truyền dữ liệu.
Tầng liên kết (Data link layer): điều khiển kết nối, truy xuất đường truyền.
Tầng vật lý (Physical layer): đường truyền vật lý, các chuẩn về điện, dây cáp, đầu nối..
Mô hình TCP/IP(Internet control
message protocol)
7
Application: cung cấp các dịch vụ cho các
ứng dụng mạng
FTP, SMTP, HTTP
Transport: truyền dữ liệu giữa các tiến trình
TCP, UDP
Network: dẫn đường cho các gói tin từ nguồn
đến đích
IP, các giao thức dẫn đường
Link: truyền dữ liệu giữa các trạm lân cận
PPP, Ethernet
Physical: các bit “trên đường dây”
Các họ giao thức
8
Giao thức TCP
9
Hướng kết nối: connection orientedHai bên phải thiết lập kênh truyền trước khi truyền
dữ liệu.
Được thực hiện bởi quá trình gọi là bắt tay ba bước (three ways handshake).
• Truyền dữ liệu theo dòng (stream oriented): tự động phân chia dòng dữ liệu thành các đoạn nhỏ để truyền đi, tự động ghép các đoạn nhỏ thành dòng dữ liệu và gửi trả ứng dụng.
• Đúng trật tự (ordering guarantee): dữ liệu gửi trước sẽ được nhận trước
Giao thức TCP
10
Đặc tính của TCP Tin cậy, chính xác: thông tin gửi đi sẽ được đảm bảo đến đích, không
dư thừa, sai sót...Độ trễ lớn, khó đáp ứng được tính thời gian thực.
Cổng giao thức
11
Mỗi máy tính có nhiều ứng dụng chạy đồng thời.
Dữ liệu có kèm theo thông tin cho biết ai đang sử dụng nó
Dùng để phân biệt 2 ứng dụng mạng với nhau đi kèm với IP và Socket
Nằm trong khoảng 1…65535.
Giao thức UDP
12
Giao thức UDP: User Datagram ProtocolCũng là giao thức lõi trong TCP/IP.
Cung cấp dịch vụ truyền dữ liệu giữa các ứng dụng.
UDP chia nhỏ dữ liệu ra thành các datagram
Sử dụng trong các ứng dụng khắt khe về mặt thời gian, chấp nhận sai sót: thoại, video, game...
• Đặc tính của UDPKhông cần thiết lập kết nối trước khi truyền (Connectionless).
Nhanh, chiếm ít tài nguyên dễ xử lý.
Hạn chế: Không có cơ chế báo gửi (report).
Không đảm báo trật tự các datagram (ordering).
Không phát hiện được mất mát hoặc trùng lặp thông tin (loss, duplication).
UDP header
13
Cổng giao thức
14
Địa chỉ Ip và các lớp dành riêng
15
Mỗi máy tính kết nối vào internet phải có 1 địa chỉ IP.
Địa chỉ 127.0.0.1 là địa chỉ loopback.
Địa chỉ IP của 1 máy có thể được cấp bởi DHCP server .
Địa chỉ mạng
16
Địa chỉ broadcast
17
Hệ thống phân giản tên miền DNS
18
Các tên miền được phân cấp và quản lý bởi INTERNIC
Cấp cao nhất là ROOT, sau đó là cấp 1, cấp 2,...
Tổng quan về lập trình mạng
19
Sơ lược về lập trình mạng
20
Lập trình mạng là các kỹ thuật lập trình nhằm xây dựng ứng dụng, phần mềm khai thác hiệu quả tài nguyên mạng máy tính.
Ngôn ngữ lập trình mạngC/C++: Mạnh và phổ biến, được hầu hết các lập trình
viên sử dụng để viết các ứng dụng mạng hiệu năng cao.Java: Khá thông dụng, sử dụng nhiều trong các điện
thoại di động (J2ME).C#: Mạnh và dễ sử dụng, tuy nhiên chạy trên nền .Net
Framework và chỉ hỗ trợ họ hệ điều hành Windows.Python, Perl, PHP...Ngôn ngữ thông dịch, sử dụng để
viết các tiện ích nhỏ, nhanh chóngGiáo trình này sẽ chỉ đề cập đến hai ngôn ngữ C/C++và
C#.
Thư viện lập trình mạng
21
Windows SocketAPI ( WinSock) Thư viện liên kết động (WS2_32.DLL) đi kèm trong hệ điều hành
Windows của Microsoft. Thường sử dụng cùng với C/C++. Cho hiệu năng cao nhất.
System.Net và System.Net.Sockets Hai namespace trong bộ thư viện .NET của Microsoft Dễ sử dụng Thường sử dụng với C#
• MFC SocketNằm trong bộ thư viện MFC của MicrosoftĐóng gói các hàm của WinSock dưới dạng các lớp hướng đối tượng.Dễ sử dụng và hiệu năng cao.
• Các thư viện của các ngôn ngữ khác: Java, PHP, Python...
• Thư viện sử dụng trong giáo trình: WinSock, MFC Socket, System.Net và System.Net.Sockets
Công cụ lập trình
22
Visual Studio (6.0, 2003 .NET, 2005, 2008, 2010, 2013)
Rất mạnh
Hỗ trợ cả WinSock, MFC Socket và .NET Socket (Phiênbản 2003.NET trở lên).
Dev C++
Miễn phí
Chỉ hỗ trợ WinSock
Công cụ hỗ trợ
23
TCPView: Hiển thị các kết nối hiện tại của máy tính.
Resource Monitor: ~ TCPView.
Wireshark.
Netcat
.NET FrameWork
24
Không phải là NNLT
Cung cấp framework cho 4 ngôn ngữ lập trìnhlàm việc với nhau gồm C#, VB.Net, Managed C++, J# .Net
Định nghĩa FrameWork Class Library(FCL)
Comman Laguage Runtime (CRL) là một máy ảođược thiết kết riêng cho Windows, là môi trườngthực thi cho các ứng dụng.
Các ngôn ngữ trong .Net có khả năng làm việchợp tác với nhau rất tốt.Vd: Class đã biên dịchtrong c# dùng được trong VB.Net
.NET FrameWork
25
Gồm có 3 thành phần: Các ngôn ngữ lập trình
Thư viện các lớp (Framework Class Library - FCL)
The Common Language Runtime (CLR)
.NET dùng trình biên dịch Just-in-time (JIT).
Mọi lớp trong .NET đều kế thừa từSystem.Object
2 ngôn ngữ phổ biến nhất là VC# và VB.NET
Không hỗ trợ đa kế thừa
Microsoft Intermediate Language(MSIL)
26
Các ứng dụng phát triển trên nền .NET Framework, sau khi biên dịch, không được dịch ra ngôn ngữ máy mà được biên dịch sang một ngôn ngữ có tên là: Microsoft Intermediate Language (MSIL)
Common Language Runtime
27
Sau khi biên dịch lần đầu tiên chạy chương trình, ngôn ngữ MSIL sẽ được biên dịch sang ngôn ngữ máy.
Quá trình chuyển đổi từ MSIL sang ngôn ngữ máy lúc thực thi chương trình được thực hiện bởi thành phần Just – in – time compiler nằm trong CLR (Common Language Runtime).
Các thành phần chính của Visual Studio
28
Biến và qui tắc đặt tên biến
29
Biến là đại lượng dùng để chứa dữ liệu.
Ví dụ bạn muốn nhập tuổi của một người từ bàn phím vào trong chương trình, khi đó trong chương trình của bạn phải có một biến để chứa tuổi của người đó.
Cú pháp khai báo biến:
<Kiểu dữ liệu> Tên biến;
Hoặc vừa khai báo biến vừa khỏi tạo giá trị
<Kiểu dữ liệu> Tên biến = giá trị ;
Mỗi một biến được cấp phát một vùng nhớ, kích thước của vùng nhớ phụ thuộc vào kiểu dữ liệu của biến, và vùng nhớ đó có một địa chỉ xác định.
Qui tắc đặt tên biến: Tên biến không được trùng với tên từ khóa Không được đặt tên biến bắt đầu bằng kí số Ngôn ngữ C# phân biệt chữ hoa và chữ thường.
Kiểu dữ liệu
30
Hướng đối tượng trong C#
31
Namespace, lớp, đối tượng
Construtors và Destructors
Nạp chồng phương thức( Overloading)
Các phương thức chồng toán tử( Operator Overloading)
Ghi đè (Overriding)
Kế thừa( Inheritance)
Namespace
32
Dùng để quản lý, tránh đặt trùng tên lớp
Giảm bớt sự phức tạp khi chạy cùng các ứng dụng khác
Có thể khai báo các namespace bên trong các namespace khác
Ví dụ:namespace NameSpace{// khai báo các lớp
public classA{}public classB{}
}
namespace NameSpace{// khai báo các lớp
public classA{}namespace NameSpace{
//……}
}
Lớp và đối tượng
33
Một kiểu trong C# định nghĩa bằng từ khoá class (và được gọi là lớp) còn thể hiện của lớp được gọi là đối tượng.
Khai báo :class Tên_lớp{//khai báo các thành phần… }
• Ví dụ:
class Sinhvien{
private string mMSSV;private string mHoten; public void In(){….}
}
Phương thức
34
Các phương thức định nghĩa những gì mà một lớp có thể làm
1 phương thức là một hàmVí dụ:
class QLSV{ public void IntenSinhvien(
) { // thực hiện 1 công việc
gì đóSinhvien sv = new
Sinhvien();sv.In();
} }
class Sinhvien{
private string mMSSV;private string mHoten; public void In(){….}
}
Constructors(Hàm khởi tạo)
35
Được gọi khi đối gọi tới đối tượng được tạo
Dùng để khởi tạo đối tượng
Có cùng tên với tên lớp
Có thể có hoặc không có tham số, và không có giá trị trả về
class Sinhvien{
private string mMSSV;private string mHoten; public Sinhvien(){
mMSSV = “DH1111048”;mHoten = “Nguyễn Văn A”;
} public Sinhvien(string mssv, string hoten){
mMSSV = mssv;mHoten = hoten;
}
}
Destructors( Hàm hủy)
36
Hàm hủy là một hàm thành viên được gọi tự động khi đối tượngbị hủy.
Ta không thể gọi trực tiếp, bộ biên dịch kích phát lời gọi đếnhàm hủy khi một đối tượng vượt ra ngoài phạm vi.
Có cùng tên với tên lớp nhưng có thêm dấu ~ phía trước.
class username(){
public:
~username();
};
Không có kiểu trả về, không có đối số.
Destructors
37
Gọi tự động khi đối tượng được hủy
class Sinhvien{
private string mMSSV;private string mHoten; public Sinhvien(){
mMSSV = “DH1111048”;mHoten = “Nguyễn Văn A”;
} ~Sinhvien(){
// thực hiện thao tác}
}
Phương thức Overloading
38
Các phương thức có cùng tên, nhưng khác:
Giá trị trả về
Tham số truyền
Kiểu tham số truyền
class Sinhvien{
private string mMSSV;private string mHoten; public In(){//…
} public In(string mssv){//….
}
}
Phương thức Overriding
39
Lng.
Đ, có
i “ nh vi.
(Base class (Derived class
”
Inheritance(kế thừa)
40
Các lớp với các đặc điểm tương tự nhau có thể được tổ chức thành một sơ đồ phân cấp kế thừa.
Cho phép tạo ra 1 lớp có thuộc tính và phươngthức (kế thừa) của 1 lớp khác. Sau đó xây dựngnhững thuộc tính và phương thức riêng.
Tạo khả năng xây dựng lớp mới từ lớp đã có
Lớp cha trong kế thừa gọi là lớp cơ sở (base class)
Lớp con trong kế thừa gọi là lớp dẫn xuất(derived class)
Inheritance(kế thừa)
41
Inheritance(kế thừa)
42
class TênLớpCon : TênLớpCha
{ // Thân lớp con
}
Hoặc
class TênLớpDẫnXuất : TênLớ pCơSở
{ // Thân lớp dẫ n xuấ t
}
Inheritance(kế thừa)
43
// Lớp cơ sở Point2D
class Point2D
{ public int x,y;public void Xuat2D() {
Console.WriteLine("({0}, {1} )", x, y);}
}
// Lop dan xuat Point3D ke thua tu lop Point2D
class Point3D:Point2D
{ public int z;public void Xuat3D() {
Console.WriteLine("({0}, {1} , {2})", x, y, z);}
}
Polymorphism(Tính đa hình )
44
Đa hình thái, nhiều cách phản ứng khác nhau cho cùng một hành vi
Lớp A có hành vi M()
Lớp B là con của lớp A, trong lớp B viết lại hành viM()
Có biến đối tượng obj
Tại thời điểm t1: obj chỉ đến một thực thể A, obj.M() sẽ cho một phản ứng
Tại thời điểm t2: obj chỉ đến một thực thể B, obj.M() sẽ cho một phản ứng khác
Tính đa hình có được là nhờ kỹ thuật override hành vi giữa 2 lớp cha con.
Từ khóa private và public
45
Delegate và Event
46
Trong lập trình đôi lúc gặp tình huống phải thực thi một hành động nào đó, nhưng lại không biết sẽ gọi phương thức nào của đối tượng nào.
Về mặt kỹ thuật, delegate thuộc kiểu tham chiếu được dùng để đóng gói phương thức đã xác định kiểu trả về và số lượng, kiểu tham số.
Vd: public delegate int WhichIsFirst(object obj1, object obj2)
I/O trong .NET
47
Streams
48
Hai stream quan trọng: networkStream và fileStream
Có 2 cách sử dụng: đồng bộ và bất đồng bộ
Việc chờ 1 tác vụ hoàn thành rồi thực hiện 1 tác vụ khác gây ra một số vấn đề bất tiện.
Bằng cách dùng phương thức gọi đồng bộ tạo được hiệu quả máy tính có thể làm nhiều việc cùng lúc nhưng thực chất là do việc chuyển những tác vụ trong khoảng 1 vài mili giây( Các máy tính hầu như chỉ có 1 CPU)
Streams với files
49
Tạo ra một ứng dụng trong .NET với những thành phần như sau:
Một form
Một File Open Dialog control tên openFileDialog
Một textbox tên tbResults, đặt thuộc tính multiline=true.
Hai buttons tên btnReadAsync và btnReadSyn
• Sử dụngng namespace: using System.IO
Streams với files
50
Khai báo:FileStream fs;byte[] fileContents;AsyncCallback callback;delegate void InfoMessageDel(String info); Khai báo thêm phương thức InfoMessageDel để tránh các thread
tranh chấp tham chiếu đến 1 đối tượng.
• Thêm code xử lý sự kiện click đối tượng btnReadAsync:private void btnReadAsync_Click(object sender, EventArgs e){
openFileDialog.ShowDialog();callback = new AsyncCallback(fs_StateChanged);fs = new FileStream(openFileDialog.FileName, FileMode.Open,FileAccess.Read, FileShare.Read, 4096, true);fileContents = new Byte[fs.Length];fs.BeginRead(fileContents, 0, (int)fs.Length, callback, null);
}
Streams với files
51
Nội dung hàm fs_StateChanged:private void fs_StateChanged(IAsyncResult asyncResult)
{
if (asyncResult.IsCompleted){
string s = Encoding.UTF8.GetString (fileContents);
InfoMessage(s);fs.Close();
}
} Code xử lý sự kiện của btnReadSync :
private voibtnReadSync_Click( object sender, EventArgs e)
{
openFileDialog.ShowDialog();
Thread thdSyncRead = new
Thread(new ThreadStart(syncRead));
thdSyncRead.Start();
}
Streams với files
52
public void syncRead()
{
FileStream fs;
try
{
fs = newFileStream(openFileDialog.FileName, FileMode.OpenOrCreate);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
…..
Tiếp theo try..catch
fs.Seek(0, SeekOrigin.Begin);
byte[] fileContents = new byte[fs.Length];
fs.Read(fileContents, 0, (int)fs.Length);
string s = Encoding.UTF8.GetString(fileContents);
InfoMessage(s);
fs.Close();
}
FileStream
53
Mã hóa dữ liệu
54
Trong ví dụ ở trên Encoding.UTF8.GetString() nhằm chuyển mảng byte thành string.
Các dạng khác Unicode (Encoding.Unicode), ASCII, UTF7.
UTF8 dùng 1 byte cho một ký tự, unicode dùng 2byte cho mỗi ký tự.
Binary và text streams
55
Plain text là 1 kiểu thông dụng cho người dùng dễ sử dụng, dễ đọc, dễ soạn thảo.
Tương lai sẽ thay bằng xml
Viết ứng dụng đếm số dòng có trong 1 file .txt
Binary và text streams
56
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.ShowDialog();
FileStream fs = new FileStream(ofd.FileName, FileMode.OpenOrCreate);
StreamReader sr = new StreamReader(fs);
int lineCount = 0;
while (sr.ReadLine() != null)
{
lineCount++;
}
fs.Close();
MessageBox.Show("Có " + lineCount + " dòng " + ofd.FileName);
}
StreamReader
57
BinaryWriter
58
Serialize và Deserialize
59
BinaryFormatter sử dụng 2 phương thức
Serialize và Deserialize để viết và đọc đối tượng
từ trong luồng:
Serialize:chuyển đổi một đối tượng sang một
định dạng, và có thể được viết vào File mà không
mất dữ liệu.
Deserialize: đọc dữ liệu đã định dạng từ một
File và chuyển nó về dạng ban đầu.
Serialization
60
Serialization là một quá trình để chuyển đổi một cấu trúc dữ liệu hoặc đối tượng thành một định dạng có thể lưu trữ được (ví dụ như trong một file, bộ nhớ, hoặc vận chuyển thông qua mạng), sau đó nó có thể được phục hồi để trở lại trạng thái ban đầu trong một môi trường khác thông qua quá trình deserialization. Rất nhiều ngôn ngữ lập trình hiện nay hỗ trợ kĩ thuật này bao gồm C#, Java, Objective-C, Perl, Python, Ruby, PHP
Attribute [Serializable] và [NonSerialized]
61
Mọi đối tượng muốn được serialize đều phải được khai báo kèm theo attribute [Serializable]. Ngoài ra, mọi kiểu dữ liệu được sử dụng trong đối tượng cũng phải tuân theo quy tắc này.
Nếu bạn muốn loại trừ một thành phần (method, field, property,…) không muốn được serialize, bạn có thể đánh dấu chúng bằng attribute [NonSerialized].
Serialization
62
Ví dụ điển hình là hệ thống đặt hàng, vốn có yêucầu độ an toàn rất cao, vì vậy mỗi lỗi xảy ra phảiđược theo vết chặt chẽ
Để đặt đơn hàng vào stream (trên đĩa hoặc trênmạng) ta có thể ghi mỗi giá trị dưới dạng text, dùng ký tự phân cách,… để xuất và tái tạo cácđối tượng. Tuy nhiên cách dễ dàng nhất là dùngSerialization
Serialization
63
public enum purchaseOrderStates
{ISSUED,DELIVERED,INVOICED,PAID
}
[Serializable()] public class company
{public string name; public string address;public string phone;
}
[Serializable()]
Serialization
64
public class lineItem
{
public string description;
public int quantity;
public double cost;
}
[Serializable()]
public class purchaseOrder
{
private purchaseOrderStates _purchaseOrderStatus;
private DateTime _issuanceDate;
private DateTime _deliveryDate;
private DateTime _invoiceDate;
private DateTime _paymentDate;
public company buyer;
public company vendor;
public string reference;
public lineItem[] items;
Serialization
65
Serialization
public purchaseOrder()
{
_purchaseOrderStatus = purchaseOrderStates.ISSUED;
_issuanceDate = DateTime.Now;
}
public void recordDelivery()
{
if (_purchaseOrderStatus == purchaseOrderStates.ISSUED)
{
_purchaseOrderStatus = purchaseOrderStates.DELIVERED; _deliveryDate = DateTime.Now;
}
}
Serialization với SoapFormatter
66
Serialize đối tượng thành định dạng XML để truyền tải thông tin giữa các ứng dụng qua mạng thông qua giao thức HTTP. Do là dạng văn bản nên dung lượng dữ liệu tạo ra sẽ nặng hơn so với BinaryFormatter.
Hỗ trợ trong( System.Runtime.Serialization.Formatters.Soap)
Serialization dùng SoapFormatter
67
company Vendor = new company();
company Buyer = new company();
lineItem Goods = new lineItem();
purchaseOrder po = new purchaseOrder();
Vendor.name = "Acme Inc.";
Buyer.name = "Wiley E. Coyote";
Goods.description = "anti-RoadRunner cannon";
Goods.quantity = 1;
Goods.cost = 599.99;
po.items = new lineItem[1];
po.items[0] = Goods;
po.buyer = Buyer;
po.vendor = Vendor;
SoapFormatter sf = new SoapFormatter();
FileStream fs = File.Create("..\\po.xml");
sf.Serialize(fs, po);
fs.Close();
Deserialization dùng SoapFormatter
68
SoapFormatter sf = new SoapFormatter(); FileStream fs = File.OpenRead("..\\po.xml");purchaseOrder po = (purchaseOrder)sf.Deserialize(fs);fs.Close();MessageBox.Show("Customer is " + po.buyer.name +
"\nVendor is " + po.vendor.name + ", phone is " + po.vendor.phone +"\nItem is " + po.items[0].description + " has quantity " +po.items[0].quantity.ToString() + ", has cost " + po.items[0].cost.ToString());
SoapFormatter
69
Simple Object Access Protocol (SOAP)
Serialization dùng BinaryFormatter
70
Định dạng của SOAP tương đối ấn tượng, tuy nhiên không gọn nhẹ nên khá tiêu tốn băng thông đường truyền
Trong trường hợp ấy, phương pháp khả thi hơn là BinaryFormatter company Vendor = new company(); company Buyer = new company(); lineItem Goods = new lineItem();//tương tự ví dụ SoapFormatterBinaryFormatter bf = new BinaryFormatter(); FileStream fs = File.Create("..\\po_bin.txt"); bf.Serialize(fs, po); fs.Close();MessageBox.Show("Serialize succesful!", "Info");
Deserialization dùng BinaryFormatter
71
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.OpenRead("..\\po_bin.txt");
purchaseOrder po =(purchaseOrder)bf.Deserialize(fs);
fs.Close();
MessageBox.Show("Customer is " + po.buyer.name);
Kết quả minh họa BinaryFormatter
72
Serialization dùng XmlSerializer
73
company Vendor = new company(); company Buyer = new company();
lineItem Goods = new lineItem();
//tương tự ví dụ SoapFormatter
XmlSerializer xs = new
XmlSerializer(po.GetType());
FileStream fs = File.Create("..\\po1.xml");
xs.Serialize(fs, po);
fs.Close();
Deserialization dùng XmlSerializer
74
purchaseOrder po = new purchaseOrder();
XmlSerializer xs = new XmlSerializer(po.GetType());
FileStream fs = File.OpenRead("..\\po1.xml");
po = (purchaseOrder)xs.Deserialize(fs);
fs.Close();
MessageBox.Show("Customer is " + po.buyer.name
+ "\nVendor is " + po.vendor.name
+ ", phone is " + po.vendor.phone
+ "\nItem is " + po.items[0].description
+ " has quantity "
+ po.items[0].quantity.ToString()
+ ", has cost " + po.items[0].cost.ToString());
XmlSerializer
75
Kết quả minh họa XmlSerializer
76
XmlSerializer, BinaryFormatter ,SoapFormatter?
77
XmlSerializer dùng cho dịch vụ Web.
BinaryFormatter/SoapFormatter dùng choRemoting.
SoapFormatter dùng để tuần tự hóa 1 đối tượng thành SOAP.
BinaryFormatter dùng để tuần tự hóa 1 đối tượng thành dạng nhị phân.
XmlSerializer chỉ chuyển đổi các tính chất publiccòn BinaryFormatter/SoapFormatter chuyển đổi cả public lẫn private.
Sockets
78
Socket là gì
79
Là nền tảng của lập trình mạng
Là một đối tượng thể hiện truy cập mức thấp vào IP Stack
Có thể ở chế độ mở, đóng hoặc 1 số chế độ trung gian khác
Có thể gởi, nhận dữ liệu
Dữ liệu tổng quát được gởi từng khối( gọi là packet ) khoảng vài KB/ lần để tăng hiệu suất
Nhắc lại địa chỉ và cổng
80
Nguyên lý hoạt động của địa chỉ và cổng
81
Trong máy có rất nhiều ứng dụng muốn trao đổi với ứng dụng khác thông qua mạng
Mỗi máy tính chỉ có 1 đường truyền dữ liệu
VD: có 2 ứng dụng trên máy A muốn trao đổi với 2 ứng dụng trên máy B, vấn đề? Làm sao gởi đến đúng ứng dụng cần gởi?
Mỗi ứng dụng sẽ được gán 1 số hiệu port nằm trong khoảng từ 1…65535
Nguyên lý hoạt động của địa chỉ và cổng
82
Trên máy A:
Khi 1 ứng dụng trên máy A muốn gởi đến 1 ứng dụng trên máy B chỉ cần điền vào số hiệu port(Remote Port) vào gói tin cần gởi
Trên máy B:
Các ứng dụng chỉ việc kiểm tra số port trên mỗi gói
tin đến có trùng với số hiệu Port của mình hay
không (Local Port).
Nếu đúng thì nó sẽ xử lý
SOCKET
File mô tả để tham chiếu đến kết nối mạng được gọi là các Socket.
Đặc trưng của Socket:
Một kết nối mạng hay một đường ống dẫn để truyền tải dữ liệu.
Một kiểu truyền thông như stream hay datagram.
Giao tiếp qua Socket
83
SOCKET Socket phải được gắn vào một địa chỉ mạng và một port trên hệ thống.
Socket đã được gắn địa chỉ mạng và port, được dùng để gởi và nhận dữ
liệu trong mạng.
Trong .Net Framework lớp Socket hỗ trợ cho việc lập trình Socket.
Namespace System.Net.Sockets chứa các lớp giao tiếp với Winsock API.
Phương thức tạo lập Socket trong .Net Framework như sau:
Socket (AddressFamily, SocketType, ProtocolType)
AddressFamily: định nghĩa kiểu mạng địa chỉ trong mạng.
SocketType: định nghĩa kiểu dữ liệu kết nối (kiểu Socket)
ProtocolType: định nghĩa kiểu giao thức kết nối.
Ví dụ: Socket sk = Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
84
Giao thức phổ biến và Số hiệu Port
85
Một số quy định
86
Không bao giờ có 2 ứng dụng cùng port
Well-know: 1-1023 thường dùng cho những ứng dụng quan trọng trên hệ điều hành
1024 – 49151: Thường dùng cho người lập trình( nên dùng theo qui định)
49152 – 65535 (Dynamic): Dùng dự trữ
Các thuộc tính của socket
87
Domain : vùng giao tiếp của socket .
• Nếu socket hoạt động trên môi trường internet thì nó sẽ có giá trị là AF_INET (tức là hoạt động dựa theo chuẩn internet và dùng IP để xác định địa chỉ vật lý).
• Nếu socket hoạt động trên môi trường mạng cục bộ thì ta có thể dùng giao tiếp theo chuẩn : AF_UNIX (dùng đường dẫn và hệ thống file để đặt tên và xác định kết nối giữa hai hay nhiều ứng dụng . Trong môi trường Linux thì các thiết bị cũng được coi như hệ thống file đặc biệt và socket cũng được coi như vậy )
Các thuộc tính của socket
88
Kiểu socket type
SOCK_STREAM : tạo ra kết nối bền vững (kết nối dạng TCP ) . Kết nối này đảm bảo gói tin này đến đích an toàn và không bị mất mát , nhưng nó có nhược điểm là chậm và tốn nhiều tài nguyên.
SOCK_DGRAM : cách gửi nhận dữ liệu một chiều (kết nối UDP) , client gửi dữ liệu đi mà không cần biết là server có nhân đựơc dữ liệu hay không . Server khi nhận được dữ liệu cũng không cần phải xác nhận với client là đã nhận dữ liệu . Tuy không an toàn nhưng nó nhanh và được sử dụng nhiều trong môi trường multimedia.
Các thuộc tính của socket
89
Giao thức của socket (protocol ):
Giao thức là cách quy ước gửi nhận dữ liệu trong quá trình trao đổi thông tin (VD ai gửi trước , ai nhận , gửi nhận theo thứ tự nào ....) Có nhiều giao thức đối với mỗi loại socket , nhưng dùng nhiều là TCP và UDP .
MỘT SỐ HÀM THÔNG DỤNG CỦA SOCKET
90
Hàm tạo socket :SOCKET socket( int af, int type, int protocol);
af : domain . Có thể lấy các giá trị sau : AF_INET , AF_UNIX , AF_IPX , AF_ISO , AF_NS ...
type : SOCK_STREAM , SOCK_DGRAM
protocol : lấy giá trị 0 để sử protocol mặc định cho mỗi loại socket
Giá trị trả về là socket discriptor , là một con số dùng để mô tả socket mình vừa tạo ra .
MỘT SỐ HÀM THÔNG DỤNG CỦA SOCKET
91
Hàm đóng socket :closesocket(SOCKET sk ) ;
Hàm bindint bind(SOCKET s, const struct sockaddr* name, int namelen);Trước khi gọi hàm bind bạn phải tạo một socket trước bằng hàm socket() . Sau khi tạo socket là tới lúc mình sẽ dùng hàm bind gán cho nó các thông tin như port và địa chỉ
Hàm accept
92
Khi ta gọi hàm accept nó sẽ đưa thread mà hàm accept đang hoạt động vào trạng thái blocking . Khi có một kết nối ở trong hàng đợi thì hàm accept sẽ kết thúc và thread sẽ thoát khỏi trạng thái blocking . Khi này hàm accept đã tạo ra một socket mới ( thông tin về socket này chúng ta không thể biết ) và đặt các địa chỉ của client vào struct sockaddr* addr . Socket mới này chính là kết quả trả về của hàm accept . Ta sẽ dùng socket mới này để liên lạc với client chứ không dùng socket cũ là s .
Các hàm send và recv
93
• int send( SOCKET s, const char* buf, int len, int flags);
• int recv( SOCKET s, char* buf, int len, int flags);
Đây là các hàm dùng để trao đổi thông tin giữa client và server (dạng kết nối TCP )int recvfrom( SOCKET s, char* buf, int len, int flags, struct sockaddr* from, int* fromlen);int sendto( SOCKET s, const char* buf, int len, int flags, const struct sockaddr* to, int tolen);Đây là các hàm dùng để trao đổi thông tin giữa client và server (dạng kết nối UDP )
Socket hướng kết nối (TCP Socket)
94
Có 1 đường kết nối ảo giữa 2 tiến trình
Một trong 2 tiến trình phải đợi tiến trình kia yêu cầu kết nối.
Có thể sử dụng để liên lạc theo mô hình Client/Server
Trong mô hình Client/Server thì Server lắng nghe và chấp nhận một yêu cầu kết nối
Mỗi thông điệp gửi đều có xác nhận trở về
Các gói tin chuyển đi tuần tự
Đặc điểm của Socket không hướng kết nối
95
Hai tiến trình liên lạc với nhau không kết nối trực tiếp
Thông điệp gửi đi phải kèm theo địa chỉ của người nhận
Thông điệp có thể gửi nhiều lần
Người gửi không chắc chắn thông điệp tới tay người nhận Thông điệp gửi sau có thể đến đích trước thông điệp gửi trước đó.
IPAddress
96
Trên Internet mỗi 1 trạm đều có 1 định danh duy nhất và được gọi là địa chỉ.
Địa chỉ trên Internet là một tập hợp gồm 4 con số có giá trị từ 0-255 và cách nhau bởi dấu chấm.
Để thể hiện địa chỉ này, người ta có thể viết dưới các dạng sau: Tên: May01, Server, …Địa chỉ IP nhưng đặt trong một chuỗi: "192.168.1.1",
"127.0.0.1“Đặt trong một mảng 4 byte, mỗi byte chứa một số từ 0-
255.Ví dụ: Đổi địa chỉ 192.168.1.2 ra số, ta tính như sau:
2 * 256 ^ 3 + 1 * 256 ^ 2 + 168 * 256 ^ 1 + 192 * 256 ^ 0 = 33663168
Lớp IPAddress: các thành viên
97
Lớp IPAddress: các thành viên
98
Lớp IPAddress: các thành viên
99
IPAddress: Ví dụ tạo địa chỉ
100
Cách 1: Dùng hàm khởi tạoByte[] b = new Byte[4]; b[0] = 192; b[1] = 168; b[2] = 10; b[3] = 10;IPAddress Ip1 = new IPAddress(b);
Cách 2: Dùng hàm khởi tạoIPAddress Ip2 = new IPAddress(16885952);
Cách 3: Dùng hàm khởi tạoIPAddress Ip3 = IPAddress.Parse("172.16.1.1")
Cách 4: Thông qua tính toánLong So = 192* 256^0+168* 256^1+1* 256^2 + 2*256^3;IPAddress Ip4 = new IPAddress(So);
IPAddress: Ví dụ kiểm tra địa chỉ
101
private void KiemTra()
{
IPAddress ip;
String Ip4 = "127.0.0.1";
String Ip5 = "999.0.0.1";
MessageBox.Show(IPAddress.TryParse(Ip4, out ip).ToString());
MessageBox.Show(IPAddress.TryParse(Ip5, out ip).ToString()); }
IPAddress: Ví dụ chuyển địa chỉ hiện hành ra mảng
102
void ChuyenDoi()
{
IPAddress Ip3 = new IPAddress(16885952);
Byte[] b= new Byte[4]; b = Ip3.GetAddressBytes();
MessageBox.Show("Address: " + b[0] + "." + b[1] + "." + b[2] + "." + b[3])
}
Lớp IPEndpoint
103
Trong mạng, để hai trạm có thể trao đổi thông tin được với nhau thì chúng cần phải biết được địa chỉ (IP) của nhau và số hiệu cổng mà hai bên dùng để trao đổi thông tin.
Lớp IPAddress mới chỉ cung cấp địa chỉ IP (IPAddress), như vậy vẫn còn thiếu số hiệu cổng (Port number).
Lớp IPEndpoint chính là lớp chứa đựng cả IPAddress và Port number.
Lớp IPEndpoint: các thành viên
104
Lớp IPEndpoint: các thành viên
105
Lớp IPEndpoint: ví dụ khởi tạo
106
private void TaoEndpoint()
{// Tạo một địa chỉ IPIPAddress IPAdd = IPAddress.Parse("127.0.0.1");// Truyền vào cho hàm khởi tạo để tạo IPEndpointIPEndPoint IPep = new IPEndPoint(IPAdd, 10000);
}
private void TaoEndPointBoiTenMay()
{IPAddress IPAdd;//tạo đối tượng IP từ tên của máy thông qua Phương thức tĩnh
Dns.GetHostAddresses của lớp DNSIPAdd = Dns.GetHostAddresses("Localhost")[0];IPEndPoint IPep = new IPEndPoint(IPAdd, 10000);}
Lớp IPEndpoint: ví dụ khởi tạo
107
Lưu ý : Vì một máy tính có thể có nhiều card mạng (Interface) do vậy có thể có nhiều hơn 1 địa chỉ IP.
Hàm GetHostAddresses sẽ trả về cho chúng ta một mảng chứa tất cả các địa chỉđó.
Chúng ta lấy chỉ số là 0 để chọn địa chỉ của card mạng đầu tiên.
Lớp IPHostEntry
108
IPHostEntry là lớp chứa (Container) về thông tin địa chỉ của các máy trạm trên Internet.
Lưu ý: Nó chỉ là nơi để "chứa", do vậy trước khi sử dụng cần phải “nạp" thông tin vào cho nó.
Lớp này rất hay được dùng với lớp DNS
Lớp IPHostEntry: các thành viên
109
Lớp DNS
110
DNS (Domain Name Service) là một lớp giúp chúng ta trong việc phân giải tên miền (Domain Resolution) đơn giản.
Phân giải tên miền tức là: Đầu vào là tên của máy trạm thì đầu ra sẽ cho ta địa chỉ IP tương ứng của máy đó, ví dụ: ServerCNTT 192.168.3.8
Ngoài ra lớp Dns còn có rất nhiều phương thức cho chúng ta thêm thông tin về máy cục bộ như tên, địa chỉ, v.v.
Lớp DNS: các thành viên
111
Lớp DNS: các thành viên
112
Lớp DNS: các thành viên
113
Lưu ý: Đây là các phương thức tĩnh, do vậy khi gọi thì gọi trực tiếp từ tên lớp mà không cần phải khai báo một đối tượng mới của lớp này.
Ví dụ: Dns.Resolve, Dns.GetHostname, Dns.GetHostEntry, v.v…
Lớp DNS: ví dụ 1
114
private void ShowIPs(){// Lấy tất cả địa chỉIP của máyIPAddress[] add = Dns.GetHostAddresses(“ABC-PC");
foreach (IPAddress ip in add) {MessageBox.Show(ip.ToString());}//for (int i = 0; i < add.Length; i++)//{// MessageBox.Show(add[i].ToString());//}}
Lớp DNS: ví dụ 1
115
private void CreatIPHostEntry() {
IPHostEntry iphe1, iphe2, iphe3;
IPAddress ipadd = IPAddress.Parse("127.0.0.1");
iphe1 = Dns.GetHostEntry("Notebook");
iphe2 = Dns.GetHostEntry("127.0.0.1");
iphe3 = Dns.GetHostEntry(ipadd);
MessageBox.Show(iphe1.HostName);
MessageBox.Show(iphe2.HostName) ;
MessageBox.Show(iphe3.HostName) ;
}
SOCKETusing System;
using System.Net;
using System.Net.Sockets;
class SockProp
{ public static void Main ()
{ IPAddress ia = IPAddress.Parse("127.0.0.1");
IPEndPoint ie = new IPEndPoint(ia, 8000);
Socket test = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("AddressFamily: {0}", test.AddressFamily);
Console.WriteLine("SocketType: {0}", test.SocketType);
Console.WriteLine("ProtocolType: {0}", test.ProtocolType);
Console.WriteLine("Blocking: {0}", test.Blocking);
test.Blocking = false;
Console.WriteLine("new Blocking: {0}",test.Blocking);
Console.WriteLine("Connected: {0}", test.Connected);
test.Bind(ie);
IPEndPoint iep = (IPEndPoint)test.LocalEndPoint;
Console.WriteLine("Local EndPoint: {0}", iep.ToString());
test.Close();
}
} 116
SOCKET
LẬP TRÌNH SOCKET HƯỚNG KẾT NỐI
Lập trình Socket hướng kết nối, giao thức TCP được dùng để thành lập
phiên làm việc giữa hai endpoint.
Khi sử dụng giao thức TCP để thành lập kết nối ta phải đàm phán kết nối
trước nhưng khi kết nối đã được thành lập dữ liệu có thể truyền đi giữa các
thiết bị một cách tin tưởng.
117
SOCKETLẬP TRÌNH PHÍA SERVER
Đầu tiên Server sẽ tạo một Socket, Socket này sẽ được gắn vào một địa
chỉ ip và một port cục bộ.
Hàm để thực hiện việc này là hàm Bind().
Hàm Bind() cần một danh đối số là một IPEndPoint cục bộ.
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
server.Bind(ipep);
IPAddress.Any: để chấp nhận kết nối trên bất kỳ card mạng nào
118
SOCKET
LẬP TRÌNH PHÍA SERVER
Sau khi Socket đã được gắn kết vào một địa chỉ và một port, Server phải
sẵn sàng chấp nhận kết nối từ Client.
Việc này được thực hiện nhờ vào hàm Listen().
Hàm Listen() có một đối số, đó chính là số Client tối đa mà nó lắng nghe.server.Listen(10);
Tiếp theo Server dùng hàm Accept() để chấp nhận kết nối từ Client:Socket client = server.Accept();
Hàm Accept() sẽ dừng Server lại và chờ cho đến khi nào có Client kết nối
đến và sẽ trả về một Socket khác, Socket này được dùng để trao đổi dữ
liệu với Client.
Khi đã chấp nhận kết nối với Client thì Server có thể gởi và nhận dữ liệu
với Client thông qua phương thức Send() và Receive().string welcome = "Hello Client";
buff = Encoding.ASCII.GetBytes(welcome);
client.Send(buff, buff.Length, SocketFlags.None);119
SOCKET
LẬP TRÌNH PHÍA SERVER
Phương thức Send() của Socket dùng để gởi dữ liệu, phương thức này có
một số đối số quan trọng sau:
Buff : mảng các byte cần gởi
Offset: vị trí đầu tiên trong mảng cần gởi
Size: số byte cần gởi
SocketFlags: chỉ ra cách gởi dữ liệu trên Socket
Phương thức Receive() dùng để nhận dữ liệu. Phương thức này có một số
đối số quan trọng sau:
Buff : mảng các byte cần nhận
Offset: vị trí đầu tiên trong mảng cần nhận
Size: số byte cần nhận
SocketFlags: chỉ ra cách nhận dữ liệu trên Socket
120
SOCKETusing System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class TcpServerDonGian
{
public static void Main()
{
//Số byte thực sự nhận được dùng hàm Receive()
int byteReceive;
//buffer để nhận và gởi dữ liệu
byte[] buff = new byte[1024];
//EndPoint cục bộ
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
//Server Socket
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
//Kết nối server với 1 EndPoint
server.Bind(ipep);
//Server lắng nghe tối đa 10 kết nối
server.Listen(10);
Console.WriteLine("Dang cho Client ket noi den...");121
SOCKET//Hàm Accept() sẽ block server lại cho đến khi có Client kết nối đến
Socket client = server.Accept();
//Client EndPoint
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("Da ket noi voi Client {0} tai port {1}",
clientep.Address, clientep.Port);
string welcome = "Hello Client";
//Chuyển chuỗi thành mảng các byte
buff = Encoding.ASCII.GetBytes(welcome);
//Gởi câu chào cho Client
client.Send(buff, buff.Length, SocketFlags.None);
while (true)
{ //Reset lại buffer
buff = new byte[1024];
//Lấy số byte thực sự nhận được
byteReceive = client.Receive(buff);
//Nếu Client ngắt kết nối thì thoát khỏi vòng lặp
if (byteReceive == 0) break;
Console.WriteLine(Encoding.ASCII.GetString(buff, 0,
byteReceive));
//Sau khi nhận dữ liệu xong, gởi lại cho Client
client.Send(buff, byteReceive, SocketFlags.None);
}122
SOCKETConsole.WriteLine("Da dong ket noi voi Client: {0}",
clientep.Address);
//Đóng kết nối
client.Close();
server.Close();
}
}
123
SOCKETLẬP TRÌNH PHÍA CLIENT
Client phải gắn kết một địa chỉ của một Socket đã được tạo ra nhưng sử
dụng phương thức Connect().
Phương thức Connect() yêu cầu một IPEndPoint của Server mà Client cần
kết nối đến.
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
server.Connect(ipep);
}catch (SocketException e)
{
Console.WriteLine("Không thể kết nối đến Server");
Console.WriteLine(e.ToString());
return;
}
Phương thức Connect() sẽ dừng lại cho đến khi Client kết nối được với Server124
SOCKETLẬP TRÌNH PHÍA CLIENT
Khi kết nối được thành lập, Client dùng phương thức Send() và Receive()
của lớp Socket để gởi và nhận dữ liệu.
Khi quá trình trao đổi dữ liệu đã hoàn tất, đối tượng Socket phải được đóng
lại.
Client Socket dùng phương thức Shutdown() để dừng Socket và dùng
phương thức Close() để thực sự đóng phiên làm việc.
Phương thức Shutdown() của Socket dùng một tham số để quyết định cách
Socket sẽ dừng lại. Các phương thức đó là:
Giá trị Mô tả
SocketShutdown.Both Ngăn cản gởi và nhận dữ liệu trên Socket.
SocketShutdown.Receive Ngăn cản nhận dữ liệu trên Socket. Cờ RST sẽ được gởi nếu có
thêm dữ liệu được nhận.
SocketShutdown.Send Ngăn cản gởi dữ liệu trên Socket. Cờ FIN sẽ được gởi sau khi tất
cả dữ liệu còn lại trong buffer đã được gởi đi.
125
SOCKET
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleTcpClient
{
public static void Main()
{
//Buffer để gởi và nhận dữ liệu
byte[] buff = new byte[1024];
//Chuỗi nhập vào và chuỗi nhận được
string input, stringData;
//IPEndPoint ở server
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"),
5000);
//Server Socket
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
126
SOCKET//Hàm Connect() sẽ bị block lại và chờ khi kết nối được với server
try
{
server.Connect(ipep);
}
//Quá trình kết nối có thể xảy ra lỗi nên phải dùng try, catch
catch (SocketException e)
{
Console.WriteLine("Không thể kết nối đến Server");
Console.WriteLine(e.ToString());
return;
}
//Số byte thực sự nhận được
int byteReceive = server.Receive(buff);
//Chuỗi nhận được
stringData = Encoding.ASCII.GetString(buff, 0, byteReceive);
Console.WriteLine(stringData);
127
SOCKETwhile (true)
{ //Nhập dữ liệu từ bàn phím
input = Console.ReadLine();
//Nếu nhập exit thì thoát và đóng Socket
if (input == "exit") break;
//Gởi dữ liệu cho server
server.Send(Encoding.ASCII.GetBytes(input));
//Reset lại buffer
buff = new byte[1024];
//Số byte thực sự nhận được
byteReceive = server.Receive(buff);
//Chuỗi nhận được
stringData = Encoding.ASCII.GetString(buff, 0,
byteReceive);
Console.WriteLine(stringData);
}
Console.WriteLine("Dong ket noi voi server...");
//Dừng kết nối, không cho phép nhận và gởi dữ liệu
server.Shutdown(SocketShutdown.Both);
//Đóng Socket
server.Close();
}
}128
UDP SOCKETMô hình lập trình Socket phi kết nối
Phương thức Connect() không cần dùng trong chương trình UDP Client.
Giao thức phi kết nối UDP không đảm bảo dữ liệu được truyền tới đích.
Socket phi kết nối cung cấp hai phương thức SendTo() và
ReceiveFrom() để thực hiện việc kết nối.
UDP SOCKET
Lập trình phía Server
UDP là một giao thức phi kết nối do đó chỉ làm hai việc để tạo ra một ứng
dụng Server gởi và nhận dữ liệu:
• Tạo ra Socket
• Kết nối Socket đến một IPEndPoint cục bộ
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
newsock.Bind(ipep);
1. Tạo ra
IPEndPoint2. Tạo ra
Socket
3. Kết nối
Socket với
IPEndPoint
Để thực hiện truyền thông phi kết nối, phải chỉ ra SocketType là Datagram
và ProtocolType là Udp.
UDP SOCKETLập trình phía Serverusing System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleUdpSrvr
{ public static void Main()
{ int recv;
byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
newsock.Bind(ipep);
Console.WriteLine("Dang cho Client ket noi den...");
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine("Thong diep duoc nhan tu {0}:",
Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
UDP SOCKET
Lập trình phía Serverstring welcome = "Hello Client";
data = Encoding.ASCII.GetBytes(welcome);
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
while (true)
{
data = new byte[1024];
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
newsock.SendTo(data, recv, SocketFlags.None, Remote);
}
}
}
Sau khi gắn Socket vào một IPEndPoint, Server sẽ chờ Client kết nối đến,
khi Client kết nối đến, Client sẽ gởi thông điệp đến Server. Server sau khi
nhận được thông điệp từ Client nó sẽ gởi câu chào ngược lại cho Client.
UDP SOCKET
Lập trình phía Clientusing System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleUdpClient
{ public static void Main()
{ byte[] data = new byte[1024];
string input, stringData;
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
string welcome = "Hello server";
data = Encoding.ASCII.GetBytes(welcome);
server.SendTo(data, data.Length, SocketFlags.None, ipep);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)sender;
data = new byte[1024];
int recv = server.ReceiveFrom(data, ref Remote);
Console.WriteLine("Thong diep duoc nhan tu {0}:", Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
UDP SOCKET
Lập trình phía Clientwhile (true)
{ input = Console.ReadLine();
if (input == "exit") break;
server.SendTo(Encoding.ASCII.GetBytes(input), Remote);
data = new byte[1024];
recv = server.ReceiveFrom(data, ref Remote);
stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(stringData);
}
Console.WriteLine("Dang dong client");
server.Close();
}
}
Client gởi thông điệp đến Server và chờ câu chào trả về từ Server.
Chương trình SimpleUdpClient đọc dữ liệu nhập vào từ bàn phím rồi gởi
đến và chờ dữ liệu từ Server gởi trả về. Khi Server gởi trả dữ liệu về, Client
sẽ lấy thông điệp đó ra và hiển thị lên màn hình.
Chương trình UDP Server sẽ không biết khi nào Client ngắt kết nối do đó
khi Client ngắt kết nối thì phải gởi thông điệp ngắt kết nối cho Server biết.
UDP SOCKET
Phân biệt các thông điệp UDP Mỗi phương thức ReceiveFrom() chỉ đọc dữ liệu được gởi từ một phương
thức SendTo().
Khi UDP Socket có thể nhận thông điệp từ bất kỳ Client nào.
Để UDP Socket phân biệt được Client gởi dữ liệu thì mỗi thông điệp phải
được chứa trong một gói tin riêng và được đánh dấu bởi thông tin IP của
thiết bị gởi.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class TestUdpSrvr
{
Chương trình UDP Server
UDP SOCKET
public static void Main()
{ int recv; byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
newsock.Bind(ipep);
Console.WriteLine("Dang cho client ket noi den...");
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tmpRemote = (EndPoint)(sender);
recv = newsock.ReceiveFrom(data, ref tmpRemote);
Console.WriteLine("Thong diep duoc nhan tu {0}:", tmpRemote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
string welcome = "Xin chao client";
data = Encoding.ASCII.GetBytes(welcome);
newsock.SendTo(data, data.Length, SocketFlags.None,tmpRemote);
for (int i = 0; i < 5; i++)
{ data = new byte[1024];
recv = newsock.ReceiveFrom(data, ref tmpRemote);
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
}
newsock.Close();
}
}
Chương trình UDP Server
UDP SOCKET
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class TestUdpClient
{ public static void Main()
{ byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
string welcome = "Xin chao Server";
data = Encoding.ASCII.GetBytes(welcome);
server.SendTo(data, data.Length, SocketFlags.None, ipep);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tmpRemote = (EndPoint)sender;
data = new byte[1024];
int recv = server.ReceiveFrom(data, ref tmpRemote);
Console.WriteLine("Thong diep duoc nhan tu {0}:",
tmpRemote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
Chương trình UDP Client
UDP SOCKET
server.SendTo(Encoding.ASCII.GetBytes("Thong diep 1"), tmpRemote);
server.SendTo(Encoding.ASCII.GetBytes("Thong diep 2"), tmpRemote);
server.SendTo(Encoding.ASCII.GetBytes("Thong diep 3"), tmpRemote);
server.SendTo(Encoding.ASCII.GetBytes("Thong diep 4"), tmpRemote);
server.SendTo(Encoding.ASCII.GetBytes("Thong diep 5"), tmpRemote);
Console.WriteLine("Dang dong client");
server.Close();
}
}
Chương trình UDP Client
UDP Server nhận biết
được các thông điệp
riêng rẽ
Kết quả ở Server
Lớp UDPClient
139
Giao thức UDP (User Datagram Protocol hay User Define Protocol) là một giao thức phi kết nối (Connectionless)
Nói cách khác là không cần thiết lập kết nối giữa hai bên khi tiến hành trao đổi thông tin.
Giao thức này không tin cậy bằng giao thức TCP nhưng tốc độ lại nhanh và dễ cài đặt. Ngoài ra, với giao thức UDP ta còn có thể gửi các gói tin quảng bá (Broadcast) cho đồng thời nhiều máy.
Lớp UDPClient – Trình tự kết nối
140
Lớp UDPClient: các thành viên
141
Lớp UDPClient: các thành viên
142
Lớp UDPClient: các thành viên
143
Lớp UDPClient
144
Lớp UDPClient
145
Tổng kết: Lớp UDPClient
146
Khi muốn gửi dữ liệu qua mạng bằng lớp UDPClient, làm đơn giản như sau:
Tạo một UDPClient và gán cho nó một số hiệu cổng. Ví dụ: UDPClient udp = new UDPClient(1000)
Tạo một địa chỉ IP ứng với địa chỉ của máy mà ta muốn giao tiếp bằng IPEndPoint hoặc IPAddress hoặc IPHostEntry. (Lưu ý: Nếu dùng Dns.GetHostEntry thì ta có thể truyền vào là tên của máy. Sau đó muốn lấy địa chỉ thì: Dns.GetHostEntry("Tên_Máy").Address[0])
Tổng kết: Lớp UDPClient
147
Gửi dữ liệu đi: Bước 1: Chuyển chuỗi thành mảng byte
Bước 2: Gọi phương thức Send, trong đó truyền địa chỉ IP của máy ở xa mà ta vừa tạo ở 2 và thêm vào số hiệu cổng mà máy ở xa đang dùng để nhận dữ liệu.
Khi nhận: Dùng phương thức Receive để nhận dữ liệu về. Khi đó chúng ta cần tạo một đối tượng IPEndPoint với địa chỉ và số hiệu cổng của máy ở xa mà chúng ta muốn nhận dữ liệu. Phương thức này trả về dữ liệu ở dạng mảng byte, do vậy để chuyển sang dạng chuỗi ký tự thì cần dùng lớp Encoding để chuyển đổi.
Lớp TCPClient
148
Để đảm bảo độ tin cậy trong các ứng dụng mạng, người ta còn dùng một giao thức khác, gọi là giao thức có kết nối: TCP (Transport Control Protocol).
Trên Internet chủ yếu là dùng loại giao thức này, ví dụ như Telnet, HTTP, SMTP, POP3… Để lập trình theo giao thức TCP, .NET cung cấp hai lớp có tên là TCPClient và TCPListener.
Lớp TCPClient
149
Lớp TCPClient
150
Lớp TCPClient
151
Lớp TCPClient: trình tự kết nối
152
Bước 1: Tạo một đối tượng TCPClient
Bước 2: Kết nối đến máy chủ (Server) dùng phương thức Connect
Bước 3: Tạo 2 đối tượng StreamReader (Receive) và StreamWriter (Send) và “nối” với GetStream của TCPClient
Bước 4: Dùng đối tượng StreamWriter.Writeline/Write vừa tạo ở
trên để gửi dữ liệu đi.
Dùng đối tượng StreamReader.Readline/Read vừa tạo ở trên để đọc dữ liệu về.
Bước 5: Đóng kết nối.
Lớp TCPClient: trình tự kết nối
153
Nếu muốn gửi/nhận dữ liệu ở mức byte (nhị phân) thì dựng NetworkStream(truyền GetStream cho NetworkStream)
TcpClient
154
Ví dụ sử dụng TcpClient
Chương trình chat dùng TCPClient
155
class Connection
{TcpClient tcpClient; private Thread thrSender;private StreamReader srReceiver;private StreamWriter swSender;private string currUser; private string strResponse;public Connection(TcpClient tcpCon) {
tcpClient = tcpCon;thrSender = new Thread(AcceptClient); thrSender.Start();
}
Chương trình chat dùng TCPClient
156
private void CloseConnection()
{
tcpClient.Close();
srReceiver.Close();
swSender.Close();
}
private void AcceptClient()
{
srReceiver = new System.IO.StreamReader(tcpClient.GetStream());
swSender = new System.IO.StreamWriter(tcpClient.GetStream());
currUser = srReceiver.ReadLine();
if (currUser != "")
{
if (ChatServer.htUsers.Contains(currUser) == true)
{
swSender.WriteLine("0|This username already exists.");
swSender.Flush();
CloseConnection();
return;
}
Chương trình chat dùng TCPClient
157
else if (currUser == "Administrator")
{
swSender.WriteLine("0|This username is reserved.");
swSender.Flush();
CloseConnection();
return;
}
else {
swSender.WriteLine("1");
swSender.Flush();
ChatServer.AddUser(tcpClient, currUser);
}
Chương trình chat dùng TCPClient
158
}
else {
CloseConnection();
return;
}
try {
while ((strResponse = srReceiver.ReadLine()) != "")
{
if (strResponse == null) {
ChatServer.RemoveUser(tcpClient);
}
Chương trình chat dùng TCPClient
159
else {
ChatServer.SendMessage(currUser,
strResponse);
}
}
}
catch {
ChatServer.RemoveUser(tcpClient);
}
Lớp TCPClient: ví dụ
160
Tạo một TCP Client và kết nối đến server (FTP Server), sau đó gửi 1 chuỗi
using System.Net.Sockets;
using System.Net;
using System.IO;
public class Form1 {// Tạo địa chỉ ứng với 127.0.0.1Long DiaChi = 1 * 256 ^ 3 + 127 * 256 ^ 0// Tạo một IPEndPoint từ địa chỉ IP và cổng(TCPClient cần một IPEndPoint)IPEndPoint LocalEP = new IPEndPoint(DiaChi, 1000); '// cho cục bộ (client)// Tạo một đối tượng TCP ứng với địa chỉ và cổng ở trênTcpClient tcp = new TcpClient(LocalEP);// Hai luồng nhập và xuất dùng để đọc/ghi vào kết nối TCPStreamWriter Ghi;StreamReader Doc;
Lớp TCPClient: ví dụ
161
private void Form1_Load(…) {
tcp.Connect("localhost", 21);MessageBox.Show(tcp.Connected)Doc = new StreamReader(tcp.GetStream());Ghi = new StreamWriter(tcp.GetStream()); //Gửi thử một
chuỗi cho server (FTP Server)Ghi.Writeline("User nhiemtb");Ghi.Flush();// Đọc dữ liệu do Server gửi vềString S = Doc.ReadLine();MessageBox.Show("Dữ liệu gửi từ server : " + S);
}
Lớp TCPClient: ví dụ
162
private void Gui_Du_Lieu(String Data)
{Ghi.WriteLine(Data);
Ghi.Flush();
}
}
Nhận xét
163
Ở ví dụ trên ta thấy rằng việc gửi thì có thể thực hiện nhiều lần với việc gọi nhiều lần phương thức Gửi_Dữ_Liệu. Tuy nhiên, đối với việc nhận dữ liệu thì ta chỉ thực hiện một lần. Trong trường hợp nếu ta muốn nhận dữ liệu bất cứ khi nào có dữ liệu về thì cần áp dụng kỹ thuật "Thăm dò" và “Kích hoạt sự kiện" như trong phần UDPClient.
Nhận xét
164
Ý tưởng thực hiện như sau:
Bước 1: Tạo một TCPClient
Bước 2: Kết nối
Bước 3: Tạo một luồng mới, luồng này "chuyên theo dõi" xem có dữ liệu mới về hay không (chỉ việc kiểm tra bộ đệm (đối tượng StreamReader.EndOfStream = True/False). Nếu bộđệm không rỗng (có dữ liệu mới) thì giá trị EndOfStream sẽ bằng False. Khi có dữ liệu trong bộđệm ta kích hoạt (Raise) sự kiện Có_Dữ_Liệu lên. Trong sự kiện này ta sẽ viết các lệnh xử lý.
TCPListener
165
TCPListerner là một lớp cho phép người lập trìnhcó thể xây dựng các ứng dụng Server (Ví dụ nhưSMTP Server, FTP Server, DNS Server, POP3 Server hay server tự định nghĩa ….).
Phương thức khởi tạo
TcpListener (Port:Int32) Tạo một TcpListener và lắng nghe tại cổng chỉ định.
TcpListener(IPEndPoint) Tạo một TcpListener với giá trị Endpoint truyền vào.
TcpListener
(IPAddress, Int32)Tạo một TcpListener và lắng nghe các kết nối đến
tại địa chỉ IP và cổng chỉ định.
TCPListener
166
Tên Mô tả
AcceptSocket Chấp nhận một yêu cầu kết nối đang chờ.
AcceptTcpClientChấp nhận một yêu cầu kết nối đang chờ. (Ứng dụng sẽ
dừng tại lệnh này cho đến khi nào có một kết nối đến)
Pending Cho biết liệu có kết nối nào đang chờ đợi không ? (True =
có).
Start Bắt đầu lắng nghe các yêu cầu kết nối.
Stop Dừng việc nghe.
Các phương thức khác
TcpListener
167
Ví dụ sử dụng TcpListener
168
TcpListenerVí dụ sử dụng TcpListener
So sánh TCP & UDP
169