2. Nguồn Tham khảo
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
2
3. Mục tiêu
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
3
5. Mô hình OSI
Đượ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
5
6. 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-toend .
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..
7. Mô hình TCP/IP(Internet control
message protocol)
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”
7
9. Giao thức TCP
Hướng kết nối: connection oriented
Hai 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
9
10. Giao thức TCP
Đặ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.
10
11. Cổng giao thức
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.
11
12. Giao thức UDP
Giao thức UDP: User Datagram Protocol
Cũ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 UDP
Khô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).
12
15. Địa chỉ Ip và các lớp dành riêng
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 .
15
20. Sơ lược về lập trình mạng
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ạng
C/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óng
Giáo trình này sẽ chỉ đề cập đến hai ngôn ngữ C/C++và
C#.
20
21. Thư viện lập trình mạng
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 Socket
Nằ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
21
22. Công cụ lập trình
Visual Studio (6.0, 2003 .NET, 2005, 2008, 2010,
2013)
Rất mạnh
Hỗ trợ cả WinSock, MFC Socket và .NET Socket (Phiên
bản 2003.NET trở lên).
Dev C++
Miễn phí
Chỉ hỗ trợ WinSock
22
23. Công cụ hỗ trợ
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
23
24. .NET FrameWork
Không phải là NNLT
Cung cấp framework cho 4 ngôn ngữ lập trình
là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ường
thực thi cho các ứng dụng.
Các ngôn ngữ trong .Net có khả năng làm việc
hợp tác với nhau rất tốt.Vd: Class đã biên dịch
trong c# dùng được trong VB.Net
24
25. .NET FrameWork
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
25
26. Microsoft Intermediate
Language(MSIL)
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)
26
27. Common Language Runtime
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).
27
29. Biến và qui tắc đặt tên biến
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
29
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.
31. Hướng đối tượng trong C#
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)
31
32. Namespace
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
{
}
}
32
namespace NameSpace
{
// khai báo các lớp
public classA
{
}
namespace NameSpace
{
//……
}
}
33. Lớp và đối tượng
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ụ:
33
class Sinhvien
{
private string
mMSSV;
private string
mHoten;
public void In(){
….
}
}
34. Phương thức
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àm
Ví dụ:
class Sinhvien
{
private string
mMSSV;
private string
mHoten;
public void
In(){
….
}
}
34
class QLSV
{
public void IntenSinhvien(
)
{
// thực hiện 1 công việc
gì đó
Sinhvien sv = new
Sinhvien();
sv.In();
}
}
35. Constructors(Hàm khởi tạo)
Đượ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;
}
}
35
36. Destructors( Hàm hủy)
Hàm hủy là một hàm thành viên được gọi tự động khi đối tượng
bị 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 đến
hà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ố.
36
37. Destructors
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
}
}
37
38. Phương thức Overloading
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){
//….
}
}
38
40. Inheritance(kế thừa)
Các lớp với các đặc điểm tương tự nhau có thể
40
đượ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ương
thức (kế thừa) của 1 lớp khác. Sau đó xây dựng
nhữ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)
42. Inheritance(kế thừa)
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
}
42
43. Inheritance(kế thừa)
// 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);
}
}
43
44. Polymorphism(Tính đa hình )
Đa hình thái, nhiều cách phản ứng khác nhau cho
44
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 vi
M()
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.
46. Delegate và Event
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)
46
48. Streams
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)
48
49. Streams với files
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
49
50. Streams với files
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);
}
50
51. Streams với files
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();
}
51
52. Streams với files
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();
}
52
54. Mã hóa dữ liệu
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ự.
54
55. Binary và text streams
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
55
56. Binary và text streams
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);
}
56
59. Serialize và Deserialize
BinaryFormatter sử dụng 2 phương thức
Serialize và Deserialize để viết và đọc đối tượng
từ trong luồng:
59
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.
60. Serialization
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
60
61. Attribute [Serializable] và
[NonSerialized]
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].
61
62. Serialization
Ví dụ điển hình là hệ thống đặt hàng, vốn có yêu
cầ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ên
mạ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ùng
Serialization
62
64. Serialization
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;
64
66. Serialization với SoapFormatter
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)
66
67. Serialization dùng SoapFormatter
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();
67
68. Deserialization dùng SoapFormatter
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());
68
70. Serialization dùng BinaryFormatter
Đị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ụ SoapFormatter
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.Create("..po_bin.txt");
bf.Serialize(fs, po);
fs.Close();
MessageBox.Show("Serialize succesful!", "Info");
70
71. Deserialization dùng BinaryFormatter
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);
71
73. Serialization dùng XmlSerializer
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();
73
74. Deserialization dùng XmlSerializer
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());
74
77. XmlSerializer, BinaryFormatter ,Soap
Formatter?
XmlSerializer dùng cho dịch vụ Web.
BinaryFormatter/SoapFormatter dùng cho
Remoting.
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 public
còn BinaryFormatter/SoapFormatter chuyển đổi
cả public lẫn private.
77
79. Socket là gì
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
79
81. Nguyên lý hoạt động của địa chỉ và
cổng
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
81
82. Nguyên lý hoạt động của địa chỉ và
cổng
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ý
82
83. SOCKET
Giao tiếp qua 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.
83
84. 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
86. Một số quy định
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ữ
86
87. Các thuộc tính của socket
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 )
87
88. Các thuộc tính của socket
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.
88
89. Các thuộc tính của socket
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 .
89
90. MỘT SỐ HÀM THÔNG DỤNG CỦA
SOCKET
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 .
90
91. MỘT SỐ HÀM THÔNG DỤNG CỦA
SOCKET
Hàm đóng socket :
closesocket(SOCKET sk ) ;
Hàm bind
int 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ỉ
91
92. Hàm accept
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 .
92
93. Các hàm send và recv
• 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 )
93
94. Socket hướng kết nối (TCP Socket)
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
94
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ự
95. Đặc điểm của Socket không hướng
kết nối
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 đó.
95
96. IPAddress
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ừ 0255.
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
96
100. IPAddress: Ví dụ tạo địa chỉ
Cách 1:
Dùng hàm khởi tạo
Cách 2:
Dùng hàm khởi tạo
Byte[] b = new Byte[4]; b[0] = 192; b[1] = 168;
b[2] = 10; b[3] = 10;
IPAddress Ip1 = new IPAddress(b);
IPAddress Ip2 = new IPAddress(16885952);
Cách 3: Dùng hàm khởi tạo
IPAddress Ip3 = IPAddress.Parse("172.16.1.1")
Cách 4: Thông qua tính toán
Long So = 192* 256^0+168* 256^1+1* 256^2 +
2*256^3;
IPAddress Ip4 = new IPAddress(So);
100
101. IPAddress: Ví dụ kiểm tra địa chỉ
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()); }
101
102. IPAddress: Ví dụ chuyển địa chỉ hiện
hành ra mảng
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])
}
102
103. Lớp IPEndpoint
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.
103
106. Lớp IPEndpoint: ví dụ khởi tạo
private void TaoEndpoint()
{
// Tạo một địa chỉ IP
IPAddress IPAdd = IPAddress.Parse("127.0.0.1");
// Truyền vào cho hàm khởi tạo để tạo IPEndpoint
IPEndPoint 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 DNS
IPAdd = Dns.GetHostAddresses("Localhost")[0];
IPEndPoint IPep = new IPEndPoint(IPAdd, 10000);
}
106
107. Lớp IPEndpoint: ví dụ khởi tạo
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.
107
108. Lớp IPHostEntry
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
108
110. Lớp DNS
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.
110
113. Lớp DNS: các thành viên
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…
113
114. Lớp DNS: ví dụ 1
private void ShowIPs()
{
// Lấy tất cả địa chỉIP của máy
IPAddress[] 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());
//}
}
114
116. SOCKET
using 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
117. 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
118. SOCKET
LẬ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
119. 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
120. 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
121. SOCKET
using 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
122. 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
124. SOCKET
LẬ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 Server
124
125. SOCKET
LẬ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
126. 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
127. 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
128. SOCKET
while (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
129. UDP SOCKET
Mô 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.
130. 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
IPEndPoint
2. 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.
131. UDP SOCKET
Lập trình phía Server
using 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));
132. UDP SOCKET
Lập trình phía Server
string 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.
133. UDP SOCKET
Lập trình phía Client
using 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));
134. UDP SOCKET
Lập trình phía Client
while (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.
135. 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.
Chương trình UDP Server
using
using
using
using
class
{
System;
System.Net;
System.Net.Sockets;
System.Text;
TestUdpSrvr
136. UDP SOCKET
Chương trình UDP Server
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();
}
}
137. UDP SOCKET
Chương trình UDP Client
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));
138. UDP SOCKET
Chương trình UDP Client
server.SendTo(Encoding.ASCII.GetBytes("Thong
server.SendTo(Encoding.ASCII.GetBytes("Thong
server.SendTo(Encoding.ASCII.GetBytes("Thong
server.SendTo(Encoding.ASCII.GetBytes("Thong
server.SendTo(Encoding.ASCII.GetBytes("Thong
Console.WriteLine("Dang dong client");
server.Close();
}
}
UDP Server nhận biết
được các thông điệp
riêng rẽ
diep
diep
diep
diep
diep
Kết quả ở Server
1"),
2"),
3"),
4"),
5"),
tmpRemote);
tmpRemote);
tmpRemote);
tmpRemote);
tmpRemote);
139. Lớp UDPClient
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.
139
146. Tổng kết: Lớp UDPClient
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])
146
147. Tổng kết: Lớp UDPClient
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.
147
148. Lớp TCPClient
Để đả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.
148
152. Lớp TCPClient: trình tự kết nối
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.
152
153. Lớp TCPClient: trình tự kết nối
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)
153
155. Chương trình chat dùng TCPClient
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();
}
155
156. Chương trình chat dùng TCPClient
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;
}
156
157. Chương trình chat dùng TCPClient
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);
}
157
158. Chương trình chat dùng TCPClient
}
else {
CloseConnection();
return;
}
try {
while ((strResponse = srReceiver.ReadLine()) != "")
{
if (strResponse == null) {
ChatServer.RemoveUser(tcpClient);
}
158
159. Chương trình chat dùng TCPClient
else {
ChatServer.SendMessage(currUser,
strResponse);
}
}
}
catch {
ChatServer.RemoveUser(tcpClient);
}
159
160. Lớp TCPClient: ví dụ
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.1
Long 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ên
TcpClient tcp = new TcpClient(LocalEP);
// Hai luồng nhập và xuất dùng để đọc/ghi vào kết nối TCP
StreamWriter Ghi;
StreamReader Doc;
160
161. Lớp TCPClient: ví dụ
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);
}
161
163. Nhận xét
Ở 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.
163
164. Nhận xét
Ý 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ý.
164
165. TCPListener
TCPListerner là một lớp cho phép người lập trình
có 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.
165
166. TCPListener
Các phương thức khác
Tên
Mô tả
AcceptSocket
Chấp nhận một yêu cầu kết nối đang chờ.
AcceptTcpClient
Chấ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
Start
Bắt đầu lắng nghe các yêu cầu kết nối.
Stop
166
Cho biết liệu có kết nối nào đang chờ đợi không ? (True =
có).
Dừng việc nghe.
ISO: International Organization for Standartdization
Thông tin port được chưa trong header tcp, udp của packet
Thông tin port được chưa trong header tcp, udp của packet
Domain name system
Microsoft foundation Class: – Là thành phần quan trọng của ứng dụng, đóng vai trò trung gian trong trao đổi thông tin giữa ứng dụng với người dùng bởi giao diện đồ họa dễ nhìn. – Là công cụ xử lý message hiệu quả và không thể thiếu trong cơ chế điều phối message của ứng dụng windows.
Do biêndịchrangônngữtrunggiannênmột project pháttriểntrên .NET Framework cóthểviếtbằngnhiềungônngữmà .NET Framework hỗtrợ.
CRL là một máy ảo của .NET nó được dùng để chuyển đổi mã MSIL sang mã máy. Sau đó các đoạn mã được thực thi bởi CPU.
c 4096 byte/lần làm cho việc đọc hiệu quả
c 4096 byte/lần làm cho việc đọc hiệu quả
c 4096 byte/lầnlàmchoviệcđọchiệuquảfs.Seek(0, SeekOrigin.Begin);đặt con trỏ ở vịtríđầu file
Lớp IPAddress cung cấp 4 thuộc tính mô tả các địa chỉ IP đặc biệt:Any: dùng để mô tả một địa chỉ IP bất kỳ của hệ thống.Broadcast: dùng để mô tả địa chỉ IP Broadcast cho mạng cục bộ.Loopback: dùng để mô tả địa chỉ loopback của hệ thống.None: không dùng địa chỉ IP