Do vậy, khi nhận được dữ liệu thì Client hoặc Server thường phải biết được dữ liệu trước khi chuyển sang mảng Byte mà bên kia gửi đi, do vậy thường trong mảng Byt[r]
Trang 1MAI HÀ AN, TRẦN XUÂN HÒA
Bài giảng
LẬP TRÌNH MẠNG
TRƯỜNG ĐẠI HỌC LÂM NGHIỆP - 2015
Trang 3LỜI NÓI ĐẦU
Trong kỹ thuật phát triển phần mềm máy tính, kỹ thuật lập trình giao tiếp giữa các phần mềm máy tính chạy trên các máy tính khác nhau, thậm chí chạy trên các môi trường hệ điều hành khác nhau là một vấn đề hết sức cơ bản mà bất
cứ ai làm về phát triển phần mềm đều cần phải nắm rõ nhằm nâng cao khả năng hoạt động của phần mềm Để làm được việc đó, các kỹ sư phần mềm cần phải trang bị cho mình các kiến thức về lập trình giao tiếp giữa các máy tính với nhau thông qua mạng truyền dẫn
Lập trình mạng là môn học nhằm cung cấp cho người học những kiến thức căn bản về lập trình giao tiếp giữa các phần mềm chạy trên các máy tính khác nhau được kết nối với nhau thông qua mạng truyền dẫn
Cuốn bài giảng này được xây dựng nhằm đưa ra những kiến thức căn bản nhất về lập trình mạng, trong bài giảng có đưa ra các ví dụ minh họa được sử dụng bằng ngôn ngữ lập trình C#.NET trên môi trường lập trình là Visual Studio
Để nắm bắt và thực hành tốt các bài tập ví dụ cũng như các bài tập ôn tập, người học cần trang bị cho mình một máy tính có cài đặt chương trình Visual Studio C#
Chúc các bạn thành công!
Tác giả
Trang 5Chương 1 KHÁI QUÁT VỀ MẠNG MÁY TÍNH
Nội dung chính: Chương 1 giới thiệu về các khái niệm căn bản trong mạng máy tính có liên quan đến các nội dung ở các chương sau nhằm làm rõ hơn các khái niệm được đề cập đến trong các kỹ thuật lập trình giao tiếp giữa các máy tính thông qua các giao thức Chương 1 trình bày các nội dung căn bản như sau:
- Mô hình tham chiếu OSI;
- Các giao thức căn bản trong mạng máy tính;
- Địa chỉ IP;
- Địa chỉ miền
1.1 Mô hình tham chiếu 7 tầng OSI
Mô hình kết nối hệ thống mở được Tổ chức quốc tế về tiêu chuẩn hoá ISO (International Organizaiton for Standardization) đưa ra nhằm cung cấp một 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 áp dụng theo để phát triển các sản phẩm viễn thông Ý tưởng mô hình hoá được tạo ra còn nhằm hỗ trợ cho việc kết nối giữa các hệ thống và modun hoá các thành phần phục vụ mạng viến thông
Chức năng của mô hình OSI:
Cung cấp kiến thức về hoạt động của kết nối liên mạng
Đưa ra trình tự công việc để thiết lập và thực hiện kết nối giữa các thiết bị trên mạng
Mô hình OSI còn có một số thuận lợi sau:
- Chia nhỏ các hoạt động phức tạp của mạng thành các phần công việc
đơn giản
- Cho phép các nhà thiết kế có khả năng phát triển trên từng modun
chức năng
- Cung cấp các khả năng định nghĩa các chuẩn giao tiếp có tính tương
thích cao “plug and play” và tích hợp nhiều nhà cung cấp sản phẩm
Trang 6 Cấu trúc mô hình OSI:
Mô hình OSI gồm 7 tầng (level), mỗi lớp thực hiện các chức năng riêng cho hoạt động kết nối mạng
Hình 1.1 Mô hình 7 tầng OSI Tầng ứng dụng (Application layer - lớp 7): Tầng ứng dụng quy định giao diện giữa người sử dụng và môi trường OSI, nó cung cấp các phương tiện cho người sử dụng truy cập vả sử dụng các dịch vụ của mô hình OSI Các ứng dụng cũng được cấp như các chương trình xử lý kí tự, bảng biểu, thư tín … và lớp 7 đưa ra các giao thức HTTP, FTP, SMTP, POP3, Telnet
Tầng trình bày (Presentation layer - lớp 6): Chuyển đổi các thông tin
từ cú pháp người sử dụng sang cú pháp để truyền dữ liệu, ngoài ra nó có thể nén
dữ liệu truyền và mã hóa chúng trước khi truyền đễ bảo mật Tầng này sẽ định dạng dữ liệu từ lớp 7 đưa xuống rồi gửi đi đảm bảo sao cho bên thu có thể đọc được dữ liệu của bên phát Các chuẩn định dạng dữ liệu của lớp 6 là GIF, JPEG, PICT, MP3, MPEG …
Tầng giao dịch (Session layer – lớp 5): Thực hiện thiết lập, duy trì và kết thúc các phiên làm việc giữa hai hệ thống Tầng giao dịch quy định một giao diện ứng dụng cho tầng vận chuyển sử dụng Nó xác lập ánh xạ giữa các tên đặt địa chỉ, tạo ra các tiếp xúc ban đầu giữa các máy tính khác nhau trên cơ sở các giao dịch truyền thông Nó đặt tên nhất quán cho mọi thành phần muốn đối thoại riêng với nhau Các giao thức trong lớp 5 sử dụng là NFS, X- Window System, ASP
Trang 7Tầng vận chuyển (Transport layer - lớp 4): Tầng vận chuyển xác định địa chỉ trên mạng, cách thức chuyển giao gói tin trên cơ sở trực tiếp giữa hai đầu mút, đảm bảo truyền dữ liệu tin cậy giữa hai đầu cuối (end-to-end) Các giao thức phổ biến tại đây là TCP, UDP, SPX
Tầng mạng (Network layer - lớp 3): Tầng mạng có nhiệm vụ xác định việc chuyển hướng, vạch đường các gói tin trong mạng (chức năng định tuyến), các gói tin này có thể phải đi qua nhiều chặng trước khi đến được đích cuối cùng Lớp 3 là lớp có liên quan đến các địa chỉ logic trong mạng các giao thức hay sử dụng ở đây là IP, RIP, IPX, OSPF, AppleTalk
Tầng liên kết dữ liệu (Data link layer - lớp 2): Tầng liên kết dữ liệu có nhiệm vụ xác định cơ chế truy nhập thông tin trên mạng, các dạng thức chung trong các gói tin, đóng gói và phân phát các gói tin Lớp 2 có liên quan đến địa chỉ vật lý của các thiết bị mạng, topo mạng, truy nhập mạng, các cơ chế sửa lỗi
và điều khiển luồng
Tầng vật lý (Phisical layer - lớp 1): Tầng vật lý cung cấp phương thức truy cập vào đường truyền vật lý để truyền các dòng Bit không cấu trúc, ngoài ra
nó cung cấp các chuẩn về điện, dây cáp, đầu nối, kỹ thuật nối mạch điện, điện
áp, tốc độ cáp truyền dẫn, giao diện nối kết và các mức nối kết
1.2 Họ giao thức TCP/IP
Hình 1.2 Mô hình TCP/IP Lớp ứng dụng: Các nhà thiết kế TCP/IP cảm thấy rằng các giao thức mức cao nên bao gồm các chi tiết của lớp trình bày và lớp phiên Để đơn giản,
họ tạo ra một lớp ứng dụng kiểm soát các giao thức mức cao, các vấn đề của lớp trình bày, mã hóa và điều khiển hội thoại TCP/IP tập hợp tất cả các vấn đề liên quan đến ứng dụng vào trong một lớp và đảm bảo dữ liệu được đóng gói một cách thích hợp cho lớp kế tiếp
Trang 8Lớp vận chuyển: Lớp vận chuyển đề cập đến các vấn đề chất lượng dịch
vụ như độ tin cậy, điều khiển luồng và sửa lỗi
Lớp liên mạng: Mục tiêu của lớp này là truyền các gói từ nguồn đến được đích Giao thức đặc trưng khống chế lớp này được gọi là IP Công việc xác định đường dẫn tốt nhất và hoạt động chuyển mạch gói diễn ra tại lớp này
Lớp truy xuất mạng: Nó cũng được gọi là lớp Host-to-Network Nó là lớp liên quan đến tất cả các vấn đề mà một gói IP yêu cầu để tạo một liên kết vật
lý thực sự, và sau đó tạo một liên kết vật lý khác Nó bao gồm các chi tiết kỹ thuật LAN và WAN, và tất cả các chi tiết trong lớp liên kết dữ liệu cũng như lớp vật lý của mô hình OSI
Một số điểm khác nhau của TCP/IP và mô hình OSI
Mô hình TCP/IP kết hợp lớp Presentation Session vào trong lớp Application
Mô hình TCP/IP kết hợp lớp DataLink và lớp Physical vào trong một lớp
Mô hình TCP/IP đơn giản hơn bởi vì có ít lớp hơn
TCP/IP được chuẩn hóa và được sử dụng phổ biến trên toàn thế giới
Hình 1.3 So sánh giữa mô hình TCP/IP và mô hình OSI
1.3 Giao thức TCP và UDP
Transmission Control Protocol (TCP)
Đây là giao thức trung gian hoạt động giữa chương trình ứng dụng và IP TCP và IP là hai giao thức đầu tiên được định nghĩa và là thành phần chính để tạo nên bộ giao thức TCP/IP Nhiệm vụ của TCP là phát hiện các lỗi khi truyền
Trang 9tải dữ liệu của IP và yêu cầu gửi, sắp xếp lại, và giúp giảm sự quá tải trong mạng Khi nhận được dữ liệu, TCP sẽ thực hiện lắp ráp lại các packet và chuyển tới chương trình ứng dụng Do đó, TCP được gọi là giao thức “Đáng tin cậy” (reliable) và hướng kết nối (connection-oriented) Điều này cũng khiến cho TCP trở nên chậm và không phù hợp trong các ứng dụng đòi hỏi sự truyền tải tức thời như VoIP
User Datagram Protocol (UDP)
UDP là một thành phần của TCP/IP và cũng là giao thức trung gian nằm bên trên IP Giao thức này được dùng để truyền tải dữ liệu trên mạng thông qua các datagram Không giống như TCP, dữ liệu được truyền bởi UDP không được đảm bảo đến được đích và theo thứ tự Thay vào đó, UDP có tốc độ truyền tải nhanh hơn TCP và được sử dụng trong các ứng dụng để truyền tải media, VoIP, game online Do không cần phải duy trì kết nối như TCP nên UDP được gọi là giao thức không hướng kết nối (connectionless hay stateless)
So sánh giữa TCP và UDP
- Không cho phép mất gói tin
- Đảm bảo việc truyền dữ liệu
- Tốc độ truyền thấp hơn UDP
- Cho phép mất dữ liệu
- Không đảm bảo
- Tốc độ truyền cao, VolP truyền tốt qua UDP 1.4 Cổng giao thức
Cổng giao thức được lưu trữ trong một số 16 bit và có giá trị từ 0 đến
65535 Dùng để phân biệt giữa 2 ứng dụng mạng với nhau gắn với địa chỉ IP và Socket
Một số cổng và các giao thức thông dụng:
21: File Transfer Protocol (FTP);
22: Secure Shell (SSH);
23: Telnet remote login service;
25: Simple Mail Transfer Protocol (SMTP);
53: Domain Name System (DNS) service;
80: Hypertext Transfer Protocol (HTTP) used in the World Wide Web;
110: Post Office Protocol (POP);
143: Internet Message Access Protocol (IMAP);
161: Simple Network Management Protocol (SNMP);
Trang 10 443: HTTPs with Transport Layer Security or Secure Sockets Layer (TLS/SSL)
Phân loại địa chỉ IP
Địa chỉ
network
192.168.1.0 172.112.0.0
- Các bit phần host đồng thời bằng 0
- Dùng để nhận biết giữa các mạng khác nhau Địa chỉ host 192.168.1.1 - Địa chỉ của các node mạng
(PC/Print/Router…) Địa chỉ
- Các bit phần host đồng thời bằng 1 Quảng bá mạng 192.168.1.0
- Các bit phần network & host đều bằng 1 Quảng bá toàn mạng
Phân lớp địa chỉ IP sử dụng
Hình 1.5 Phân lớp địa chỉ IP
Trang 111.6 Địa chỉ tên miền
DNS là từ viết tắt trong tiếng Anh của Domain Name System, là Hệ thống phân giải tên miền được phát minh vào năm 1984 cho Internet, chỉ một hệ thống cho phép thiết lập tương ứng giữa địa chỉ IP và tên miền
Nguyên tắc làm việc của DNS
Mỗi nhà cung cấp dịch vụ vận hành và duy trì DNS server riêng của mình, gồm các máy bên trong phần riêng của mỗi nhà cung cấp dịch vụ đó trong Internet Tức là, nếu một trình duyệt tìm kiếm địa chỉ của một website thì DNS server phân giải tên website này phải là DNS server của chính tổ chức quản lý website đó chứ không phải là của một tổ chức (nhà cung cấp dịch vụ) nào khác
INTERNIC (Internet Network Information Center) chịu trách nhiệm theo dõi các tên miền và các DNS server tương ứng INTERNIC là một tổ chức được thành lập bởi NFS (National Science Foundation), AT&T và Network Solution, chịu trách nhiệm đăng ký các tên miền của Internet INTERNIC chỉ có nhiệm vụ quản lý tất cả các DNS server trên Internet chứ không có nhiệm vụ phân giải tên cho từng địa chỉ
DNS có khả năng truy vấn các DNS server khác để có được một tên đã được phân giải DNS server của mỗi tên miền thường có hai việc khác biệt Thứ nhất, chịu trách nhiệm phân giải tên từ các máy bên trong miền về các địa chỉ Internet, cả bên trong lẫn bên ngoài miền nó quản lý Thứ hai, chúng trả lời các DNS server bên ngoài đang cố gắng phân giải những tên bên trong miền nó quản lý
DNS server có khả năng ghi nhớ lại những tên vừa phân giải Để dùng cho những yêu cầu phân giải lần sau Số lượng những tên phân giải được lưu lại tùy thuộc vào quy mô của từng DNS
Trang 12DNS: Cơ sở dữ liệu phân tán lưu các bản ghi tài nguyên (RR) Định dạng RR: (name, value, type, TTL)
Ví dụ: Khi bạn truy cập www.vfu.edu.vn thì trang web sẽ chuyển hướng đến vfu.edu.vn và ngược lại sử dụng CNAME kết hợp A Record
+ Name: Tên bí danh cho một tên thực nào đó;
+ Value: Tên thực
- Type = NS
+ Name: Domain (ví dụ: dantri.com.vn);
+ Value: Địa chỉ IP của authoritative ứng với domain đó
- Type = MX (Mail Exchange): Dùng để xác định Mail Server cho domain + Value: Tên của mailserver
1.7 Câu hỏi ôn tập
Câu 1: So sánh mô hình tham chiếu OSI với mô hình TCP/IP?
Câu 2: Dịch vụ vận chuyển hướng kết nối và không hướng kết nối được ứng dụng trong các lĩnh vực nào trong thực tế? Hãy kể ra một vài ứng dụng điển hình mỗi dịch vụ
Câu 3: Các phần mềm ứng dụng kết nối để trao đổi thông tin với nhau qua mạng cần những thông tin gì? Ý nghĩa của từng thông tin đó?
Trang 13Chương 2 LẬP TRÌNH MẠNG VỚI SOCKET Lập trình mạng là lập trình trao đổi thông tin giữa các phần mềm máy tính với nhau thông qua mạng kết nối Các phần mềm máy tính có thể hoạt động trong cùng một mạng cục bộ hoặc kết nối với nhau thông qua mạng Internet Mục đích của lập trình mạng nhằm mở rộng phạm vi hoạt động của các phần mềm máy tính, giúp cho các hoạt động của con người trở nên đơn giản hơn, chia
sẻ thông tin với nhau tốt hơn, giảm đi các chi phí cho các hoạt động trao đổi thông tin
Lập trình Socket là một trong nhiều cách thức lập trình truyền thông giữa các ứng dụng trong mạng máy tính được sử dụng tương đối phổ biến Lập trình Socket đơn giản, dễ cài đặt, có thể sử dụng dựa trên nhiêu giao thức khác nhau do vậy nó đảm bảo được nhiều nhu cầu truyền thông tin khác nhau của người dùng Trong chương này sẽ giới thiệu về kỹ thuật lập trình Socket và ứng dụng trong môi trường NET Chương này gồm các nội dung căn bản như sau:
- Các khái niệm căn bản về lập trình Socket;
- Lập trình Socket trong NET;
- Địa chỉ của máy tính chạy ứng dụng cần kết nối đến, đó chính là địa chỉ IP
- Địa chỉ ứng dụng trong máy tính Trong máy tính có thể có nhiều ứng dụng cùng hoạt động tại một thời điểm, vậy ứng dụng nào sẽ nhận được thông tin khi có tín hiệu gửi đến, để làm được việc đó các hệ điều hành sử dụng cổng ứng dụng (port) để làm việc đó Mỗi ứng dụng khi hoạt động sẽ đăng ký sử dụng một hoặc một vài cổng nào đó, và khi có tín hiệu gửi đến hệ điều hành sẽ dựa trên số cổng của tín hiệu gửi đến để cho phép ứng dụng nhận thông tin
Vậy Socket là gì? Có thể nói Socket là một thành phần giúp các phần mềm máy tính có thể trao đổi thông tin với nhau qua mạng kết nối
Trang 14Trong lập trình mạng dùng Socket, chúng ta không trực tiếp truy cập vào các thiết bị mạng để gửi và nhận dữ liệu Thay vì vậy, một tệp mô tả trung gian được tạo ra để điều khiển việc lập trình Các tệp mô tả dùng để tham chiếu đến các kết nối mạng được gọi là các Socket Socket định nghĩa những đặc trưng sau:
Một kết nối mạng hay một đường ống dẫn để truyền tải dữ liệu, đó là một kiểu truyền thông như Stream (luồng) hay Datagram (gói tin)
Một giao thức như TCP hay UDP
Sau khi một Socket được tạo ra nó phải được gắn vào một địa chỉ mạng và một cổng (port) trên hệ thống cục bộ hay ở xa Khi Socket đã được gắn vào các địa chỉ mạng và cổng, nó có thể đượ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 Phương thức tạo lập như sau: Socket (AddressFamily, SocketType, ProtocolType)
2.1.2 IP Address
IP Address là một đối tượng dùng để mô tả một địa chỉ IP, đối tượng này
có thể được sử dụng trong nhiều phương thức của Socket Một số phương thức của lớp IP Address
IPAddress
HostToNetworkOrder Chuyển 1 địa chỉ IP từ host byte order
thành network byte order IsLoopBack Cho biết địa chỉ IP có phải là địa chỉ
LoopBack hay không NetworkToHostOrder Chuyển 1 địa chỉ IP từ network byte
order thành host byte order
ToString Chuyển 1 đối tượng IP Address thành một chuỗi
Phương thức Parse() thường được dùng để tạo ra 1 thể hiện của IP Address:
Trang 15IPAddress localIpAddress = IPAddress.Parse("127.0.0.1");
Lớp IP Address cũng 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
2.1.3 IPEndPoint
IPEndPoint là một đối tượng mô tả sự kết hợp của một địa chỉ IP và port Đối tượng IPEndPoint được dùng để gắn kết các Socket với các địa chỉ cục bộ hoặc các địa chỉ ở xa
Hai thuộc tính của IPEndPoint có thể được dùng để lấy được vùng các port trên hệ thống là MinPort và MaxPort
Một số phương thức cần chú ý:
Phương thức khởi tạo:
- IPEndPoint (Int64, Int32)
- IPEndPoint (IPAddress, Int32)
Create: Tạo một EndPoint từ một địa chỉ Socket
ToString: Trả về địa chỉ IP và số hiệu cổng theo khuôn dạng địa chỉ, cổng
Trang 16NetworkStream;
Socket (AddressFamily af, SocketType st, ProtocolType pt)
2.1.5 Ví dụ về sử dụng Socket trong NET
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;
Trang 17Trong mô hình lập trình Client/Server, máy gửi yêu cầu đóng vai trò là Client, máy phục vụ trả lời yêu cầu đóng vai trò là Server Như vậy, Client chỉ hoạt động khi nào chúng có nhu cầu truy vấn thông tin, Server thì hoạt động liên tục để lắng nghe, tiếp nhận, xử lý yêu cầu và gửi trả kết quả cho Client
Như vậy phía Server sẽ hoạt động liên tục, do vậy trong lập trình người ta thường sử dụng một vòng lặp vô tận để lặp đi lặp lại việc lắng nghe, khi phát hiện có yêu cầu được gửi đến, phía Server sẽ tiếp nhận yêu cầu và cho yêu cầu được tiếp nhận vào hàng đợi Mô-đun xử lý yêu cầu phía Server sẽ lần lượt xử lý các yêu cầu trong hàng đợi bằng cách khởi tạo một tiến trình xử lý yêu cầu
Dữ liệu trao đổi giữa Client và Server phải được quy định một cách chặt chẽ về cú pháp để đảm bảo cả Client và Server đều hiểu Thông thường Client chỉ gửi các câu lệnh kèm theo các tham số, dữ liệu Server gửi về Client có thể ở nhiều dạng khác nhau, có thể là dạng văn bản, dạng bảng, dạng hình ảnh,
Để đảm bảo thông tin trao đổi giữa Client và Server được an toàn thì các
dữ liệu trao đổi giữa Client và Server thông thường được xử lý bằng thuật toán
mã hóa và giải mã thông tin
Dữ liệu từ Client hay Server trước khi gửi đi đểu phải chuyển đổi về dạng mảng Byte (mảng chứa các giá trị nguyên từ 0 đến 255) Do vậy, khi nhận được
dữ liệu thì Client hoặc Server thường phải biết được dữ liệu trước khi chuyển sang mảng Byte mà bên kia gửi đi, do vậy thường trong mảng Byte người lập trình thường bố trí một vài phần tử để chỉ ra loại dữ liệu được gửi đi trước khi chuyển sang mảng Byte để phía nhận biết được (việc sử dụng một hay nhiều phần tử ghép trong mảng Byte phụ thuộc vào người thiết kế chương trình, thông thường cả phía Client và Server đều được định nghĩa về các kiểu dữ liệu có thể trao đổi để tránh nhầm lẫn)
Sơ đồ trao đổi và xử lý thông tin theo mô hình Client/Server:
2.3 Lập trình Socket hướng kết nối (TCP Socket)
Trong lập trình Socket hướng kết nối, giao thức TCP được dùng để thành
Client
Server
Tiếp nhận yêu cầu
Xử lý yêu cầu Mảng Byte
Trang 18lậ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
Để lập trình Socket hướng kết nối ta phải thực hiện một loạt các thao tác giữa clien và Server như trong mô hình bên dưới:
Hình 2.1 Mô hình lập trình Socket hướng kết nối
2.3.1 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()
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
Socket server = new Socket(AddressFamily.InterNetwork,
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();
Trang 19Hàm Accept() này sẽ dừng Server lại và chờ cho đến khi nào có Client kết nối đến nó 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);
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
Việc gửi và nhận dữ liệu được thực hiện liên tục thông qua một vòng lặp
kế tiếp sẽ chỉ có thể nhận được dữ liệu tối đa bằng lần nhận dữ liệu trước
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 nhận;
Size: Số byte cần gửi;
SocketFlags: Chỉ ra cách nhận dữ liệu trên Socket
Phương thức Receive() trả về số byte dữ liệu nhận được từ Client Nếu
Trang 20không có dữ liệu được nhận, phương thức Receive() sẽ bị dừng lại và chờ cho tới khi có dữ liệu Khi Client gửi tín hiệu kết thúc phiên làm việc (bằng cách gửi
cờ FIN trong gói TCP), phương thức Receive() sẽ trả về giá trị 0 Khi phương thức Receive() trả về giá trị 0, ta đóng Socket của Client lại bằng phương thức Close() Socket chính (Server Socket) vẫn còn hoạt động để chấp nhận các kết nối khác Nếu không muốn Client nào kết nối đến nữa thì ta đóng Server lại luôn:
//buffer để nhận và gửi dữ liệu
byte[] buff = new byte[1024];
Trang 21IPEndPoint 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
Console.WriteLine("Da dong ket noi voi Client: {0}", clientep.Address);
Trang 222.3.2 Lập trình phía Client
Lập trình Socket hướng kết nối phía Client đơn giản hơn phía Server Client cũng 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() chứ không sử dụng phương thức Bind() giống như phía Server 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,
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
Trang 23gử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
Ví dụ: Chương trình TCP Client đơn giản:
//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;
Trang 24Console.WriteLine("Không thể kết nối đến Server"); Console.WriteLine(e.ToString());
//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);
Trang 25Các Socket phi kết nối cho phép gửi các thông điệp mà không cần phải thiết lập kết nối trước Một phương thức đọc sẽ đọc toàn bộ thông điệp được gửi bởi một phương thức gửi, điều này làm tránh được các rắc rối, phức tạp với biên
dữ liệu Thật không may mắn là giao thức phi kết nối UDP không đảm bảo dữ liệu được truyền tới đích Nhiều yếu tố như mạng bận, mạng bị đứt nữa chừng
có thể ngăn cản các gói tin được truyền tới đích
Nếu một thiết bị chờ dữ liệu từ một thiết bị ở xa, nó phải được gán một địa chỉ và port cục bộ, dùng hàm Bind() để gán Một khi đã thực hiện xong, thiết
bị có thể dùng Socket để gửi dữ liệu ra ngoài hay nhận dữ liệu từ Socket
Bởi vì thiết bị Client không tạo ra kết nối đến một địa chỉ Server cụ thể do
đó phương thức Connect() không cần dùng trong chương trình UDP Client Mô hình bên dưới mô tả các bước lập trình Socket phi kết nối:
Hình 2.2 Mô hình lập trình Socket phi kết nối Khi kết nối không được thành lập thì phương thức Send() và Receive() không được dùng bởi vì trong hai phương thức trên đều không chỉ ra địa chỉ đích của dữ liệu
Thay vào đó, Socket phi kết nối cung cấp hai phương thức để thực hiện việc này là SendTo() và ReceiveFrom()
2.4.1 Lập trình phía Server
UDP là một giao thức phi kết nối do đó các lập trình viên chỉ phải 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,
Trang 26Phương thức SendTo() dùng để gửi dữ liệu, phương thức này chỉ ra dữ liệu để gửi và IPEndPoint của thiết bị nhận Có nhiều quá tải hàm của phương thức SendTo() có thể được dùng tùy vào yêu cầu cụ thể
SendTo(byte[] data, EndPoint Remote) Phương thức trên gửi một mảng dữ liệu đến một EndPoint được chỉ ra bởi Remote Một quá tải hàm khác phức tạp hơn của phương thức SendTo()
SendTo(byte[] data, SocketFlags Flags, EndPoint Remote) Phương thức này cho phép thêm cờ SocketFlag, nó chỉ ra các tùy chọn UDP được sử dụng Để chỉ ra số byte được gửi từ mảng byte ta sử dụng quá tải hàm sau của phương thức SendTo():
SendTo(byte[] data, int Offset, int Size, SocketFlags Flags, EndPoint Remote)
Phương thức ReceiveFrom() có dùng định dạng với phương thức SendTo(), chỉ có một điểm khác biệt sau ở cách EndPoint được khai báo Phương thức ReceiveFrom() đơn giản được định nghĩa như sau:
ReceiveFrom(byte[] data, ref EndPoint Remote) Cũng như thông thường, tham số thứ nhất là một mảng byte được định nghĩa để nhận dữ liệu, tham số thứ hai ra phải truyền tham chiếu của đối tượng EndPoint Tham chiếu này tham chiếu đến vị trí bộ nhớ nơi biến được lưu trữ Phương thức ReceiveFrom() sẽ đặt thông tin EndPoint từ thiết bị ở xa vào vùng
bộ nhớ của đối tượng EndPoint tham chiếu đến Bằng việc sử dụng đối số thứ hai là tham chiếu ta sẽ lấy được địa chỉ IP và port của máy ở xa
Ví dụ: chương trình UDP Server đơn giản
using System;
Trang 27byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); Socket newsock = new
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine("Thong diep duoc nhan tu {0}:",
Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
string welcome = "Hello Client";
data = new byte[1024];
recv = newsock.ReceiveFrom(data, ref Remote);
Trang 28Để chương trình UDP chấp nhận các thông điệp UDP đến, nó phải được gắn với một port trên hệ thống Việc này được thực hiện bằng cách tạo ra một đối tượng IPEndPoint sử dụng một địa chỉ IP cục bộ thích hợp, trong trường hợp này ta chỉ ra IPAddresss.Any để có thể dùng bất kỳ địa chỉ IP nào trên máy cục
bộ để lắng nghe
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:
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine("Thong diep duoc nhan tu
{0}:",Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); string welcome = "Hello client";
Khi gửi câu chào cho Client xong, Server sẽ bắt đầu nhận và gửi thông điệp
2.4.2 Lập trình phía Client
Bởi vì Client không cần chờ trên một port UDP định sắn nên nó cũng chẳng cần dùng phương thức Bind(), thay vì vậy nó sẽ lấy một port ngẫu nhiên trên hệ thống khi dữ liệu được gửi và nó giữa port này để nhận dữ liệu trả về chương trình UDP Client cũng tương tự chương trình UDP Server:
Ví dụ: Chương trình UDP Client đơn giản
Trang 29int recv = server.ReceiveFrom(data, ref Remote);
Console.WriteLine("Thong diep duoc nhan tu {0}:",
Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
data = new byte[1024];
recv = server.ReceiveFrom(data, ref Remote);
stringData = Encoding.ASCII.GetString(data, 0, recv);
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
Chương trình Client gửi thông điệp đến Server và chờ câu chào trả về từ Server Bởi vì Client không cần chấp nhận các thông điệp UDP trên một port định trước nên Client không dùng phương thức Bind() Nó sẽ nhận các thông điệp
Trang 30UDP trên cùng port mà nó đã gửi
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 Nếu người dùng nhận vào “exit” thì vòng lặp sẽ thoát và kết nối bị đóng lại
Không giống như chương trình TCP Server, 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ì nó phải gửi thông điệp ngắt kết nối cho Server biết
2.5 Sử dụng các lớp hỗ trợ được xây dựng từ lớp Socket
TcpClient newcon = new TcpClient();
TcpClient newcon = new TcpClient(iep); newcon.Connect("192.168.6.2", 8000);
TcpClient(String host, int port):
Phương thức tạo lập thứ ba này thường được sử dụng nhất, nó cho phép chỉ ra thiết bị nhận trong phương thức tạo lập và không cần phải dùng phương
Trang 31thức Connect() Địa chỉ của thiết bị ở xa có thể là một chuỗi hostname hoặc một chuỗi địa chỉ IP Phương thức tạo lập của TcpClient sẽ tự động phân giải hostname thành địa chỉ IP
Ví dụ:
TcpClient newcon = new TcpClient("www.isp.net", 8000);
Mỗi khi đối tượng TcpClient được tạo ra, nhiều thuộc tính và phương thức
có thể được dùng để xử lý việc truyền dữ liệu qua lại giữa các thiết bị
Close() Đóng kết nối TCP
Connect() Thành lập kết nối TCP với thiết bị ở xa
Equals() So sánh hai đối tượng TcpClient
GetHashCode() Lấy mã hash code
GetStream() Lấy đối tượng Stream nó có thể dùng để gửi và nhận dữ liệu GetType() Lấy kiểu của thể hiện hiện tại
ToString() Chuyển thể hiện hiện tại sang kiểu chuỗi
Phương thức Connect() dùng để kết nối đối tượng TcpClient đến thiết bị ở
xa Mỗi khi kết nối được thành lập, phương thức GetStream() gán một đối tượng NetworkStream để gửi và nhận dữ liệu nhờ vào phương thức Read() và Write() Lớp TcpClient còn có nhiều thuộc tính được mô tả trong bảng sau:
Thuộc Tính Mô Tả
LingerState Lấy hoặc thiết lập thời gian kết nối TCP vẫn còn sau khi
gọi phương thức Close() NoDelay Lấy hoặc thiết lập thời gian trễ được dùng để gửi hoặc nhận
ở bộ đệm TCP ReceiveBufferSize Lấy hoặc thiết lập kích thước bộ đệm TCP nhận
ReceiveTimeout Lấy hoặc thiết lập thời gian timeout của Socket
SendBufferSize Lấy hoặc thiết lập kích thước bộ đệm TCP gửi
SendTimeout Lấy hoặc thiết lập giá trị timeout của Socket
Ví dụ: Chương trình TcpClient đơn giản:
Trang 32byte[] data = new byte[1024]; string input,
stringData; TcpClient server;
int recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData);
data = new byte[1024];
recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(stringData);
Trang 33Sau khi đối tượng NetworkStream được tạo ra, ta có thể dùng phương thức Read() và Write() để nhận và gửi dữ liệu:
while (true)
{
input = Console.ReadLine(); if (input == "exit") break; ns.Write(Encoding.ASCII.GetBytes(input), 0, input.Length); ns.Flush();
data = new byte[1024];
recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(stringData);
}
Phương thức Read() yêu cầu 3 tham số:
Mảng các byte để đặt dữ liệu nhận vào;
Vị trí offset trong bộ đệm mà tại đó ta muốn đặt dữ liệu;
Chiều dài của bộ đệm dữ liệu
Cũng giống như phương thức Receive() của Socket, phương thức Read()
sẽ đọc một lượng dữ liệu có độ lớn tối đa đúng bằng độ lón bộ đệm Nếu bộ đệm quá nhỏ, phần dữ liệu còn lại sẽ nằm ở trong stream và đợi lần gọi phương thức Read() tiếp theo
Phương thức Write() cũng yêu cầu ba tham số:
Mảng các byte để gửi dữ liệu;
Vị trí offset trong bộ đệm mà tại đó ta muốn gửi dữ liệu;
Chiều dài của dữ liệu được gửi
Trang 34Cần chú ý rằng TCP không bảo vệ các biên thông điệp Điều này cũng áp dụng cho lớp TcpClient, do đó ta cần phải xử lý vấn đề biên thông điệp giống như phương thức Receive() của lớp Socket bằng cách tạo ra vòng lặp để đảm bảo tất cả dữ liệu đều được đọc từ stream
2.5.2 Lớp TCPListener
Cũng giống như lớp TcpClient, lớp TcpListener cũng cho phép chúng ta tạo ra các chương trình TCP Server một cách đơn giản
Lớp TcpListener có ba phương thức tạo lập:
TcpListener(int port): Gắn một đối tượng TcpListener vào một port được chỉ ra trên máy cục bộ
TcpListener(IPEndPoint ie): Gắn một đối tượng TcpListener vào một đối tượng EndPoint cục bộ
TcpListener(IPAddress addr, int port): Gắn một đối tượng TcpListener vào một đối tượng IPAddress và một port
Không giống như lớp TcpClient, các phương thức tạo lập của lớp TcpListener yêu cầu ít nhất một tham số: số port mà Server lắng nghe kết nối Nếu Server có nhiều card mạng và ta muốn lắng nghe trên một card mạng nào
đó thì ta có thể dùng một đối tượng IPEndPoint để chỉ ra địa chỉ IP của card cùng với số port dùng để lắng nghe
Lớp TcpListener có một số phương thức sau:
AcceptSocket() Chấp nhận kết nối trên port và gán kết nối cho một đối
tượng Socket AcceptTcpClient() Chấp nhận kết nối trên port và gán kết nối cho một đối
tượng TCPClient Equals() So sánh hai đối tượng TcpListener
GetHashCode() Lấy hash code
GetType() Lấy kiểu của thể hiện hiện tại
Pending() Kiểm tra xem có yêu cầu đang chờ kết nối hay không
Start() Bắt đầu lắng nghe kết nối
Stop() Ngừng lắng nghe kết nối
Trang 35ToString() Chuyển đối tượng TcpListener thành chuỗi
Phương thức Start() tương tự như phương thức Bind() và Listen() được dùng ở lớp socket Phương thức Start() kết nối Socket đến EndPoint được định nghĩa ở phương thức tạo lập của lớp TcpListener và đặt TCP port vào chế độ lắng nghe, sẵng sàng chấp nhận kết nối Phương thức AcceptTcpClient() có thể
so sánh với phương thức Accept() của Socket, chấp nhận kết nối và gán nó cho một đối tượng TcpClient
Sau khi đối tượng TcpClient được tạo ra, tất cả các truyền thông với thiết
bị ở xa được thực hiện với đối tượng TcpClient mới chứ không phải với đối tượng
TcpListener ban đầu, do đó đối tượng TcpListener có thể được dùng để chấp nhận kết nối khác Để đóng đối tượng TcpListener ta dùng phương thức Stop()
byte[] data = new byte[1024];
TcpListener newsock = new TcpListener(5000);
Trang 36recv = ns.Read(data, 0, data.Length);
TcpListener newsock = new TcpListener(5000); newsock.Start(); Console.WriteLine("Dan cho client ket noi den ");
TcpClient client = newsock.AcceptTcpClient();
NetworkStream ns = client.GetStream();
Khi đối tượng TcpClient được thành lập, một đối tượng NetworkStream được gán vào để truyền thông với máy ở xa Tất cả các thông tin liên lạc đều được thực hiện bằng cách sử dụng phương thức Read() và Write()
2.5.3 Lớp UdpClient
Lớp UdpClient được tạo ra để giúp cho việc lập trình mạng với giao thức UDP được đơn giản hơn Lớp UdpClient có bốn phương thức tạo lập:
UdpClient(): Tạo ra một đối tượng UdpClient nhưng không gắn vào bất
kỳ địa chỉ hay port nào
UdpClient(int port): Gắn đối tượng UdpClient mới tạo vào một port
UdpClient(IPEndPoint iep): Gắn đối tượng UdpClient mới tạo vào một địa chỉ IP cục bộ và một port
UdpClient(string host, int port): Gắn đối tượng UdpClient mới tạo vào một địa chỉ IP vào một port bất kỳ và kết hợp nó với một địa chỉ IP và port ở xa
Các phương thức tạo lập của lớp UdpClient tương tự như các phương thức
Trang 37tạo lập của lớp TcpClient, chúng ta có thể để cho hệ thống chọn port thích hợp cho ứng dụng hoặc ta có thể chỉ ra port được dùng trong ứng dụng Nếu ứng dụng UDP phải chấp nhận dữ liệu trên một port nào đó, ta phải định nghĩa port
đó trong phương thức tạo lập của lớp UdpClient
Một số phương thức của lớp UdpClient:
Close() Đóng Socket ở bên dưới
Connect() Cho phép chỉ ra IP endpoint ở xa để gửi và nhận dữ liệu DropMulticastGroup() Gỡ bỏ Socket từ 1 nhóm UDP multicast
Equals() So sánh hai đối tượng UdpClient
GetHashCode() Lấy hash code
GetType() Lấy kiểu của đối tượng hiện tại
JoinMulticastGroup() Thêm Socket vào một nhóm UDP multicast
Receive() Nhận dữ liệu từ Socket
Send() Gửi dữ liệu đến thiết bị ở xa từ Socket
ToString() Chuyển đối tượng UdpClient thành chuỗi
Có nhiều chỗ khác nhau giữa phương thức Send(), Receive() của lớp UdpClient và phương thức SendTo(), ReceiveFrom() của Socket
Phương thức Receive()
Lớp UdpClient sử dụng phương thức Receive() để chấp nhận các gói tin trên một card mạng và một port Chỉ có một cách sử dụng của phương thức Receive(): byte[] Receive(ref IPEndPoint iep)
Khi dữ liệu được nhận từ Socket, nó không đặt vào mảng byte trong phương thức như trong phương thức ReceiveFrom() mà nó sẽ trả về một mảng byte Sự khác nhau thứ hai là phương thức ReceiveFrom() đặt thông tin của máy
ở xa vào một đối tượng EndPoint còn phương thức Receive() đặt thông tin của máy ở xa vào một đối tượng IPEndPoint, việc này có thể làm cho lập trình viên cảm thấy dễ lập trình hơn
Khi nhiều dữ liệu được nhận hơn kích thước bộ đệm, thay vì phát sinh ra một biệt lệ như trong phương thức ReceiveFrom() của Socket, UdpClient tả về một bộ đệm dữ liệu đủ lớn để chứa dữ liệu nhận đây là một tính năng rất hay của phương thức Receive()
Phương thức Send()
Trang 38Phương thức Send() có ba quá tải hàm để gửi dữ liệu tới thiết bị ở xa:
Send(byte[] data, int sz): Gửi một mảng dữ liệu với kích thước là sz đến thiết bị ở xa mặc định Để dùng quá tải hàm này, ta phải chỉ ra thiết bị ở xa mặc
Định bằng cách hoặc sử dụng phương thức tạo lập của lớp UdpClient hoặc dùng phương thức Connect()
Send(byte[] data, int sz, IPEndPoint iep): Cho phép gửi mảng dữ liệu có kích thước sz đến thiết bị ở xa được chỉ ra bởi iep
Send(byte[] data, int sz, string host, int port): Gửi mảng dữ liệu kích thước sz đến máy ở xa và port được chỉ ra
Ví dụ: Chương trình UdpClient Client/Server:
byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); UdpClient newsock = new UdpClient(ipep);
Console.WriteLine("Dang cho client ket noi den "); IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); data = newsock.Receive(ref sender);
Console.WriteLine("Thong diep duoc nhan tu {0}:",
sender.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, data.Length));
string welcome = "Xin chao client"; data =
Trang 39byte[] data = new byte[1024];
string input, stringData;
UdpClient server = new UdpClient("127.0.0.1", 5000); IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); string welcome = "Xin chao server";
data = Encoding.ASCII.GetBytes(welcome);
server.Send(data, data.Length);
data = server.Receive(ref sender);
Console.WriteLine("Thong diep duoc nhan tu {0}:",
Trang 40input.Length);
data = server.Receive(ref sender);
stringData = Encoding.ASCII.GetString(data, 0, data.Length);
2.6.1 Lập trình sự kiện của Windows
Trong lập trình sự kiện trong Windows, mỗi khi một sự kiện xảy ra, một phương thức được gọi để thực thi dựa trên sự kiện đó như trong hình dưới đây:
Hình 2.3 Mô hình xử lý sự kiện trọng windows
Trong các chương trước, chúng ta đã lập trình Socket trong chế độ blocking Socket ở chế độ blocking sẽ chờ mãi mãi cho đến khi hoàn thành nhiệm vụ của nó Trong khi nó bị blocking thì các chức năng khác của chương trình không thực hiện được
Khi lập trình Windows thì lúc gọi một phương thức bị blocking thì toàn
bộ chương trình sẽ đứng lại và không thực hiện các chức năng khác được Do đó việc lập trình bất đồng bộ là cần thiết để cho chương trình khỏi bị đứng
Sử dụng Event và Delegate