0

Bài Giảng Tóm Tắt Lập Trình Mạng

177 35 1
  • Bài Giảng Tóm Tắt Lập Trình Mạng

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Tài liệu liên quan

Thông tin tài liệu

Ngày đăng: 19/01/2021, 05:02

Để sử dụng giá trị checksum trong một chương trình ứng dụng ICMP, đầu tiên điền tất cả các thành phần dữ liệu, thiết lập thành phần Checksum thành 0, tiếp theo gọi phương thức getCheck[r] (1)KHOA CÔNG NGHỆ THÔNG TIN BÀI GIẢNG TÓM TẮT LẬP TRÌNH MẠNG Dành cho sinh viên ngành Cơng Nghệ Thông Tin (Lưu hành nội bộ) (2)Trang MỤC LỤC CHƯƠNG I: NHỮNG KIẾN THỨC CƠ BẢN VỀ LẬP TRÌNH MẠNG I.1.TỔNG QUAN I.1.1 Tầng Ethernet I.1.2 Địa Ethernet I.1.3 Ethernet Protocol Type I.1.4 Data payload I.1.5 Checksum 10 I.2.TẦNG IP 10 I.2.1 Trường địa 11 I.2.2 Các cờ phân đoạn 11 I.2.3 Trường Type of Service 12 I.2.4 Trường Protocol 12 I.3.TẦNG TCP 13 I.3.1 TCP port 14 I.3.2 Cơ chế đảm bảo độ tin cậy truyền tải gói tin 16 I.3.3 Quá trình thành lập phiên làm việc TCP 17 I.4.TẦNG UDP 18 CHƯƠNG II: LẬP TRÌNH SOCKET HƯỚNG KẾT NỐI 21 II.1.SOCKET 21 II.2.IPADDRESS 24 II.3.IPENDPOINT 25 II.4.LẬP TRÌNH SOCKET HƯỚNG KẾT NỐI 25 II.4.1 Lập trình phía Server 26 II.4.2 Lập trình phía Client 30 II.4.3 Vấn đề với đệm liệu 32 II.4.4 Xử lý với đệm có kích thước nhỏ 33 II.4.5 Vấn đề với thông điệp TCP 35 II.4.6 Giải vấn đề với thông điệp TCP 39 II.4.6.1 Sử dụng thông điệp với kích thước cố định 39 (3)Trang II.4.6.3 Sử dụng hệ thống đánh dấu để phân biệt thông điệp 50 II.4.7 Sử dụng C# Stream với TCP 50 II.4.7.1 Lớp NetworkStream 50 II.4.7.2 Lớp StreamReader StreamWriter 54 CHƯƠNG III: LẬP TRÌNH SOCKET PHI KẾT NỐI 59 III.1.TỔNG QUAN 59 III.2.LẬP TRÌNH PHÍA SERVER 60 III.3.LẬP TRÌNH PHÍA CLIENT 62 III.3.1 Sử dụng phương thức Connect() chương trình UDP Client 64 III.3.2 Phân biệt thông điệp UDP 65 III.4.NGĂN CẢN MẤT DỮ LIỆU 67 III.5.NGĂN CẢN MẤT GÓI TIN 70 III.5.1 Sử dụng Soket Time-out 71 III.6.ĐIỀU KHIỂN VIỆC TRUYỀN LẠI CÁC GÓI TIN 73 CHƯƠNG V: SỬ DỤNG CÁC LỚP HELPER CỦA C# SOCKET 79 IV.1.LỚP TCPCLIENT 79 IV.2.LỚP TCPLISTENER 82 IV.3.LỚP UDPCLIENT 85 CHƯƠNG V: ĐA NHIỆM TIỂU TRÌNH 89 V.1.KHÁI NIỆM TIẾN TRÌNH VÀ TIỂU TRÌNH CỦA WINDOWS 89 V.2.MƠ HÌNH 89 V.3.CÁC KỸ THUẬT TRONG NET TẠO TIỂU TRÌNH 90 V.3.1 Tạo tiểu trình Thread-pool 90 V.3.2 Tạo tiểu trình bất đồng 93 V.3.2.1 Phương thức BlockingExample 96 V.3.2.2 Phương thức PollingExample 97 V.3.2.3 Phương thức WaitingExample 98 V.3.2.4 Phương thức WaitAllExample 99 V.3.2.5 Phương thức CallbackExample 100 V.3.3 Thực thi phương thức Timer 102 V.3.4 Thực thi phương thức tiểu trình 104 V.3.5 Điều khiển trình thực thi tiểu trình 106 V.3.6 Nhận biết tiểu trình kết thúc 110 (4)Trang V.3.8 Kết thúc tiến trình 114 V.4.THỰC THI PHƯƠNG THỨC BẰNG CÁCH RA HIỆU ĐỐI TƯỢNG WAITHANDLE 115 CHƯƠNG V: ĐỒNG BỘ HÓA 117 VI.1.LÝ DO ĐỒNG BỘ HÓA 117 VI.2.CÁC PHƯƠNG PHÁP ĐỒNG BỘ HÓA 117 VI.3.PHƯƠNG PHÁP SEMAPHORE 117 VI.4.PHƯƠNG PHÁP DÙNG LỚP MONITOR 119 VI.5.SYSTEM.THREADING.WAITHANDLE, BAO GỒM AUTORESETEVENT, MANUALRESETEVENT 121 VI.6.PHƯƠNG PHÁP MUTEX 124 CHƯƠNG V: LẬP TRÌNH SOCKET BẤT ĐỒNG BỘ 126 VII.1.LẬP TRÌNH SỰ KIỆN TRONG WINDOWS 126 VII.1.1 Sử dụng Event Delegate 127 VII.1.2 Lớp AsyncCallback lập trình Windows 129 VII.2.SỬ DỤNG SOCKET BẤT ĐỒNG BỘ 129 VII.2.1 Thành lập kết nối 130 VII.2.1.1 Phương thức BeginAccept() EndAccept() 130 VII.2.1.2 Phương thức BeginConnect() EndConnect() 132 VII.2.2 Gởi liệu 133 VII.2.2.1 Phương thức BeginSend() phương thức EndSend() 133 VII.2.2.2 Phương thức BeginSendTo() EndSendTo() 134 VII.2.3 Nhận liệu 135 VII.2.3.1 Phương thức BeginReceive(), EndReceive, BeginReceiveFrom(), EndReceiveFrom() 135 VII.2.4 Chương trình WinForm gởi nhận liệu Client Server 135 VII.2.4.1 Chương trình Server 135 VII.2.4.2 Mơ hình chương trình Server 135 VII.2.4.3 Lớp ServerProgram 136 VII.2.4.4 Lớp ServerForm 139 VII.2.5 Chương trình Client 140 VII.2.5.1 Mơ hình chương trình Client 141 VII.2.5.2 Lớp ClientProgram 142 (5)Trang VII.3.LẬP TRÌNH SOCKET BẤT ĐỒNG BỘ SỬ DỤNG TIỂU TRÌNH 146 VII.3.1 Lập trình sử dụng hàng đợi gởi hàng đợi nhận thông điệp 146 VII.3.2 Lập trình ứng dụng nhiều Client 152 (6)Trang CHƯƠNG I: NHỮNG KIẾN THỨC CƠ BẢN VỀ LẬP TRÌNH MẠNG I.1 Tổng quan Internet Protocol (IP) tảng lập trình mạng IP phương tiện truyền tải liệu hệ thống hệ thống mạng cục (LAN) hay hệ thống mạng diện rộng (WAN) Mặc dù lập trình viên mạng chọn giao thức khác để lập trình IP cung cấp kỹ thuật mạnh để gởi liệu thiết bị, đặc biệt thông qua mạng Internet Để hiểu rõ khái niệm bên lập trình mạng, phải hiểu rõ giao thức IP, hiểu cách chuyển liệu thiết bị mạng Lập trình mạng dùng giao thức IP thường phức tạp Có nhiều yếu tố cần quan tâm liên quan đến cách liệu gởi qua mạng: số lượng Client Server, kiểu mạng, tắc nghẽn mạng, lỗi mạng,… Bởi yếu tố ảnh hưởng đến việc truyền liệu từ thiết bị đến thiết bị khác mạng việc hiểu rõ chúng vấn đề quan trọng để lập trình mạng thành cơng Một gói liệu mạng gồm nhiều tầng thông tin Mỗi tầng thông tin chứa dãy byte đặt theo trật tự định sẵn Hầu hết gói liệu dùng lập trình mạng chứa ba tầng thông tin với liệu dùng để truyền tải thiết bị mạng Hình sau mơ tả hệ thống thứ bậc gói IP: Hình I.1: Các tầng giao thức mạng gói liệu I.1.1 Tầng Ethernet (7)Trang rãi mạng Ethernet Hầu hết thiết bị mạng kể hệ điều hành Windows mặc định dùng giao thức Ethernet phiên để truyền tải gói IP Hình I.2: Ethernet Header Phần đầu Ethernet phiên địa MAC (Media Access Card) dùng để xác định thiết bị mạng với số giao thức Ethernet xác định giao thức tầng chứa gói Ethernet Mỗi gói Ethernet bao gồm: • byte địa MAC đích • byte địa MAC nguồn • byte xác định giao thức tầng • Data payload từ 46 đến 1500 byte • 4-byte checksum (8)Trang Địa Ethernet (địa MAC) địa thiết bị, địa gán nhà sản xuất thiết bị mạng không thay đổi Mỗi thiết bị mạng Ethernet phải có địa MAC Địa MAC gồm phần: • byte xác định nhà sản xuất • byte xác định số serial nhà sản xuất Giản đồ địa Ethernet cho phép địa broadcast multicast Đối với địa broadcast tất bit địa đích gán (FFFFFFFFFFFF) Mỗi thiết bị mạng chấp nhận gói có địa broadcast Địa hữu ích cho giao thức phải gởi gói truy vấn đến tất thiết bị mạng Địa multicast loại địa đặc biệt địa Ethernet, địa multicast cho phép số thiết bị chấp nhận gói tin Một số địa Ethernet multicast: Địa Chỉ Mô Tả 01-80-C2-00-00-00 Spanning tree (for bridges) 09-00-09-00-00-01 HP Probe 09-00-09-00-00-01 HP Probe 09-00-09-00-00-04 HP DTC 09-00-2B-00-00-00 DEC MUMPS 09-00-2B-00-00-01 DEC DSM/DTP 09-00-2B-00-00-02 DEC VAXELN 09-00-2B-00-00-03 DEC Lanbridge Traffic Monitor (LTM) 09-00-2B-00-00-04 DEC MAP End System Hello 09-00-2B-00-00-05 DEC MAP Intermediate System Hello 09-00-2B-00-00-06 DEC CSMA/CD Encryption 09-00-2B-00-00-07 DEC NetBios Emulator 09-00-2B-00-00-0F DEC Local Area Transport (LAT) 09-00-2B-00-00-1x DEC Experimental 09-00-2B-01-00-00 DEC LanBridge Copy packets (all bridges) 09-00-2B-02-00-00 DEC DNA Lev Routing Layer Routers 09-00-2B-02-01-00 DEC DNA Naming Service Advertisement 09-00-2B-02-01-01 DEC DNA Naming Service Solicitation 09-00-2B-02-01-02 DEC DNA Time Service (9)Trang Địa Chỉ Mô Tả 09-00-2B-04-00-00 DEC Local Area System Transport (LAST) 09-00-2B-23-00-00 DEC Argonaut Console 09-00-4E-00-00-02 Novell IPX 09-00-77-00-00-01 Retix spanning tree bridges 09-00-7C-02-00-05 Vitalink diagnostics 09-00-7C-05-00-01 Vitalink gateway 0D-1E-15-BA-DD-06 HP CF-00-00-00-00-00 Ethernet Configuration Test protocol (Loopback) I.1.3 Ethernet Protocol Type Một phần khác quan trọng Ethernet Header trường Protocol Type, trường có kích thước hai byte Sự khác gói tin Ethernet phiên Ethernet 802.2 802.3 xảy trường Các gói tin Ethernet 802.2 802.3 sử dụng trường biết kích thước gói tin Ethernet Ethernet phiên dùng trường để định nghĩa giao thức tầng gói tin Ethernet Một số giá trị trường này: Giá Trị Giao Thức 0800 IP 0806 ARP 0BAD Banyan VINES 8005 HP Probe 8035 Reverse ARP 809B AppleTalk 80D5 IBM SNA 8137 Novell 8138 Novell 814C Raw SNMP 86DD IPv6 876B TCP/IP compression (10)Trang 10 Data payload phải chứa tối thiểu 46 byte để đảm bảo gói Ethernet có chiều dài tối thiểu 64 byte Nếu phần data chưa đủ 46 byte ký tự đệm thêm vào cho đủ Kích thước trường từ 46 đến 1500 byte I.1.5 Checksum Giá trị checksum cung cấp chế kiểm tra lỗi cho liệu, kích thước trường byte Nếu gói tin bị hỏng lúc truyền, giá trị checksum bị tính tốn sai gói tin đánh dấu gói tin xấu I.2 Tầng IP Tẩng IP định nghĩa thêm nhiều trường thông tin của giao thức Ethernet Hình I.3: Thơng tin tầng IP Các trường tầng IP: Trường Bit Mô Tả (11)Trang 11 Trường Bit Mô Tả Type of Service Kiểu chất lượng dịch vụ QoS (Quality of Service) Total Length 16 Chiều dài gói IP Identification 16 Giá trị ID xác định gói IP Flags Cho biết gói IP có bị phân đoạn hay khơng hay cịn phân đoạn khác Fragment offset 13 Vị trí phân đoạn gói IP Time to Live (TTL) 8 Thời gian tối đa gói tin phép lại mạng (được tính giây) Protocol Kiểu giao thức tầng liệu Header Checksum 16 Checksum liệu gói IP header Source Address 32 Địa IP thiết bị gởi Destination Address 32 Địa IP thiết bị nhận Options Định nghĩa đặc điểm gói IP tươnglai I.2.1 Trường địa Địa Ethernet dùng để xác định thiết bị mạng LAN khơng thể dùng để xác định địa thiết bị mạng xa Để xác định thiết bị mạng khác nhau, địa IP dùng Một địa IP số 32 bit địa IP chia thành lớp sau: Lớp A 0.x.x.x–127.x.x.x Lớp B 128.x.x.x–191.x.x.x Lớp C 192.x.x.x–223.x.x.x Lớp D 224.x.x.x–254.x.x.x I.2.2 Các cờ phân đoạn (12)Trang 12 Sự phân đoạn thành lập nhờ vào việc sử dụng trường gói IP: fragmentation flags, fragment offset, trường identification Cờ phân đoạn bao gồm ba cờ bit sau: • Cờ reserved: giá trị zero • Cờ Don’t Fragment: cho biết gói IP khơng bị phân đoạn • Cờ More Fragment: cho biết gói tin bị phân đoạn phân đoạn khác Trường IP Indentification xác định định danh gói IP Tất phân đoạn gói IP có số indentification Số identification giúp cho phần mềm máy nhận biết phân đoạn thuộc gói IP ráp lại cho Trường fragment offset cho biết vị trí phân đoạn gói tin ban đầu I.2.3 Trường Type of Service Trường Type of Service xác định kiểu chất lượng dịch vụ QoS (Quality of Service) cho gói IP Trường dùng để đánh dấu gói IP có độ ưu tiên chẳng hạn dùng để tăng độ ưu tiên liệu cần thời gian thực Video, Audio Trong hầu hết truyền tải mạng, trường được thiết lập giá trị zero, cho biết liệu bình thường, nhiên với ứng dụng cần thời gian thực Video hay Audio trường sử dụng để tăng độ ưu tiên cho gói liệu Trường gồm tám bit ý nghĩa bit sau: • bit dùng làm trường ưu tiên • bit cho biết thời gian trễ bình thường hay thấp • bit cho biết thơng lượng bình thường hay cao • bit cho biết độ tin cậy bình thường hay cao • bit dùng tương lai I.2.4 Trường Protocol (13)Trang 13 Giá Trị Giao Thức 1 Internet Control Message (ICMP) 2 Internet Group Message (IGP) 6 Transmission Control (TCP) 8 Exterior Gateway (EGP) 9 Interior Gateway (Cisco IGP) 17 User Datagram (UDP) 88 Cisco EIGRP Hai giao thức dùng nhiều lập trình mạng TCP UDP I.3 Tầng TCP Giao thức TCP (Transmission Control Protocol) giao thức hướng kết nối, cho phép tạo kết nối điểm tới điểm hai thiết bị mạng, thiết lập đường quán để truyền tải liệu TCP đảm bảo liệu chuyển tới thiết bị đích, liệu khơng tới thiết bị đích thiết bị gởi nhận thơng báo lỗi (14)Trang 14 Hình I.4: Các trường TCP Header Mỗi trường TCP Header kết hợp với chức đặc biệt phiên làm việc TCP Có số chức quan trọng sau: • Source port Destination port: theo dõi kết nối thiết bị • Sequence Acknowledgement number: theo dõi thứ tự gói tin truyền tải lại gói tin bị • Flag: mở đóng kết nối thiết bị để truyền tải liệu I.3.1 TCP port TCP sử dụng port để xác định kết nối TCP thiết bị mạng Để liên lạc với ứng dụng chạy thiết bị mạng xa ta cần phải biết hai thơng tin : • Địa IP thiết bị xa (15)Trang 15 Để kết nối TCP thành lập, thiết bị xa phải chấp nhận gói tin truyền đến port gán Bởi có nhiều ứng dụng chạy thiết bị sử dụng TCP thiết bị phải cấp phát cổng khác cho ứng dụng khác Hình I.5: Kết nối TCP đơn giản Trong hình thiết bị A chạy hai ứng dụng Server, hai ứng dụng chờ gói tin từ Client Một ứng dụng gán port 8000 ứng dụng gán port 9000 Thiết bị mạng B muốn kết nối đến thiết bị mạng A phải gán TCP port trống từ hệ điều hành port mở suốt phiên làm việc Các port Client thường không quan trọng gán port hợp lệ thiết bị Tổ hợp địa IP port IP endpoint Một phiên làm việc TCP định nghĩa kết hợp IP endpoint cục IP endpoint xa Một ứng dụng mạng sử dụng IP endpoint cục thiết bị xa phải sử dụng địa IP hay port riêng IANA định nghĩa danh sách port TCP tiêu chuẩn gán cho ứng dụng đặc biệt: Port Mô Tả 7 Echo 13 Daytime 17 Quote of the day 20 FTP (data channel) (16)Trang 16 Port Mô Tả 22 SSH 23 Telnet 25 SMTP 37 Time 80 HTTP 110 POP3 119 NNTP 123 Network Time Protocol (NTP) 137 NETBIOS name service 138 NETBIOS datagram service 143 Internet Message Access Protocol (IMAP) 389 Lightweight Directory Access Protocol (LDAP) 443 Secure HTTP (HTTPS) 993 Secure IMAP 995 Secure POP3 Các port từ 0->1023 gán cho ứng dụng thơng dụng với ứng dụng mà lập trình viên tạo port gán phải từ 1024->65535 I.3.2 Cơ chế đảm bảo độ tin cậy truyền tải gói tin Trường TCP Header sau port số sequence acknowledgement Những giá trị cho phép TCP theo dõi gói tin đảm bảo nhận theo thứ tự Nếu gói tin bị lỗi, TCP yêu cầu truyền tải lại gói tin bị lỗi ráp chúng lại trước gởi gói tin cho ứng dụng Mỗi gói tin có số sequence cho phiên làm việc TCP Một số ngẫu nhiên chọn cho gói tin gởi phiên làm việc Mỗi gói tin gởi tăng số sequence số byte liệu TCP gói tin trước Điều đảm bảo gói tin xác định luồng liệu TCP (17)Trang 17 tự giữ đệm đặt vào thứ tự gói tin khác nhận thành cơng Nếu gói tin bị mất, thiết bị nhận thấy số sequence bị lỗi gởi số acknowledgement thấp để u cầu gói tin bị lỗi Nếu khơng có cửa sổ trượt gói tin phải hồi báo lại, làm tăng băng thông độ trễ mạng I.3.3 Quá trình thành lập phiên làm việc TCP Quá trình làm thành lập phiên làm việc TCP thực nhờ vào việc sử dụng cờ (Flag): Flag Mô Tả 6 bit dành riêng Dành riêng để sử dụng tương lai, giá trị luôn zero 1-bit URG flag Đánh dấu gói tin liệu khẩn cấp 1-bit ACK flag Hồi báo nhận gói tin 1-bit PUSH flag Cho biết liệu đẩy vào ứng dụng 1-bit RESET flag Thiết lập lại tình trạng khởi đầu kết nối TCP 1-bit SYN flag Bắt đầu phiên làm việc 1-bit FIN flag Kết thúc phiên làm việc TCP sử dụng tình trạng kết nối để định tình trạng kết nối thiết bị Một giao thức bắt tay đặc biệt dùng để thành lập kết nối theo dõi tình trạng kết nối suốt phiên làm việc Một phiên làm việc TCP gồm ba pha sau: • Mở bắt tay • Duy trì phiên làm việc • Đóng bắt tay Mỗi pha yêu cầu bit cờ thiết lập thứ tự Quá trình mở bắt tay thường gọi ba bắt tay yêu cầu ba bước để thành lập kết nối • Thiết bị gởi gởi cờ SYN cho biết bắt đầu phiên làm việc (18)Trang 18 • Thiết bị gởi gởi cờ ACK cho biết phiên làm việc mở sẵng sàng cho việc gởi nhận gói tin Sau phiên làm việc thành lập, cờ ACK thiết lập gói tin Để đóng phiên làm việc, q trình bắt tay khác thực dùng cờ FIN: • Thiết bị khởi đầu đóng kết nối gởi cờ FIN • Thiết bị bên gởi cờ FIN ACK gói tin cho biết chấp nhận đóng kết nối • Thiết bị khởi đầu đóng kết nối gởi cờ ACK để đóng kết nối Hình I.6: Các bước bắt tay giao thức TCP I.4 Tầng UDP (19)Trang 19 Hình I.7: UDP Header UDP header gồm trường sau: • Source Port • Destination Port • Message Length • Checksum • Next Level Protocol Cũng giống TCP, UDP theo dõi kết nối cách sử dụng port từ 1024->65536, port UDP từ 0->1023 port dành riêng cho ứng dụng phổ biến, số dùng phổ biến như: Port Mô Tả 53 Domain Name System 69 Trivial File Transfer Protocol 111 Remote Procedure Call 137 NetBIOS name service (20)Trang 20 Port Mô Tả (21)Trang 21 CHƯƠNG II: LẬP TRÌNH SOCKET HƯỚNG KẾT NỐI II.1 Socket Trong lập trình mạng dùng Socket, không trực tiếp truy cập vào thiết bị mạng để gởi nhận liệu Thay vậy, file mô tả trung gian tạo để điều khiển việc lập trình Các file mơ tả dùng để tham chiếu đến kết nối mạng gọi Socket Socket định nghĩa đặc trưng sau: • Một kết nối mạng hay đường ống dẫn để truyền tải liệu • Một kiểu truyền thơng stream hay datagram • Một giao thức TCP hay UDP Sau Socket tạo phải gắn vào địa mạng port hệ thống cục hay xa Một Socket gắn vào địa mạng port, dùng để gởi nhận liệu 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 sau: Socket (AddressFamily, SocketType, ProtocolType) Phương thức tạo lập lớp Socket cần đối số truyền vào sau: +AddressFamily: họ địa dùng, tham số có giá trị sau: AppleTalk Địa AppleTalk Atm Native ATM services address Banyan Địa Banyan Ccitt Địa cho giao thức CCITT, X25 Chaos Địa cho giao thức MIT CHAOS Cluster Địa cho sản phẩm cluster Microsoft DataKit Địa cho giao thức Datakit DataLink Địa giao thức tầng data-link (22)Trang 22 Ecma Địa ECMA (European Computer Manufacturers Association) FireFox Địa FireFox HyperChannel Địa NSC Hyperchannel Ieee12844 Địa workgroup IEEE 1284.4 ImpLink Địa ARPANET IMP InterNetwork Địa IP version InterNetworkV6 Địa IP version Ipx Địa IPX SPX Irda Địa IrDA Iso Địa cho giao thức ISO Lat Địa LAT Max Địa MAX NetBios Địa NetBios NetworkDesigners Địa Network Designers NS Địa Xerox NS Osi Địa cho giao thức ISO Pup Địa cho giao thức PUP Sna Địa IBM SNA Unix Địa Unix Unknown Chưa biết họ địa Unspecified Chưa họ địa VoiceView Địa VoiceView +SocketType: kiểu Socket, tham số có giao thức sau: Kiểu Mô tả (23)Trang 23 Dgram sử dụng giao thức UDP họ địa InterNetwork Raw Được sử giao thức cấp thấp Internet Control Message Protocol (Icmp) Internet Group Management Protocol (Igmp) Ứng dụng phải cung cấp IP header gởi Khi nhận nhận IP header tùy chọn tương ứng Rdm Được sử dụng giao thức phi kết nối, hướng thông điệp, truyền thông điệp tin cậy, biên thông điệp bảo vệ Rdm (Reliably Delivered Messages) thông điệp đến không bị trùng lặp thứ tự Hơn nữa, thiết bị nhận thiết bị thông điệp bị mất Nếu khởi tạo Socket dùng Rdm, ta không cần yêu cầu kết nối tới host xa trước gởi nhận liệu Seqpacket Cung cấp hướng kết nối truyền chiều dịng byte cách tin cậy Seqpacket khơng trùng lập liệu bảo vệ biên liệu Socket kiểu Seqpacket truyền thông với máy đơn yêu cầu kết nối trước truyền liệu Stream Được sử dụng giao thức hướng kết nối, không bị trùng lặp dữ liệu, không bảo vệ biên liệu Socket kiểu Stream truyền thông với máy đơn yêu cầu kết nối trước truyền liệu Stream dùng giao thức Transmission Control Protocol (Tcp) họ địa InterNetwork Unknown Chưa biết kiểu Socket +ProtocolType: kiểu giao thức, tham số có giá trị sau: ProtocolType Mô tả Ggp Gateway To Gateway Protocol Icmp Internet Control Message Protocol IcmpV6 Internet Control Message Protocol IPv6 Idp Internet Datagram Protocol Igmp Internet Group Management Protocol IP Internet Protocol IPSecAuthenticationHeader IPv6 Authentication (24)Trang 24 IPv4 Internet Protocol version IPv6 Internet Protocol version (IPv6) Ipx Internet Packet Exchange Protocol ND Net Disk Protocol (unofficial) Pup PARC Universal Packet Protocol Raw Raw IP packet protocol Spx Sequenced Packet Exchange protocol SpxII Sequenced Packet Exchange version protocol Tcp Transmission Control Protocol Udp User Datagram Protocol Unknown Giao thức chưa biết Unspecified Giao thức chưa Ví dụ phương thức tạo lập lớp Socket: Socket sk = Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); II.2 IPAddress IPAddress đối tượng dùng để mô tả địa IP, đối tượng sử dụng nhiều phương thức Socket Một số phương thức lớp IPAddress Phương Thức Mô Tả Equals So sánh địa IP GetHashCode Lấy giá trị has cho đối tượng IPAddress GetType Trả kiểu thể địa IP HostToNetworkOrder Chuyển địa IP từ host byte order thành network byte order IsLoopBack Cho biết địa IP có phải địa LoopBack hay không (25)Trang 25 Phương Thức Mô Tả Parse Chuyển chuỗi thành thể IPAddress ToString Chuyển đối tượng IPAddress thành chuỗi Phương thức Parse() thường dùng để tạo thể IPAddress: IPAddress localIpAddress = IPAddress.Parse("127.0.0.1"); Lớp IPAddress cung cấp thuộc tính để mơ tả địa IP đặc biệt: • Any: dùng để mơ tả địa IP hệ thống • Broadcast: dùng để mơ tả địa IP Broadcast cho mạng cục • Loopback: dùng để mơ tả địa loopback hệ thống • None: không dùng địa IP II.3 IPEndPoint IPEndPoint đối tượng mô tả kết hợp địa IP port Đối tượng IPEndPoint dùng để gắn kết Socket với địa cục địa xa Hai thuộc tính IPEndPoint dùng để lấy vùng port hệ thống MinPort MaxPort II.4 Lập trình Socket hướng kết nối Trong lập trình Socket hướng kết nối, giao thức TCP dùng để thành lập phiên làm việc 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 kết nối thành lập liệu truyền thiết bị cách tin tưởng (26)Trang 26 Hình II.1: Mơ hình lập trình Socket hướng kết nối II.4.1 Lập trình phía Server Đầu tiên Server tạo Socket, Socket gắn vào địa ip port cục bộ, hàm để thực việc hàm Bind() Hàm cần danh đối số IPEndPoint cục bộ: IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); server.Bind(ipep); Bởi Server thường chấp nhận kết nối địa IP port riêng nên ta dùng IPAddress.Any để chấp nhận kết nối card mạng Địa IP ta dùng địa IP version kiểu giao thức TCP nên AddressFamily InterNetwork SocketType Stream Sau Socket gắn kết vào địa port, Server phải sẵn sàng chấp nhận kết nối từ Client Việc thực nhờ vào hàm Listen().Hàm Listen() có đối số, số Client tối đa mà lắng nghe server.Listen(10); (27)Trang 27 Hàm Accept() dừng Server lại chờ có Client kết nối đến trả Socket khác, Socket dùng để trao đổi liệu với Client Khi chấp nhận kết nối với Client Server gởi nhận liệu với Client thông qua phương thức Send() Receive() string welcome = "Hello Client"; buff = Encoding.ASCII.GetBytes(welcome); client.Send(buff, buff.Length, SocketFlags.None); Phương thức Send() Socket dùng để gởi liệu, phương thức có số đối số quan trọng sau: ✓ Buff : mảng byte cần gởi ✓ Offset: vị trí mảng cần gởi ✓ Size: số byte cần gởi ✓ SocketFlags: cách gởi liệu Socket Việc gởi nhận liệu thực liên tục thơng qua vịng lặp vơ hạn: while (true) { buff = new byte[1024]; recv = client.Receive(buff); if (recv == 0) break; Console.WriteLine(Encoding.ASCII.GetString(buff, 0, recv)); client.Send(buff, recv, SocketFlags.None); } Phương thức Receive() đặt liệu vào buffer, kích thước buffer thiết lập lại, buffer khơng thiết lập lại, lần gọi phương thức Receive() nhận liệu tối đa lần nhận liệu trước Phương thức có số đối số quan trọng sau: ✓ Buff : mảng byte cần gởi ✓ Offset: vị trí mảng cần nhận ✓ Size: số byte cần gởi (28)Trang 28 Phương thức Receive() trả số byte liệu nhận từ Client Nếu khơng có liệu nhận, phương thức Receive() bị dừng lại chờ có 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 gói TCP), phương thức Receive() trả giá trị Khi phương thức Receive() trả giá trị 0, ta đóng Socket Client lại phương thức Close() Socket (Server Socket) cịn hoạt động để chấp nhận kết nối khác Nếu không muốn Client kết nối đến ta đóng Server lại ln: client.Close(); server.Close(); Chương trình TCP Server đơn giản: using System; using System.Net; using System.Net.Sockets; using System.Text; class TcpServerDonGian { public static void Main() { //Số byte thực nhận dùng hàm Receive() int byteReceive; //buffer để nhận gởi liệu byte[] buff = new byte[1024]; //EndPoint cục 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 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 "); //Hàm Accept() block server lại 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); (29)Trang 29 //Chuyển chuỗi thành mảng 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 nhận byteReceive = client.Receive(buff); //Nếu Client ngắt kết nối khỏi vịng lặp if (byteReceive == 0) break; Console.WriteLine(Encoding.ASCII.GetString(buff, 0, byteReceive)); //Sau nhận liệu xong, gởi lại cho Client client.Send(buff, byteReceive, SocketFlags.None); } Console.WriteLine("Da dong ket noi voi Client: {0}", clientep.Address); //Đóng kết nối client.Close(); server.Close(); } } Để kiểm tra thử chương trình ta dùng chương trình Telnet Windows để kiểm tra Dùng lệnh telnet 127.0.0.1 5000 (30)Trang 30 Sau dùng lệnh telnet, kết trả hình kết nối thành cơng II.4.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 phía Server Client phải gắn kết địa Socket tạo sử dụng phương thức Connect() không sử dụng phương thức Bind() giống phía Server Phương thức Connect() yêu cầu IPEndPoint 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() dừng lại Client kết nối với Server Nếu kết nối thực phát sinh biệt lệ, hàm Connect() tra phải để khối try, catch để khơng bị lỗi chương trình Khi kết nối thành lập, Client dùng phương thức Send() Receive() lớp Socket để gởi nhận liệu tương tự Server làm Khi trình trao đổi liệu hồn tất, đối tượng Socket phải đóng lại Client Socket dùng phương thức Shutdown() để dừng Socket dùng phương thức Close() để thực đóng phiên làm việc Phương thức Shutdown() Socket dùng tham số để định cách Socket dừng lại Các phương thức là: (31)Trang 31 Giá trị Mô tả SocketShutdown.Both Ngăn cản gởi nhận liệu Socket SocketShutdown.Receive Ngăn cản nhận liệu Socket Cờ RST gởi có thêm liệu nhận SocketShutdown.Send Ngăn cản gởi liệu Socket Cờ FIN gởi sau tất liệu lại buffer gởi Chương trình TCP Client đơn giản: using System; using System.Net; using System.Net.Sockets; using System.Text; class SimpleTcpClient { public static void Main() { //Buffer để gởi nhận liệu byte[] buff = new byte[1024]; //Chuỗi nhập vào chuỗi nhận 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); //Hàm Connect() bị block lại chờ kết nối với server hết block try { server.Connect(ipep); } //Quá trình kết nối xảy 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 nhận int byteReceive = server.Receive(buff); //Chuỗi nhận (32)Trang 32 Console.WriteLine(stringData); while (true) { //Nhập liệu từ bàn phím input = Console.ReadLine(); //Nếu nhập exit đóng Socket if (input == "exit") break; //Gởi liệu cho server server.Send(Encoding.ASCII.GetBytes(input)); //Reset lại buffer buff = new byte[1024]; //Số byte thực nhận byteReceive = server.Receive(buff); //Chuỗi nhận 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 gởi liệu server.Shutdown(SocketShutdown.Both); //Đóng Socket server.Close(); } } II.4.3 Vấn đề với đệm liệu Trong ví dụ Client, Server đơn giản mảng byte dùng đệm để gởi nhận liệu Socket Bởi chương trình chạy môi trường điều khiển, tất thơng điệp thuộc dạng text kích thước nhỏ nên loại buffer vấn đề Trong giới thực, kích thước kiểu liệu đến truyền thông Client Server Vấn đề xảy khi liệu đến lớn kích thước đệm liệu (33)Trang 33 • Kích thước đệm liệu phương thức Receive() • Kích thước đệm tham số phương thức Receive() Trong ví dụ đơn giản trên, buffer định nghĩa mảng byte kích thước 1024 Bởi kích thước liệu không phương thức Receive() nên kích thước đệm tự động lấy kích thước mặc định đệm liệu 1024 byte Phương thức Receive() đọc 1024 byte liệu lần đặt liệu đọc vào biến buff byteReceive = client.Receive(buff); Vào lúc phương thức Receive() gọi, đệm TCP chứa 1024 byte, phương thức trả số lượng liệu mà thực đọc biến byte Receive Để chuyển liệu thành chuỗi, ta dùng phương thức GetString() sau: stringData = Encoding.ASCII.GetString(buff, 0, byteReceive); Trong đối số hàm GetString, ta phải truyền vào số byte thực đọc không ta nhận chuỗi với byte thừa đằng sau II.4.4 Xử lý với đệm có kích thước nhỏ Hệ điều hành Window dùng đệm TCP để gởi nhận liệu Điều cầ thiết để TCP gởi lại liệu lúc cần thiết Một liệu hồi báo nhận thành cơng xóa khỏi đệm (34)Trang 34 Dữ liệu đến hoạt động theo cách tương tự Nó lại đệm phương thức Receive() dùng để đọc Nếu phương thức Receive() khơng đọc tồn liệu đệm, phần cịn lại nằm chờ phương thức Receive() đọc Dữ liệu không bị không lấy đoạn liệu mong muốn Để thấy vấn đề, ta tiến hành thay đổi kích thước đệm từ 1024 byte xuống cịn 10 byte Và chạy lại chương trình Client, Server đơn giản Hình II.4: Kết trả chạy chương trình với buffer nhỏ Bởi đệm liệu không đủ lớn để lấy hết liệu đệm TCP nên phương thức Receive() lấy lượng liệu có độ lớn độ lớn đệm liệu, phần cịn lại nằm đệm TCP lấy gọi lại phương thức Receive() Do câu chào Client Server phải dùng tới hai lần gọi phương thức Receive() lấy hết Trong lần gởi nhận liệu kế tiếp, đoạn liệu đọc từ đệm TCP ta gởi liệu với kích thước lớn 10 byte nhận ta nhận 10 byte (35)Trang 35 II.4.5 Vấn đề với thông điệp TCP Một khó khăn nhà lập trình mạng sử dụng giao thức TCP để chuyển liệu giao thức không quan tâm đến biên liệu Hình II.5: Client Send hai lần Server Receive Như hình vấn đề xảy truyền liệu không đảm bảo phương thức Send() không đọc phương thức Receive() Tất liệu đọc từ phương thức Receive() không thực đọc trực tiếp từ mạng mà đọc từ đệm TCP Khi gói tin TCP nhận từ mạng đặt theo thứ tự đệm TCP Mỗi phương thức Receive() gọi, đọc liệu đệm TCP, không quan tâm đến biên liệu Chúng ta xem xét ví dụ sau, Chương Trình BadTCPServer: using System; using System.Net; using System.Net.Sockets; using System.Text; class BadTcpServer { public static void Main() { //Số byte thực nhận dùng hàm Receive() int byteReceive; //buffer để nhận gởi liệu byte[] buff = new byte[1024]; //EndPoint cục (36)Trang 36 Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Kết nối server với 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 "); //Hàm Accept() block server lại 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 byte buff = Encoding.ASCII.GetBytes(welcome); //Gởi câu chào cho Client client.Send(buff, buff.Length, SocketFlags.None); for (int i = 0; i < 5; i++) { byteReceive = client.Receive(buff); Console.WriteLine(Encoding.ASCII.GetString(buff, 0, byteReceive)); } Console.WriteLine("Da dong ket noi voi Client: {0}", clientep.Address); //Đóng kết nối client.Close(); server.Close(); Console.Read(); } } Chương trình Server thành lập Socket TCP bình thường để lắng nghe kết nối, kết nối thành lập, Server gởi câu chào cho Client cố gắng nhận năm thông điệp riêng biệt từ Client: for (int i = 0; i < 5; i++) { byteReceive = client.Receive(data); (37)Trang 37 } Mỗi gọi, phương thức Receive() đọc toàn liệu đệm TCP, sau nhận năm thơng điệp, kết nối đóng lại Chương trình BadTCPClient: using System; using System.Net; using System.Net.Sockets; using System.Text; class BadTcpClient { public static void Main() { //Buffer để gởi nhận liệu byte[] buff = new byte[10]; //Chuỗi nhập vào chuỗi nhận 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); //Hàm Connect() bị block lại chờ kết nối với server hết block try { server.Connect(ipep); } //Q trình kết nối xảy lỗi nên phải dùng try, catch catch (SocketException e) { Console.WriteLine("Khon the ket noi den Server"); Console.WriteLine(e.ToString()); return; } //Số byte thực nhận int byteReceive = server.Receive(buff); //Chuỗi nhận stringData = Encoding.ASCII.GetString(buff, 0, byteReceive); Console.WriteLine(stringData); (38)Trang 38 server.Send(Encoding.ASCII.GetBytes("Thong diep 3")); server.Send(Encoding.ASCII.GetBytes("Thong diep 4")); server.Send(Encoding.ASCII.GetBytes("Thong diep 5")); Console.WriteLine("Dong ket noi voi server "); //Dừng kết nối, không cho phép nhận gởi liệu server.Shutdown(SocketShutdown.Both); //Đóng Socket server.Close(); Console.Read(); } } Kết chương trình hình bên Hình II.6: Kết Server (39)Trang 39 II.4.6 Giải vấn đề với thông điệp TCP Để giải vấn đề với biên liệu không bảo vệ, phải tìm hiểu số kỹ thuật để phân biệt thông điệp Ba kỹ thuật thông thường dùng để phân biệt thông điệp gởi thông qua TCP: ✓ Luôn sử dụng thông điệp với kích thước cố định ✓ Gởi kèm kích thước thơng điệp với thông điệp ✓ Sử dụng hệ thống đánh dấu để phân biệt thông điệp II.4.6.1 Sử dụng thơng điệp với kích thước cố định Cách dễ cách tốn chi phí để giải vấn đề với thông điệp TCP tạo giao thức ln ln truyền thơng điệp với kích thước cố định Bằng cách thiết lập tất thông điệp có kích thước, chương trình TCP nhận biết tồn thơng điệp gởi từ Client Khi gởi liệu với kích thước cố định, phải đảm bảo tồn thơng điệp gởi từ phương thức Send() Phụ thuộc vào kích thước đệm TCP liệu truyền, phương thức Send() trả số byte mà thực gởi đến đệm TCP Nếu phương thức Send() chưa gởi hết liệu phải gởi lại phần liệu lại Việc thường thực cách sử dụng vòng lặp while() vòng lặp ta kiểm tra số byte thực gởi với kích thước cố định private static int SendData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } (40)Trang 40 Cũng giống việc gởi liệu, phải luôn đảm bảo nhận tất liệu phương thức Receive() Bằng cách dùng vòng lặp gọi phương thức Receive() nhận toàn liệu mong muốn private static byte[] ReceiveData(Socket s, int size) { int total = 0; int dataleft = size; byte[] data = new byte[size]; int recv; while (total < size) { recv = s.Receive(data, total, dataleft, 0); if (recv == 0) { data = Encoding.ASCII.GetBytes("exit"); break; } total += recv; dataleft -= recv; } return data; Phương thức ReceiveData() đọc liệu với kích thước đọc đối số truyền vào, phương thức Receive() trả số byte thực sụ đọc được, số byte thực đọc mà nhỏ số byte truyền vào phương thức ReceiveData() vịng lặp tiếp tục số byte đọc kích thước u cầu Chương trình Server gởi nhận liệu với kích thước cố định using System; using System.Net; using System.Net.Sockets; using System.Text; class FixedTcpSrvr { private static int SendData(Socket s, byte[] data) { int total = 0; (41)Trang 41 int dataleft = size; int sent; while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } private static byte[] ReceiveData(Socket s, int size) { int total = 0; int dataleft = size; byte[] data = new byte[size]; int recv; while (total < size) { recv = s.Receive(data, total, dataleft, 0); if (recv == 0) { data = Encoding.ASCII.GetBytes("exit"); break; } total += recv; dataleft -= recv; } return data; } public static void Main() { byte[] data = new byte[1024]; IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsock.Bind(ipep); newsock.Listen(10); Console.WriteLine("Dang cho Client ket noi den "); Socket client = newsock.Accept(); IPEndPoint newclient = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine("Da ket noi voi Client {0} tai port {1}", newclient.Address, newclient.Port); (42)Trang 42 data = Encoding.ASCII.GetBytes(welcome); int sent = SendData(client, data); for (int i = 0; i < 5; i++) { data = ReceiveData(client, 12); Console.WriteLine(Encoding.ASCII.GetString(data)); } Console.WriteLine("Da ngat ket noi voi Client {0}", newclient.Address); client.Close(); newsock.Close(); } } Chương trình Client gởi nhận liệu với kích thước cố định using System; using System.Net; using System.Net.Sockets; using System.Text; class FixedTcpClient { private static int SendData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } private static byte[] ReceiveData(Socket s, int size) { int total = 0; int dataleft = size; byte[] data = new byte[size]; int recv; (43)Trang 43 recv = s.Receive(data, total, dataleft, 0); if (recv == 0) { data = Encoding.ASCII.GetBytes("exit "); break; } total += recv; dataleft -= recv; } return data; } public static void Main() { byte[] data = new byte[1024]; int sent; 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("Khong the ket noi den server"); Console.WriteLine(e.ToString()); return; } int recv = server.Receive(data); string stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); sent = SendData(server, Encoding.ASCII.GetBytes("Thong diep 1")); sent = SendData(server, Encoding.ASCII.GetBytes("Thong diep 2")); sent = SendData(server, Encoding.ASCII.GetBytes("Thong diep 3")); sent = SendData(server, Encoding.ASCII.GetBytes("Thong diep 4")); sent = SendData(server, Encoding.ASCII.GetBytes("Thong diep 5")); Console.WriteLine("Dong ket noi voi server "); server.Shutdown(SocketShutdown.Both); server.Close(); } } (44)Trang 44 Hình II.7: Kết gởi nhận liệu với kích thước cố định II.4.6.2 Gởi kèm kích thước thơng điệp với thơng điệp Cách giải vấn đề biên thông điệp TCP cách sử dụng thơng điệp với kích thước cố định giải pháp lãng phí tất thơng điệp phải kích thước Nếu thơng điệp chưa đủ kích thước phải thêm phần đệm vào, gây lãng phí băng thông mạng Một giải pháp cho vấn đề cho phép thơng điệp gởi với kích thước khác gởi kích thước thơng điệp kèm với thông điệp Bằng cách thiết bị nhận biết kích thước thơng điệp Để thực việc ta sửa đổi phương thức SendData() ví dụ trước private static int SendVarData(Socket s, byte[] buff) { int total = 0; int size = buff.Length; int dataleft = size; int sent; byte[] datasize = new byte[4]; datasize = BitConverter.GetBytes(size); sent = s.Send(datasize); while (total < size) { (45)Trang 45 total += sent; dataleft -= sent; } return total; } Trong phương thức SendVarData(), ta lấy kích thước thơng điệp gắn vào đầu thơng điệp, kích thước số interger byte Kích thước tối đa thông điệp 65KB Giá trị interger byte chuyển thành mảng byte, hàm GetBytes() lớp BitConverter dùng để thực việc Mảng kích thước sau gởi đến thiết bị xa, sau gởi kích thước thơng điệp xong, phần thơng điệp gởi đi, kỹ thuật gởi giống ví dụ trước, lặp tất byte gởi Bước tạo phương thức nhận byte kích thước thơng điệp tồn thơng điệp phương thức ReceiveData() ví dụ trước sửa đổi để thực việc private static byte[] ReceiveVarData(Socket s) { int total = 0; int recv; byte[] datasize = new byte[4]; recv = s.Receive(datasize, 0, 4, 0); int size = BitConverter.ToInt32(datasize, 0); int dataleft = size; byte[] data = new byte[size]; while (total < size) { recv = s.Receive(data, total, dataleft, 0); if (recv == 0) { data = Encoding.ASCII.GetBytes("exit "); break; } (46)Trang 46 return data; } Hàm ReceiveVarData() nhận byte thông điệp chuyển thành giá trị interger phương thức GetInt32() lớp BitConverter Chương trình Server gởi nhận thơng điệp với kích thước using System; using System.Net; using System.Net.Sockets; using System.Text; class VarTcpSrvr { private static int SendVarData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; byte[] datasize = new byte[4]; datasize = BitConverter.GetBytes(size); sent = s.Send(datasize); while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } private static byte[] ReceiveVarData(Socket s) { int total = 0; int recv; byte[] datasize = new byte[4]; recv = s.Receive(datasize, 0, 4, 0); int size = BitConverter.ToInt32(datasize, 0); int dataleft = size; byte[] data = new byte[size]; while (total < size) { recv = s.Receive(data, total, dataleft, 0); if (recv == 0) (47)Trang 47 data = Encoding.ASCII.GetBytes("exit "); break; } total += recv; dataleft -= recv; } return data; } public static void Main() { byte[] data = new byte[1024]; IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsock.Bind(ipep); newsock.Listen(10); Console.WriteLine("Dang cho Client ket noi den "); Socket client = newsock.Accept(); IPEndPoint newclient = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine("Da ket noi voi client {0} tai port {1}", newclient.Address, newclient.Port); string welcome = "Hello client"; data = Encoding.ASCII.GetBytes(welcome); int sent = SendVarData(client, data); for (int i = 0; i < 5; i++) { data = ReceiveVarData(client); Console.WriteLine(Encoding.ASCII.GetString(data)); } Console.WriteLine("Dong ket noi voi Client {0}", newclient.Address); client.Close(); newsock.Close(); } } Chương trình Client gởi nhận thơng điệp với kích thước using System; using System.Net; using System.Net.Sockets; using System.Text; class VarTcpClient (48)Trang 48 private static int SendVarData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; byte[] datasize = new byte[4]; datasize = BitConverter.GetBytes(size); sent = s.Send(datasize); while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } private static byte[] ReceiveVarData(Socket s) { int total = 0; int recv; byte[] datasize = new byte[4]; recv = s.Receive(datasize, 0, 4, 0); int size = BitConverter.ToInt32(datasize, 0); int dataleft = size; byte[] data = new byte[size]; while (total < size) { recv = s.Receive(data, total, dataleft, 0); if (recv == 0) { data = Encoding.ASCII.GetBytes("exit "); break; } total += recv; dataleft -= recv; } return data; } public static void Main() { (49)Trang 49 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("Khong the ket noi voi server"); Console.WriteLine(e.ToString()); return; } data = ReceiveVarData(server); string stringData = Encoding.ASCII.GetString(data); Console.WriteLine(stringData); string message1 = "Day la thong diep dau tien"; string message2 = "Thong diep ngan"; string message3 = "Thong diep dai hon cac thong diep khac"; string message4 = "a"; string message5 = "Thong diep cuoi cung"; sent = SendVarData(server, Encoding.ASCII.GetBytes(message1)); sent = SendVarData(server, Encoding.ASCII.GetBytes(message2)); sent = SendVarData(server, Encoding.ASCII.GetBytes(message3)); sent = SendVarData(server, Encoding.ASCII.GetBytes(message4)); sent = SendVarData(server, Encoding.ASCII.GetBytes(message5)); Console.WriteLine("Dang ngat ket noi voi server "); server.Shutdown(SocketShutdown.Both); server.Close(); } } (50)Trang 50 Hình II.8: Kết gởi thơng điệp với kích thước II.4.6.3 Sử dụng hệ thống đánh dấu để phân biệt thông điệp Một cách khác để gởi thơng điệp với kích thước khác sử dụng hệ thống đánh dấu Hệ thống chia thông điệp ký tự phân cách để báo hiệu kết thúc thông điệp Khi liệu nhận từ Socket, liệu kiểm tra ký tự để phát ký tự phân cách, ký tự phân cách phát liệu trước ký tự phân cách thông điệp liệu sau ký tự phân cách bắt đầu thông điệp Phương pháp có số hạn chế, thơng điệp lớn làm giảm tốc độ hệ thống tồn ký tự thơng điệp phải kiểm tra Cũng có trường hợp số ký tự thông điệp trùng với ký tự phân cách thông điệp bị tách thành thông điệp con, điều làm cho chương trình chạy bị sai lệch II.4.7 Sử dụng C# Stream với TCP Điều khiển thơng điệp dùng giao thức TCP thường gây khó khăn cho lập trình viên nên NET Framework cung cấp số lớp để giảm gánh nặng lập trình Một lớp NetworkStream, hai lớp dùng để gởi nhận text sử dụng giao thức TCP StreamWriter StreamReader (51)Trang 51 Lớp NetworkStream nằm namespace System.Net.Socket, lớp có nhiều phương thức tạo lập để tạo thể lớp NetworkStream phương thức tạo lập sau hay dùng nhất: Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); NetworkStream ns = new NetworkStream(server); Một số thuộc tính lớp NetworkStream: Thuộc Tính Mô Tả CanRead true NetworkStream hỗ trợ đọc CanSeek Luôn false CanWrite true NetworkStream hỗ trợ ghi DataAvailable true có liệu để đọc Một số phương thức lớp NetworkStream: Phương Thức Mô Tả BeginRead() Bắt đầu đọc NetworkStream bất đồng BeginWrite() Bắt đầu ghi NetworkStream bất đồng Close() Đóng đối tượng NetworkStream CreateObjRef() Tạo đối tượng dùng proxy cho NetworkStream EndRead() Kêt thúc đọc NetworkStream bất đồng EndWrite() Kêt thúc ghi NetworkStream bất đồng Equals() So sánh hai đối tượng NetworkStreams Flush() Đẩy tất liệu từ NetworkStream GetHashCode() Lấy hash code cho NetworkStream GetLifetimeService() Lấy đối tượng lifetime service cho NetworkStream GetType() Lấy kiểu NetworkStream InitializeLifetimeService() Lấy đối tượng lifetime service object để điều khiển sách lifetime choNetworkStream Read() Đọc liệu từ NetworkStream (52)Trang 52 Phương thức Read() dùng để đọc khối liệu từ NetworkStream Định dạng phương thức này: int Read(byte[] buffer, int offset, int size) Trong đó: ✓ buffer: mảng byte đọc vào ✓ offset: vị trí bắt đầu để đọc vào đệm ✓ size: số byte tối đa đọc Phương thức trả giá trị interger mô tả số byte thực đọc từ NetworkStream dặt liệu đọc vào buffer Phương thức Write() dùng để gởi khối liệu có định dạng tương tự: void Write(byte[] buffer, int offset, int size) Trong đó: ✓ buffer: mảng byte để ghi ✓ offset: vị trí bắt đầu để ghi đệm ✓ size: số byte tối đa ghi bắt đầu vị trí offset Chương trình TCP Client NetworkStream using System; using System.Net; using System.Net.Sockets; using System.Text; class NetworkStreamTcpClient { public static void Main() { byte[] data = new byte[1024]; string input, stringData; int recv; IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 500); Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { server.Connect(ipep); } (53)Trang 53 Console.WriteLine("Khong the ket noi den server"); Console.WriteLine(e.ToString()); return; } NetworkStream ns = new NetworkStream(server); if (ns.CanRead) { recv = ns.Read(data, 0, data.Length); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } else { Console.WriteLine("Error: Can't read from this Socket"); ns.Close(); server.Close(); return; } while (true) { input = Console.ReadLine(); if (input == "exit") break; if (ns.CanWrite) { ns.Write(Encoding.ASCII.GetBytes(input), 0, input.Length); ns.Flush(); } recv = ns.Read(data, 0, data.Length); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } Console.WriteLine("Dang ngat ket noi voi server "); ns.Close(); server.Shutdown(SocketShutdown.Both); server.Close(); } } Chương trình tạo đối tượng NetworkStream từ đối tượng Socket: (54)Trang 54 Khi đối tượng NetworkStream tạo ra, đối tượng Socket không tham chiếu đến bị đóng lại vào cuối chương trình, tất thông tin liên lạc với Server xa thực thông đối tượng NetworkStream: recv = ns.Read(data, 0, data.Length); ns.Write(Encoding.ASCII.GetBytes(input), 0, input.Length); ns.Flush(); Phương thức Flush() dùng sau phương thức Write() để đảm bảo liệu đặt vào NetworkStream gởi đến hệ thống xa Mặc dù đối tượng NetworkStream có thêm số chức Socket cịn tồn vấn đề với biên thơng điệp Vấn đề giải thông qua hai lớp hỗ trợ StreamReader StreamWriter Ta kiểm tra chương trình với chương trình TCP Server đơn giản II.4.7.2 Lớp StreamReader StreamWriter Namespcace System.IO chứa hai lớp StreamReader StreamWriter điều khiển việc đọc ghi thông điệp text từ mạng Cả hai lớp triển khai với đối tượng NetworkStream để xác định hệ thống đánh dấu cho thông điệp TCP Lớp StreamReader có nhiều phương thức tạo lập, phương thức tạo lập đơn giản lớp StreamReader: public StreamReader(Stream stream); Biến stream tham chiếu đến kiểu đối tượng Stream kể đối tượng NetworkStream Có nhiều phương thức thuộc tính dùng với đối tượng StreamReader sau tạo bảng sau: Phương Thức Mô Tả Close() Đóng đối tượng StreamReader CreateObjRef() Tạo đối tượng dùng proxy cho StreamReader DiscardBufferedData() Bỏ liệu StreamReader Equals() So sánh hai đối tượng StreamReader GetHashCode() Lấy hash code cho đối tượng StreamReader (55)Trang 55 Phương Thức Mô Tả GetType() Lấy kiểu đối tượng StreamReader InitializeLifetimeService() Tạo đối tượng lifetime service cho StreamReader Peek() Trả byte liệu hợp lệ từ mà không gỡ bỏ khỏi stream Read() Đọc nhiều byte liệu từ StreamReader ReadBlock() Đọc nhóm byte từ stream StreamReader đặt vào đệm ReadLine() Đọc liệu từ bắt đầu đối tượng StreamReader trở lên gặp ký tự xuống dòng ReadToEnd() Đọc liệu hết stream ToString() Tạo chuỗi mô tả đối tượng StreamReader Tương tự đối tượng StreamReader, đối tượng StreamWriter tạo từ đối tượng NetworkStream: public StreamWriter(Stream stream); StreamWriter có nhiều phương thức thuộc tính kết hợp với nó, số phương thức thuộc tính lớp StreamReader có đối tượng StreamWriter, ngồi cịn có số phương thức thuộc tính riêng: Phương Thức Mơ Tả Flush() Gởi tất liệu đệm StreamWriter stream Write() Gởi nhiều byte liệu stream WriteLine() Gởi liệu với ký tự xuống dòng stream Phương thức ReadLine() phương thức hay lớp StreamReader Nó đọc ký tự từ stream gặp ký tự xuống dịng Tính cho phép sử dụng ký tự xuống dòng ký tự phân tách thông điệp Phương thức WriteLine() lớp StreamWriter so khớp với phương thức ReadLine lớp StreamReader việc xử lý thơng điệp TCP trở nên dễ dàng Chương trình Stream TCP Server using System; using System.IO; using System.Net; using System.Net.Sockets; (56)Trang 56 class StreamTcpSrvr { public static void Main() { string data; IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsock.Bind(ipep); newsock.Listen(10); Console.WriteLine("Dang cho Client ket noi toi "); Socket client = newsock.Accept(); IPEndPoint newclient = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine("Da ket noi voi Client {0} tai port {1}", newclient.Address, newclient.Port); NetworkStream ns = new NetworkStream(client); StreamReader sr = new StreamReader(ns); StreamWriter sw = new StreamWriter(ns); string welcome = "Hello Client"; sw.WriteLine(welcome); sw.Flush(); while (true) { try { data = sr.ReadLine(); } catch (IOException) { break; } Console.WriteLine(data); sw.WriteLine(data); sw.Flush(); } Console.WriteLine("Da dong ket noi voi Client {0}", newclient.Address); sw.Close(); sr.Close(); ns.Close(); } (57)Trang 57 Chương trình StreamTcpSrvr dùng phương thức WriteLine() lớp StreamWriter để gởi thông điệp text kết thúc ký tự xuống dòng Đối với đối tượng NetworkStream, tốt hết ta phương thức Flush() sau gọi phương thức WriteLine() để đảm bảo tất liệu gởi từ đệm TCP Điểm khác biệt chương trình với chương trình TCP Server đơn giản cách chương trình StreamTcpSrvr biết Client ngắt kết nối Bởi phương thức ReadLine() hoạt động stream khơng phải Socket nên khơng thể trả giá trị Client ngắt kết nối Thay vậy, phương thức ReadLine() phát sinh biệt lệ Client ngắt kết nối ta phải dùng catch để bắt biệt lệ xử lý Client ngắt kết nối: try { data = sr.ReadLine(); } catch (IOException) { break; } Chương trình Stream TCP Client using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; class StreamTcpClient { public static void Main() { string data; string input; 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); } (58)Trang 58 { Console.WriteLine("Khong the ket noi den server"); Console.WriteLine(e.ToString()); return; } NetworkStream ns = new NetworkStream(server); StreamReader sr = new StreamReader(ns); StreamWriter sw = new StreamWriter(ns); data = sr.ReadLine(); Console.WriteLine(data); while (true) { input = Console.ReadLine(); if (input == "exit") break; sw.WriteLine(input); sw.Flush(); data = sr.ReadLine(); Console.WriteLine(data); } Console.WriteLine("Dang dong ket noi voi server "); sr.Close(); sw.Close(); ns.Close(); server.Shutdown(SocketShutdown.Both); server.Close(); (59)Trang 59 CHƯƠNG III: LẬP TRÌNH SOCKET PHI KẾT NỐI III.1 Tổng quan Các Socket phi kết nối cho phép gởi 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 đọc tồn thơng điệp gởi phương thức gởi, điều làm tránh rắc rối, phức tạp với biên liệu Thật không may mắn giao thức phi kết nối UDP không đảm bảo liệu truyền tới đích Nhiều yếu tố mạng bận, mạng bị đứt chừng ngăn cản gói tin truyền tới đích Nếu thiết bị chờ liệu từ thiết bị xa, phải gán địa port cục bộ, dùng hàm Bind() để gán Một thực xong, thiết bị dùng Socket để gởi liệu hay nhận liệu từ Socket Bởi thiết bị Client không tạo kết nối đến địa Server cụ thể phương thức Connect() khơng cần dùng chương trình UDP Client Mơ hình bên mơ tả bước lập trình Socket phi kết nối: Hình V.1: Mơ hình lập trình Socket phi kết nối (60)Trang 60 Thay vào đó, Socket phi kết nối cung cấp hai phương thức để thực việc SendTo() ReceiveFrom() III.2 Lập trình phía Server UDP giao thức phi kết nối lập trình viên phải làm hai việc để tạo ứng dụng Server gởi nhận liệu: ✓ Tạo Socket ✓ Kết nối Socket đến IPEndPoint cục IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000); Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); newsock.Bind(ipep); Để thực truyền thông phi kết nối, phải SocketType Dgram ProtocolType Udp Sau thực xong hai bước trên, Socket dùng để chấp nhận gói tin UDP đến IPEndPoint gởi gói tin udp đến thiết bị nhận khác mạng Phương thức SendTo() dùng để gởi liệu, phương thức liệu để gởi IPEndPoint thiết bị nhận Có nhiều tải hàm phương thức SendTo() dùng tùy vào yêu cầu cụ thể SendTo(byte[] data, EndPoint Remote) Phương thức gởi mảng liệu đến EndPoint Remote Một tải hàm khác phức tạp phương thức SendTo() SendTo(byte[] data, SocketFlags Flags, EndPoint Remote) Phương thức cho phép thêm cờ SocketFlag, tùy chọn UDP sử dụng Để số byte gởi từ mảng byte ta sử dụng tải hàm sau phương thức SendTo(): SendTo(byte[] data, int Offset, int Size, SocketFlags Flags, EndPoint Remote) (61)Trang 61 ReceiveFrom(byte[] data, ref EndPoint Remote) Cũng thông thường, tham số thứ mảng byte định nghĩa để nhận liệu, tham số thứ hai phải truyền tham chiếu đối tượng EndPoint Tham chiếu tham chiếu đến vị trí nhớ nơi biến lưu trữ Phương thức ReceiveFrom() đặt thông tin EndPoint từ thiết bị xa vào vùng nhớ đối tượng EndPoint tham chiếu đến Bằng việc sử dụng đối số thứ hai tham chiếu ta lấy địa IP port máy xa Chương trình UDP đơn giản 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)); 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); (62)Trang 62 } Để chương trình UDP chấp nhận thơng điệp UDP đến, phải gắn với port hệ thống Việc thực cách tạo đối tượng IPEndPoint sử dụng địa IP cục thích hợp, trường hợp ta IPAddresss.Any để dùng địa IP máy cục để lắng nghe Sau gắn Socket vào IPEndPoint, Server chờ Client kết nối đến, Client kết nối đến, Client gởi thông điệp đến Server Server sau nhận thông điệp từ Client 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 bắt đầu nhận gởi thông điệp III.3 Lập trình phía Client Bởi Client khơng cần chờ port UDP định sắn nên chẳng cần dùng phương thức Bind(), thay lấy port ngẫu nhien hệ thống liệu gởi port để nhận liệu trả Chương trình UDP Client tương tự chương trình UDP Server: Chương trình UDP Client đơn giản 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); (63)Trang 63 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)); 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(); } } Chương trình UDP Client định nghĩa IPEndPoint mà UDP Server gởi gói tin: IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000); Chương trình Client gởi thông điệp đến Server chờ câu chào trả từ Server Bởi Client khơng cần chấp nhận thông điệp UDP port định trước nên Client khơng dùng phương thức Bind() Nó nhận thơng điệp UDP port mà gởi Chương trình SimpleUdpClient đọc liệu nhập vào từ bàn phím gởi đến chờ liệu từ Server gởi trả Khi Server gởi trả liệu về, Client lấy thơng điệp hiển thị lên hình Nếu người dùng nhận vào “exit” vịng lặp kết nối bị đóng lại (64)Trang 64 III.3.1 Sử dụng phương thức Connect() chương trình UDP Client Các phương thức UDP thiết kế phép lập trình viên gởi gói tin đến máy mạng lúc Bởi giao thức UDP khơng u cầu kết nối trước gởi liệu nên phải địa máy nhận phương thức SendTo() phương thức ReceiveFrom() Nếu chương trình cần gởi nhận liệu từ máy, dùng phương thức Connect() Sau UDP socket tạo ra, dùng phương thức Connect() giống chương trình TCP để udp Server xa Sau dùng phương thức Connect() xong ta dùng phương thức Send() Receive() để truyền tải liệu thiết bị với Kỹ thuật minh họa chương trình UDP Client sau: Chương trình udp Client dùng phương thức Connect() using System; using System.Net; using System.Net.Sockets; using System.Text; class OddUdpClient { public static void Main() { byte[] data = new byte[1024]; string input, stringData; IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050); Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); server.Connect(ipep); string welcome = "Xin chao server"; data = Encoding.ASCII.GetBytes(welcome); server.Send(data); data = new byte[1024]; int recv = server.Receive(data); Console.WriteLine("Nhan thong diep tu {0}:", ipep.ToString()); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); while (true) { input = Console.ReadLine(); if (input == "exit") (65)Trang 65 server.Send(Encoding.ASCII.GetBytes(input)); data = new byte[1024]; recv = server.Receive(data); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } Console.WriteLine("Dang dong client"); server.Close(); } } III.3.2 Phân biệt thông điệp UDP Một tính quan trọng UDP mà TCP khơng có khả xử lý thơng điệp mà không cần quan tâm đến biên thông điệp UDP bảo vệ biên thông điệp tất thông điệp gởi Mỗi lần gọi phương thức ReceiveFrom() đọc liệu gởi từ phương thức SendTo() Khi UDP Socket tạo ra, nhận thơng điệp từ Client Để udp Socket phân biệt Client gởi liệu bắt buộc thơng điệp phải chứa gói tin riêng đánh dấu thông tin IP thiết bị gởi Điều cho phép thiết bị nhận phân biệt thông điệp thiết bị gởi Chương trình Client Server sau minh họa điều Chương trình UDP Server using System; using System.Net; using System.Net.Sockets; using System.Text; class TestUdpSrvr { 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); (66)Trang 66 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 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)); (67)Trang 67 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(); } } Kết Server Hình V.2: UDP Server nhận biết thơng điệp riêng rẽ III.4 Ngăn cản liệu Một thuận lợi việc truyền thông dùng giao thức TCP giao thức TCP sử dụng đệm TCP Tất liệu gởi TCP Socket đặt vào đệm TCP trước gởi mạng Cũng giống vậy, tất liệu nhận từ Socket đặt vào đệm TCP trước đọc phương thức Receive() Khi phương thức Receive() cố gắng đọc liệu từ đệm, khơng đọc hết liệu phần cịn lại nằm đệm chờ lần gọi phương thức Receive() (68)Trang 68 Socket Nếu đệm nhỏ, liệu bị Để thấy điều này, ta tiến hành thay đổi kích thước đệm chương trình UDP đơn giản trên: Chương trình BadUDPClient using System; using System.Net; using System.Net.Sockets; using System.Text; class BadUdpClient { public static void Main() { byte[] data = new byte[30]; 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 = "Xin chao serveer"; 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[30]; 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)); while (true) { input = Console.ReadLine(); if (input == "exit") break; server.SendTo(Encoding.ASCII.GetBytes(input), tmpRemote); data = new byte[30]; recv = server.ReceiveFrom(data, ref tmpRemote); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } Console.WriteLine("Dang dong client"); server.Close(); (69)Trang 69 Ta test chương trình với chương trình UDP Server đơn giản Khi ta nhận liệu 10 byte chương trình chạy bình thường ta nhập liệu lớn 10 byte chương trình BadUdpClient phát sinh biệt lệ Mặc dầu ta lấy lại liệu bị ta hạn chế liệu cách đặt phương thức ReceiveFrom() khối try-catch, liệu bị kích thước đệm nhỏ, ta tăng kích thước đệm vào lần nhận liệu Chương trình BetterUdpClient sau minh họa việc này: Chương trình BetterUdpClient using System; using System.Net; using System.Net.Sockets; using System.Text; class BetterdUdpClient { public static void Main() { byte[] data = new byte[30]; 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 = "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[30]; 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)); int i = 30; while (true) { input = Console.ReadLine(); if (input == "exit") break; server.SendTo(Encoding.ASCII.GetBytes(input), tmpRemote); data = new byte[i]; (70)Trang 70 { recv = server.ReceiveFrom(data, ref tmpRemote); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } catch (SocketException) { Console.WriteLine("Canh bao: du lieu bi mat, hay thu lai"); i += 10; } } Console.WriteLine("Dang dong client"); server.Close(); } } Thay sử dụng mảng buffer với chiều dài cố định, chương trình BetterUdpClient dùng biết thiết lập giá trị khác lần phương thức ReceiveFrom() dùng data = new byte[i]; try { recv = server.ReceiveFrom(data, ref tmpRemote); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } catch (SocketException) { Console.WriteLine("Canh bao: du lieu bi mat, hay thu lai"); i += 10; } III.5 Ngăn cản gói tin (71)Trang 71 cho thiết bị gởi biết nhận thành cơng Nếu gói tin hồi báo khơng nhận khoảng thời gian thiết bị nhận cho gói tin bị gởi lại gói tin Có hai kỹ thuật dùng để truyền lại gói tin UDP: ✓ Sử dụng Socket bất đồng đối tượng Timer Kỹ thuật yêu cầu sử dụng Socket bất đồng mà lắng nghe gói tin đến khơng bị block Sau Socket thiết lập đọc bất đồng bộ, đối tượng Timer thiết lập, đối tượng Timer tắt trước hành động đọc bất đồng kết thúc việc gởi lại liệu diễn ✓ Sử dụng Socket đồng thiết lập giá trị Socket time-out Để làm việc này, ta dùng phương thức SetSocketOption() III.5.1 Sử dụng Soket Time-out Phương thức ReceiveFrom() phương thức bị block Nó block chương trình lại chương trình nhận liệu Nếu liệu khơng nhận, chương trình block mã Mặc địn phương thức ReceiveFrom() bị block mãi khơng có liệu đọc phương thức SetSocketOption() cung cấp nhiều tùy chọn cho Socket tạo, tùy chọn ReceiveTimeout Nó thiết lập khoảng thời gian Socket chờ liệu đến trước phát tín hiệu time-out Định dạng phương thức SetSocketOption() sau: SetSocketOption(SocketOptionLevel so, SocketOptionName sn, int value) SocketOptionLevel kiểu tùy chọn Socket để thực thi SocketOptionName định nghĩa tùy chọn thiết lập, tham số cuối thiết lập giá trị cho tùy chọn Để giá trị TimeOut ta dùng sau: server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000); Trong giá trị tham số cuối số miligiây tối đa hàm ReceiveFrom() chờ có liệu để đọc Chương trình sau minh họa cách dùng TimeOut: Chương trình TimeOutUdpClient using System; (72)Trang 72 using System.Net.Sockets; using System.Text; class TimeoutUdpClient { public static void Main() { byte[] data = new byte[1024]; string input, stringData; int recv; IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000); Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); int sockopt = (int)server.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout); Console.WriteLine("Gia tri timeout mac dinh: {0}", sockopt); server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000); sockopt = (int)server.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout); Console.WriteLine("Gia tri timeout moi: {0}", sockopt); 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]; recv = server.ReceiveFrom(data, ref tmpRemote); Console.WriteLine("Thong diep duoc nhan tu {0}:", mpRemote.ToString()); Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); while (true) { input = Console.ReadLine(); if (input == "exit") break; server.SendTo(Encoding.ASCII.GetBytes(input), tmpRemote); data = new byte[1024]; recv = server.ReceiveFrom(data, ref tmpRemote); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } Console.WriteLine("Dang dong client"); server.Close(); (73)Trang 73 } Chương trình TimeoutUdpClient lấy giá trị ReceiveTimeout ban đầu từ Socket hiển thị nó, sau thiết lập giá trị thành giây: int sockopt = (int)server.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout); Console.WriteLine("Gia tri timeout mac dinh: {0}", sockopt); server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000); Phương thức GetSocketOption() trả đối tượng Object, phải ép kiểu thành kiểu interger Sau biên dịch chạy chương trình với chương trình SimpleUdpServer trên, kết xuất sau: C:\>TimeoutUdpClient Gia tri timeout mac dinh: Gia tri timeout moi: 3000 Unhandled Exception: System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at System.Net.Sockets.Socket.ReceiveFrom(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, EndPoint& remoteEP) at System.Net.Sockets.Socket.ReceiveFrom(Byte[] buffer, EndPoint& remoteEP) at TimeoutUdpClient.Main() C:\> Giá trị ban đầu ReceiveTimeout thiết lập cho biết chờ liệu mãi Sau thêm phương thức SetSocketOpition() thiết lập giá trị 3000 mili giây hàm ReceiveFrom() đợi liệu giây, sau giây khơng có liệu để đọc phát sinh biệt lệ ta phải đặt hàm khối try – catch để xử lý biệt lệ III.6 Điều khiển việc truyền lại gói tin (74)Trang 74 Cách đơn giản để thực việc truyền lại tạo phương thức riêng lớp để điều khiển tất việc gởi nhận thông điệp Các bước thực sau: 1) Gởi thông điệp đến máy xa 2) Chờ câu trả lời từ máy xa 3) Nếu câu trả lời nhận, chấp nhận khỏi phương thức với liệu nhận kích thước liệu 4) Nếu khơng nhận câu trả lời khoảng thời gian time-out, tăng biến đếm thử lên 5) Kiểm tra biến đếm thử, nhỏ số lần định nghĩa trước quay lại bước 1, số lần định nghĩa trước, không truyền lại thông báo lỗi Một phương thức để gởi nhận gói tin udp tạo ra, dùng đâu chương trình nơi có liệu gởi tới thiết bị xa chờ câu trả lời Phương thức cài đặt sau: private int SndRcvData(Socket s, byte[] message, EndPoint rmtdevice) { int recv; int retry = 0; while (true) { Console.WriteLine("Truyen lai lan thu: #{0}", retry); try { s.SendTo(message, message.Length, SocketFlags.None, rmtdevice); data = new byte[1024]; recv = s.ReceiveFrom(data, ref Remote); } catch (SocketException) { recv = 0; } (75)Trang 75 return recv; } else { retry++; if (retry > 4) { return 0; } } Phương thức yêu cầu ba tham số: ✓ Một đối tượng socket thành lập ✓ Mảng liệu chứa thông điệp để gởi tới thiết bị xa ✓ Một đối tượng EndPoint chứa địa IP port thiết bị xa Đối tượng Socket truyền vào phương thức phải khởi tạo trước giá trị ReceiveTimeout thiết lập khoảng thời gian chờ câu trả lời từ thiết bị xa Phương thức SndRcvData() gởi liệu đến thiết bị xa dùng phương thức SendTo() truyền thống Sau gởi thông điệp, phương thức SndRcvData() block phương thức ReceiveFrom() chờ thông điệp trả Nếu thông điệp nhận từ thiết bị xa khoảng giá trị ReceiveTimeout phương thức SndRcvData() đặt liệu vào mảng byte định nghĩa lớp trả số byte đọc Nếu khơng có thơng điệp trả vào lúc kết thúc giá trị ReceiveTimeout, biệt lệ phát khối catch xử lý Trong khối catch, giá trị recv thiết lập Sau khối try-catch, giá trị recv kiểm tra Nếu giá trị số dương thơng điệp nhận thành cơng, số khơng có thơng điệp nhận giá trị tăng lên, sau kiểm tra đạt tới giá trị tối đa hay chưa, chưa đạt tới giá trị tối đa tồn q trình lặp lại bắt đầu gởi lại thông điệp, tới giá trị tối đa phương thức SndRcvData() trả Chương trình RetryUdpClient using System; (76)Trang 76 using System.Net.Sockets; using System.Text; class RetryUdpClient { private byte[] data = new byte[1024]; private static IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); private static EndPoint Remote = (EndPoint)sender; private int SndRcvData(Socket s, byte[] message, EndPoint rmtdevice) { int recv; int retry = 0; while (true) { Console.WriteLine("Truyen lai lan thu: #{0}", retry); try { s.SendTo(message, message.Length, SocketFlags.None, rmtdevice); data = new byte[1024]; recv = s.ReceiveFrom(data, ref Remote); } catch (SocketException) { recv = 0; } if (recv > 0) { return recv; } else { retry++; if (retry > 4) { return 0; } } } } public RetryUdpClient() { string input, stringData; int recv; (77)Trang 77 Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); int sockopt = (int)server.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout); Console.WriteLine("Gia tri timeout mac dinh: {0}", sockopt); server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000); sockopt = (int)server.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout); Console.WriteLine("Gia tri timeout moi: {0}", sockopt); string welcome = "Xin chao Server"; data = Encoding.ASCII.GetBytes(welcome); recv = SndRcvData(server, data, ipep); if (recv > 0) { stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } else { Console.WriteLine("Khong the lien lac voi thiet bi o xa"); return; } while (true) { input = Console.ReadLine(); if (input == "exit") break; recv = SndRcvData(server, Encoding.ASCII.GetBytes(input), ipep); if (recv > 0) { stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); } else Console.WriteLine("Khong nhan duoc cau tra loi"); } Console.WriteLine("Dang dong client"); server.Close(); } public static void Main() { (78)Trang 78 (79)Trang 79 CHƯƠNG IV: SỬ DỤNG CÁC LỚP HELPER CỦA C# SOCKET IV.1 Lớp TCP Client Lớp TcpClient nằm namespace System.Net.Sockets thiết kế để hỗ trợ cho việc viết ứng dụng TCP Client dễ dàng Lớp TcpClient cho phép tạo đối tượng Tcp Client sử dụng ba phương thức tạo lập sau: TcpClient(): phương thức tạo lập đầu tiên, đối tượng tạo phương thức tạo lập gắn kết với địa cục port TCP ngẫu nhiên Sau đối tượng TcpClient tạo ra, phải kết nối đến thiết bị xa thông qua phương thức Connect() ví dụ đây: TcpClient newcon = new TcpClient(); newcon.Connect("192.168.6.1", 8000); TcpClient(IPEndPoint localEP): phương thức tạo lập cho phép địa IP cục với port dùng Đây phương thức tạo lập thường sử dụng thiết bị có nhiều card mạng muốn liệu gởi card mạng Phương thức Connect() dùng để kết nối với thiết bị xa: IPEndPoint iep = new IPEndPoint(IPAddress,Parse("192.168.6.1"), 8000); 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 thường sử dụng nhất, cho phép thiết bị nhận phương thức tạo lập không cần phải dùng phương thức Connect() Địa thiết bị xa chuỗi hostname chuỗi địa IP Phương thức tạo lập TcpClient tự động phân giải hostname thành địa IP Ví dụ: TcpClient newcon = new TcpClient("www.isp.net", 8000); (80)Trang 80 Phương Thức Mơ Tả 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 dùng để gởi nhận liệu GetType() Lấy kiểu thể ToString() Chuyển thể 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 kết nối thành lập, phương thức GetStream() gán đối tượng NetworkStream để gởi nhận liệu nhờ vào phương thức Read() Write() Lớp TcpClient cịn có nhiều thuộc tính mơ tả bảng sau: Thuộc Tính Mơ Tả LingerState Lấy thiết lập thời gian kết nối TCP sau gọi phương thức Close() NoDelay Lấy thiết lập thời gian trễ dùng để gởi nhận đệm TCP ReceiveBufferSize Lấy thiết lập kích thước đệm TCP nhận ReceiveTimeout Lấy thiết lập thời gian timeout Socket SendBufferSize Lấy thiết lập kích thước đệm TCP gởi SendTimeout Lấy thiết lập giá trị timeout Socket Chương trình TCPClient đơn giản using System; using System.Net; using System.Net.Sockets; using System.Text; class TcpClientSample { public static void Main() { (81)Trang 81 try { server = new TcpClient("127.0.0.1", 5000); } catch (SocketException) { Console.WriteLine("Khong the ket noi den server"); return; } NetworkStream ns = server.GetStream(); int recv = ns.Read(data, 0, data.Length); stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData); 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); } Console.WriteLine("Dang ngat ket noi voi server "); ns.Close(); server.Close(); } } Trong chương trình phương thức tạo lập tự động kết nối đến Server xa, nên đặt khối try-catch để phịng trường hợp Server khơng hợp lệ (82)Trang 82 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 tham số: ✓ Mảng byte để đặt liệu nhận vào ✓ Vị trí offset đệm mà ta muốn đặt liệu ✓ Chiều dài đệm liệu Cũng giống phương thức Receive() Socket, phương thức Read() đọc lượng liệu có độ lớn tối đa độ lón đệm Nếu đệm nhỏ, phần liệu lại nằm stream đợi lần gọi phương thức Read() Phương thức Write() yêu cầu ba tham số: ✓ Mảng byte để gởi liệu ✓ Vị trí offset đệm mà ta muốn gởi liệu ✓ Chiều dài liệu gởi Cần ý TCP không bảo vệ biên thông điệp Điều áp dụng cho lớp TcpClient, ta cần phải xử lý vấn đề biên thông điệp giống phương thức Receive() lớp Socket cách tạo vòng lặp để đảm bảo tất liệu đọc từ stream Ta test chương trình với chương trình TCP Server đơn giản phần IV.2 Lớp TCPListener (83)Trang 83 Lớp TcpListener có ba phương thức tạo lập: ✓ TcpListener(int port): gắn đối tượng TcpListener vào port máy cục ✓ TcpListener(IPEndPoint ie): gắn đối tượng TcpListener vào đối tượng EndPoint cục ✓ TcpListener(IPAddress addr, int port): gắn đối tượng TcpListener vào đối tượng IPAddress port Không giống lớp TcpClient, phương thức tạo lập lớp TcpListener yêu cầu tham số: số port mà Server lắng nghe kết nối Nếu Server có nhiều card mạng ta muốn lắng nghe card mạng ta dùng đối tượng IPEndPoint để địa IP card với số port dùng để lắng nghe Lớp TcpListener có số phương thức sau: Phương Thức Mô Tả AcceptSocket() Chấp nhận kết nối port gán kết nối cho đối tượng Socket AcceptTcpClient() Chấp nhận kết nối port gán kết nối cho đối tượng TCPClient Equals() So sánh hai đối tượng TcpListener GetHashCode() Lấy hash code GetType() Lấy kiểu thể Pending() Kiểm tra xem có yêu cầu 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 ToString() Chuyển đối tượng TcpListener thành chuỗi Phương thức Start() tương tự phương thứcBind() Listen() dùng lớp socket Phương thức Start() kết nối Socket đến EndPoint định nghĩa phương thức tạo lập lớp TcpListener đặ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() so sánh với phương thức Accept() Socket, chấp nhận kết nối gán cho đối tượng TcpClient (84)Trang 84 TcpListener ban đầu, đối tượng TcpListener 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() Chương trình TCPListener using System; using System.Net; using System.Net.Sockets; using System.Text; class TcpListenerSample { public static void Main() { int recv; byte[] data = new byte[1024]; TcpListener newsock = new TcpListener(5000); newsock.Start(); Console.WriteLine("Dan cho client ket noi den "); TcpClient client = newsock.AcceptTcpClient(); NetworkStream ns = client.GetStream(); string welcome = "Hello Client"; data = Encoding.ASCII.GetBytes(welcome); ns.Write(data, 0, data.Length); while (true) { data = new byte[1024]; recv = ns.Read(data, 0, data.Length); if (recv == 0) break; Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv)); ns.Write(data, 0, recv); } ns.Close(); client.Close(); newsock.Stop(); } } (85)Trang 85 lắng nghe Sau phương thức AcceptTcpClient() chờ kết nối TCP đến gán kết nối đến vào đối tượng TcpClient: 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 thành lập, đối tượng NetworkStream gán vào để truyền thông với máy xa Tất thông tin liên lạc thực cách sử dụng phương thức Read() Write() IV.3 Lớp UdpClient Lớp UdpClient tạo để giúp cho việc lập trình mạng với giao thức UDP đơn giản Lớp UdpClient có bốn phương thức tạo lập: ✓ UdpClient(): tạo đối tượng UdpClient không gắn vào địa hay port ✓ UdpClient(int port): gắn đối tượng UdpClient tạo vào port/ ✓ UdpClient(IPEndPoint iep): gắn đối tượng UdpClient tạo vào địa Ip cục port ✓ UdpClient(string host, int port): gắn đối tượng UdpClient tạo vào địa IP port kết hợp với địa IP port xa Các phương thức tạo lập lớp UdpClient tương tự phương thức tạo lập lớp TcpClient, hệ thống chọn port thích hợp cho ứng dụng ta port dùng ứng dụng Nếu ứng dụng UDP phải chấp nhận liệu port đó, ta phải định nghĩa port phương thức tạo lập lớp UdpClient Một số phương thức lớp UdpClient: Phương Thức Mơ Tả Close() Đóng Socket bên Connect() Cho phép IP endpoint xa để gởi nhận liệu DropMulticastGroup() Gỡ bỏ Socket từ nhóm UDP multicast (86)Trang 86 Phương Thức Mô Tả GetHashCode() Lấy hash code GetType() Lấy kiểu đối tượng JoinMulticastGroup() Thêm Socket vào nhóm UDP multicast Receive() Nhận liệu từ Socket Send() Gởi 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 phương thức Send(), Receive() lớp UdpClient phương thức SendTo(), ReceiveFrom() Socket Phương thức Receive() Lớp UdpClient sử dụng phương thức Receive() để chấp nhận gói tin card mạng port Chỉ có cách sử dụng phương thức Receive(): byte[] Receive(ref IPEndPoint iep) Khi liệu nhận từ Socket, khơng đặt vào mảng byte phương thức phương thức ReceiveFrom() mà trả mảng byte Sự khác thứ hai phương thức ReceiveFrom() đặt thông tin máy xa vào đối tượng EndPoint cịn phương thức Receive() đặt thơng tin máy xa vào đối tượng IPEndPoint, việc làm cho lập trình viên cảm thấy dễ lập trình Khi nhiều liệu nhận kích thước đệm, thay phát sinh biệt lệ phương thức ReceiveFrom() Socket, UdpClient tả đệm liệu đủ lớn để chứa liệu nhận tính hay phương thức Receive() Phương thức Send() Phương thức Send() có ba tải hàm để gởi liệu tới thiết bị xa: ✓ Send(byte[] data, int sz): gởi mảng liệu với kích thước sz đến thiết bị xa mặc định Để dùng tải hàm này, ta phải thiết bị xa mặc định cách sử dụng phương thức tạo lập lớp UdpClient dùng phương thức Connect() (87)Trang 87 ✓ Send(byte[] data, int sz, string host, int port): gởi mảng liệu kích thước sz đến máy xa port Chương trình UdpClient Server using System; using System.Net; using System.Net.Sockets; using System.Text; class UdpSrvrSample { public static void Main() { 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 = Encoding.ASCII.GetBytes(welcome); newsock.Send(data, data.Length, sender); while (true) { data = newsock.Receive(ref sender); Console.WriteLine(Encoding.ASCII.GetString(data, 0, data.Length)); newsock.Send(data, data.Length, sender); } } } Chương trình UdpClient Client using System; using System.Net; using System.Net.Sockets; using System.Text; class UdpClientSample { public static void Main() { (88)Trang 88 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}:", sender.ToString()); stringData = Encoding.ASCII.GetString(data, 0, data.Length); Console.WriteLine(stringData); while (true) { input = Console.ReadLine(); if (input == "exit") break; server.Send(Encoding.ASCII.GetBytes(input), input.Length); data = server.Receive(ref sender); stringData = Encoding.ASCII.GetString(data, 0, data.Length); Console.WriteLine(stringData); } Console.WriteLine("Dang dong client"); server.Close(); (89)Trang 89 CHƯƠNG V: ĐA NHIỆM TIỂU TRÌNH V.1 Khái niệm tiến trình tiểu trình Windows Tiến trình thể chương trình họat động Một tiến trình ln sở hữu khơng gian địa có kích thước 4GB chứa mã chương trình, liệu, sở hữu tài nguyên hệ thống tập tin, đối tượng đồng hóa Mỗi tiến trình tạo lập có tiểu trình sau tạo lập nhiều tiến trình khác Tiểu trình thành phần đơn vị tiến trình thực thị ứng với đọan mã chương trình Hệ điều hành Windows cho phép tiểu trình họat động độc lập tổ chức điều phối (lập lịch tiến trình) CPU để tiểu trình họat động đồng thời Hệ điều hành phân chia thời gian sử dụng CPU cho tiến trình mịn theo kiểu xoay vịng Mỗi tiểu trình có trạng thái : Running, Ready, Blocked Các tiểu trình tiến trình truy xuất đến biến tồn cục tiến trình V.2 Mơ hình Các ứng dụng cài đặt theo mơ hình đa tiến trình hay đa tiểu trình phải đối diện với vấn đề sau : - Hệ thống tiêu thụ thêm nhớ để lưu trữ cấu trúc mô tả tiến trình hay tiểu trình Biến tồn cục tiến trình Heap tiến trình Biến mơi trường tiến trình Stack tiểu trình Tiểu trình Stack tiểu trình Tiểu trình CPU T1 T2 T3 T4 T5 T6 T7 T8 T9 (90)Trang 90 - Hệ thống tốn thêm thời gian để theo vết chương trình, quản lý tiểu trình - Nhiều tiến trình tranh chấp tài nguyên dùng chung địi hỏi thực đồng hóa V.3 Các kỹ thuật NET tạo tiểu trình Thư viện lớp NET Framework cung cấp số phương pháp tạo tiểu trình mới: ✓ Thực thi phương phức tiểu trình Thread-pool ✓ Thực thi phương thức cách bất đồng ✓ Thực thi phương thức tiểu trình theo chu kỳ hay thời điểm xác định ✓ Thực thi phương thức cách hiệu đối tượng WaitHandle V.3.1 Tạo tiểu trình Thread-pool Cách tạo: ✓ Khai báo phương thức chứa mã lệnh cần thực thi ✓ Phương thức phải trả void nhận đối số ✓ Tạo thể ủy nhiệm System.Threading.WaitCallback tham chiếu đến phương thức ✓ Gọi phương thức tĩnh QueueUserWorkItem lớp System.Threading.ThreadPool, ✓ Truyền thể ủy nhiệm tạo làm đối số ✓ Bộ thực thi xếp thể ủy nhiệm vào hàng đợi thực thi tiểu trình thread-pool sẵn sàng Ví dụ thực thi phương thức có tên DisplayMessage: Truyền DisplayMessage đến thread-pool hai lần Lần đầu khơng có đối số Lần sau có đối số đối tượng MessageInfo Chương trình ThreadPoolExample using System; using System.Threading; (91)Trang 91 public class MessageInfo { private int iterations; private string message; // Phương thức khởi dựng nhận thiết lập cấu hình cho tiểu trình public MessageInfo(int iterations, string message) { this.iterations = iterations; this.message = message; } // Các thuộc tính dùng để lấy thiết lập cấu hình public int Iterations { get { return iterations; } } public string Message { get { return message; } } } public class ThreadPoolExample { // Hiển thị thông tin cửa sổ Console public static void DisplayMessage(object state) { // Ép đối số state sang MessageInfo MessageInfo config = state as MessageInfo; // Nếu đối số config null, đối số truyền cho phương thức // ThreadPool.QueueUserWorkItem; // sử dụng giá trị mặc định if (config == null) { // Hiển thị thông báo cửa sổ Console ba lần for (int count = 0; count < 3; count++) { Console.WriteLine("A thread-pool example."); // Vào trạng thái chờ, dùng cho mục đích minh họa // Tránh đưa tiểu trình thread-pool // vào trạng thái chờ ứng dụng thực tế Thread.Sleep(1000); } } else { // Hiển thị thông báo định trước // với số lần định trước (92)Trang 92 Console.WriteLine(config.Message); // Vào trạng thái chờ, dùng cho mục đích minh họa // Tránh đưa tiểu trình thread-pool // vào trạng thái chờ ứng dụng thực tế Thread.Sleep(1000); } } } public static void Main() { // Tạo đối tượng ủy nhiệm, cho phép // truyền phương thức DisplayMessage cho thread-pool WaitCallback workMethod = new WaitCallback(ThreadPoolExample.DisplayMessage); // Thực thi DisplayMessage thread-pool (khơng có đối số) ThreadPool.QueueUserWorkItem(workMethod); // Thực thi DisplayMessage thread-pool (truyền // đối tượng MessageInfo cho phương thức DisplayMessage) MessageInfo info = new MessageInfo(5, "A thread-pool example with arguments."); ThreadPool.QueueUserWorkItem(workMethod, info); // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } Tình sử dụng: Khi tiểu trình thread-pool sẵn sàng, nhận cơng việc từ hàng đợi thực thi cơng việc Khi hồn tất cơng việc, thay kết thúc, tiểu trình quay thread-pool nhận công việc từ hàng đợi Việc sử dụng thread-pool giúp đơn giản hóa việc lập trình hỗ-trợ-đa-tiểu-trình Tuy nhiên, cần lưu ý định sử dụng thread-pool, cần xem xét điểm sau: (93)Trang 93 - Không thể điều khiển lịch trình tiểu trình thread-pool, thay đổi độ ưu tiên công việc Thread-pool xử lý công việc theo thứ tự thêm chúng vào hàng đợi V.3.2 Tạo tiểu trình bất đồng Khi cho gọi phương thức,thường thực cách đồng bộ; nghĩa mã lệnh thực lời gọi phải vào trạng thái dừng (block) phương thức thực xong Trong số trường hợp, cần thực thi phương thức cách bất đồng bộ; nghĩa cho thực thi phương thức tiểu trình riêng tiếp tục thực cơng việc khác Sau phương thức hồn tất, cần lấy trị trả Nguyên tắc hoạt động: NET Framework hỗ trợ chế độ thực thi bất đồng bộ, cho phép thực thi phương thức cách bất đồng ủy nhiệm Khi khai báo biên dịch ủy nhiệm, trình biên dịch tự động sinh hai phương thức hỗ trợ chế độ thực thi bất đồng bộ: BeginInvoke EndInvoke Khi gọi phương thức BeginInvoke thể ủy nhiệm, phương thức tham chiếu ủy nhiệm xếp vào hàng đợi để thực thi bất đồng Quyền kiểm sốt q trình thực thi trả cho mã gọi BeginInvoke sau đó, phương thức tham chiếu thực thi ngữ cảnh tiểu trình sẵn sàng trước tiên thread-pool Các bước thực hiện: Khai báo ủy nhiệm có chữ ký giống phương thức cần thực thi Tạo thể ủy nhiệm tham chiếu đến phương thức Gọi phương thức BeginInvoke thể ủy nhiệm để thực thi phương thức Sử dụng phương thức EndInvoke để kiểm tra trạng thái phương thức thu lấy trị trả hồn tất Các đối số phương thức BeginInvoke gồm đối số định ủy nhiệm, cộng với hai đối số dùng phương thức thực thi bất đồng kết thúc: (94)Trang 94 kết thúc Phương thức thực thi ngữ cảnh tiểu trình thread-pool Truyền giá trị null cho đối số nghĩa khơng có phương thức gọi phải sử dụng chế khác để xác định phương thức thực thi bất kết thúc ✓ Một tham chiếu đối tượng mà thực thi liên kết với trình thực thi bất đồng Phương thức thực thi bất đồng sử dụng hay truy xuất đến đối tượng này, mã lệnh sử dụng phương thức kết thúc, cho phép liên kết thơng tin trạng thái với q trình thực thi bất đồng Ví dụ, đối tượng cho phép ánh xạ kết với thao tác bất đồng khởi tạo trường hợp khởi tạo nhiều thao tác bất đồng sử dụng chung phương thức callback để xử lý việc kết thúc Phương thức EndInvoke cho phép lấy trị trả phương thức thực thi bất đồng bộ, trước hết phải xác định kết thúc Có bốn kỹ thuật dùng để xác định phương thức thực thi bất đồng kết thúc hay chưa: Blocking: dừng trình thực thi tiểu trình hành phương thức thực thi bất đồng kết thúc Điều giống với thực thi đồng Tuy nhiên, linh hoạt chọn thời điểm xác để đưa mã lệnh vào trạng thái dừng (block) cịn hội thực thêm số việc trước mã lệnh vào trạng thái Polling: lặp lặp lại việc kiểm tra trạng thái phương thức thực thi bất đồng để xác định kết thúc hay chưa Đây kỹ thuật đơn giản, xét mặt xử lý khơng hiệu Nên tránh vịng lặp chặt làm lãng phí thời gian xử lý; tốt nên đặt tiểu trình thực polling vào trạng thái nghỉ (sleep) khoảng thời gian cách sử dụng Thread.Sleep lần kiểm tra trạng thái Bởi kỹ thuật polling địi hỏi phải trì vịng lặp nên hoạt động tiểu trình chờ bị giới hạn, nhiên dễ dàng cập nhật tiến độ công việc (95)Trang 95 Có thể định giá trị time-out cho phép tiểu trình thực waiting dừng lại phương thức thực thi bất đồng diễn lâu, muốn cập nhật định kỳ trạng thái Callback: Callback phương thức mà thực thi gọi phương thức thực thi bất đồng kết thúc Mã lệnh thực lời gọi không cần thực thao tác kiểm tra nào, tiếp tục thực công việc khác Callback linh hoạt phức tạp, đặc biệt có nhiều phương thức thực thi bất đồng chạy đồng thời sử dụng callback Trong trường hợp thế, phải sử dụng đối tượng trạng thái thích hợp để so trùng phương thức hoàn tất với phương thức khởi tạo Ví dụ: Lớp AsyncExecutionExample mô tả chế thực thi bất đồng Nó sử dụng ủy nhiệm có tên AsyncExampleDelegate để thực thi bất đồng phương thức có tên LongRunningMethod Phương thức LongRunningMethod sử dụng Thread.Sleep để mô phương thức có thời gian thực thi dài: // Ủy nhiệm cho phép bạn thực việc thực thi bất đồng //của AsyncExecutionExample.LongRunningMethod public delegate DateTime AsyncExampleDelegate(int delay, string name); // Phương thức có thời gian thực thi dài public static DateTime LongRunningMethod(int delay, string name) { Console.WriteLine("{0} : {1} example - thread starting.", DateTime.Now.ToString("HH:mm:ss.ffff"), name); // Mô việc xử lý tốn nhiều thời gian Thread.Sleep(delay); Console.WriteLine("{0}:{1}example–thread finishing.", DateTime.Now.ToString("HH:mm:ss.ffff"), name); // Trả thời gian hoàn tất phương thức (96)Trang 96 AsyncExecutionExample chứa phương thức diễn đạt cách tiếp cận khác việc kết thúc phương thức thực thi bất đồng V.3.2.1 Phương thức BlockingExample Phương thức BlockingExample thực thi bất đồng phương thức LongRunningMethod tiếp tục thực cơng việc khoảng thời gian Khi xử lý xong công việc này, BlockingExample chuyển sang trang thái dừng (block) phương thức LongRunningMethod kết thúc Để vào trạng thái dừng, BlockingExample thực thi phương thức EndInvoke thể ủy nhiệm AnsyncExampleDelegate Nếu phương thức LongRunningMethod kết thúc, EndInvoke trả lập tức, không, BlockingExample chuyển sang trạng thái dừng phương thức LongRunningMethod kết thúc public static void BlockingExample() { Console.WriteLine(Environment.NewLine + "*** Running Blocking Example ***"); // Gọi LongRunningMethod cách bất đồng Truyền null cho // ủy nhiệm callback đối tượng trạng thái bất đồng AsyncExampleDelegate longRunningMethod = new AsyncExampleDelegate(LongRunningMethod); IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Blocking", null, null); // Thực công việc khác // sẵn sàng vào trạng thái dừng for (int count = 0; count < 3; count++) { Console.WriteLine("{0} : Continue processing until " + "ready to block ", DateTime.Now.ToString("HH:mm:ss.ffff")); Thread.Sleep(200); } // Đi vào trạng thái dừng phương thức // thực thi bất đồng kết thúc thu lấy kết (97)Trang 97 DateTime completion = longRunningMethod.EndInvoke(asyncResult); // Hiển thị thông tin kết thúc Console.WriteLine("{0} : Blocking example complete.", completion.ToString("HH:mm:ss.ffff")); } V.3.2.2 Phương thức PollingExample Phương thức PollingExample thực thi bất đồng phương thức LongRunningMethod sau thực vịng lặp polling LongRunningMethod kết thúc PollingExample kiểm tra thuộc tính IsComplete thể IAsyncResult (được trả BeginInvoke) để xác định phương thức LongRunningMethod kết thúc hay chưa, chưa, PollingExample gọi Thread.Sleep public static void PollingExample() { Console.WriteLine(Environment.NewLine + " Running Polling Example"); // Gọi LongRunningMethod cách bất đồng Truyền null cho // ủy nhiệm callback đối tượng trạng thái bất đồng AsyncExampleDelegate longRunningMethod = new AsyncExampleDelegate(LongRunningMethod); IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Polling", null, null); // Thực polling để kiểm tra phương thức thực thi // bất đồng kết thúc hay chưa Nếu chưa kết thúc vào // trạng thái chờ 300 mini-giây trước thực polling lần Console.WriteLine("{0} : Poll repeatedly until method is complete ", DateTime.Now.ToString("HH:mm:ss.ffff")); while (!asyncResult.IsCompleted) { (98)Trang 98 DateTime.Now.ToString("HH:mm:ss.ffff")); Thread.Sleep(300); } // Thu lấy kết phương thức thực thi bất đồng DateTime completion = longRunningMethod.EndInvoke(asyncResult); // Hiển thị thông tin kết thúc Console.WriteLine("{0} : Polling example complete.", completion.ToString("HH:mm:ss.ffff")); } V.3.2.3 Phương thức WaitingExample Phương thức WaitingExample thực thi bất đồng phương thức LongRunningExample sau chờ LongRunningMethod kết thúc WaitingExample sử dụng thuộc tính AsyncWaitHandle thể IAsyncResult (được trả BeginInvoke) để có WaitHandle Gọi phương thức WaitOne WaitHandle Việc sử dụng giá trị time-out cho phép WaitingExample dừng trình đợi để thực cơng việc khác dừng hồn tồn phương thức thực thi bất đồng diễn lâu public static void WaitingExample() { Console.WriteLine(Environment.NewLine + "*** Running Waiting Example ***"); // Gọi LongRunningMethod cách bất đồng Truyền null cho // ủy nhiệm callback đối tượng trạng thái bất đồng AsyncExampleDelegate longRunningMethod = new AsyncExampleDelegate(LongRunningMethod); IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Waiting", null, null); // Đợi phương thức thực thi bất đồng kết thúc // Time-out sau 300 mili-giây hiển thị trạng thái // cửa sổ Console trước tiếp tục đợi (99)Trang 99 while (!asyncResult.AsyncWaitHandle.WaitOne(300, false)) { Console.WriteLine("{0} : Wait timeout ", DateTime.Now.ToString("HH:mm:ss.ffff")); } // Thu lấy kết phương thức thực thi bất đồng DateTime completion = longRunningMethod.EndInvoke(asyncResult); // Hiển thị thông tin kết thúc Console.WriteLine("{0} : Waiting example complete.", completion.ToString("HH:mm:ss.ffff")); } V.3.2.4 Phương thức WaitAllExample Phương thức WaitAllExample thực thi bất đồng phương thức LongRunningMethod nhiều lần sau sử dụng mảng đối tượng WaitHandle để đợi tất phương thức kết thúc public static void WaitAllExample() { Console.WriteLine(Environment.NewLine + "*** Running WaitAll Example ***"); // Một ArrayList chứa thể IAsyncResult // cho phương thức thực thi bất đồng ArrayList asyncResults = new ArrayList(3); // Gọi ba lần LongRunningMethod cách bất đồng // Truyền null cho ủy nhiệm callback đối tượng // trạng thái bất đồng Thêm thể IAsyncResult // cho phương thức vào ArrayList AsyncExampleDelegate longRunningMethod = new AsyncExampleDelegate(LongRunningMethod); asyncResults.Add(longRunningMethod.BeginInvoke(3000, "WaitAll 1", null, null)); asyncResults.Add(longRunningMethod.BeginInvoke(2500, "WaitAll 2", null, null)); asyncResults.Add(longRunningMethod.BeginInvoke(1500, "WaitAll 3", null, null)); (100)Trang 100 // sử dụng để đợi tất phương thức // thực thi bất đồng kết thúc WaitHandle[] waitHandles = new WaitHandle[3]; for (int count = 0; count < 3; count++) { waitHandles[count] = ((IAsyncResult)asyncResults[count]).AsyncWaitHandle; } // Đợi ba phương thức thực thi bất đồng kết thúc // Time-out sau 300 mili-giây hiển thị trạng thái // cửa sổ Console trước tiếp tục đợi Console.WriteLine("{0} : Waiting until all methods are complete ", DateTime.Now.ToString("HH:mm:ss.ffff")); while (!WaitHandle.WaitAll(waitHandles, 300, false)) { Console.WriteLine("{0} : WaitAll timeout ", DateTime.Now.ToString("HH:mm:ss.ffff")); } // Kiểm tra kết phương thức xác định // thời gian phương thức cuối kết thúc DateTime completion = DateTime.MinValue; foreach (IAsyncResult result in asyncResults) { DateTime time = longRunningMethod.EndInvoke(result); if (time > completion) completion = time; } // Hiển thị thông tin kết thúc Console.WriteLine("{0} : WaitAll example complete.", completion.ToString("HH:mm:ss.ffff")); } V.3.2.5 Phương thức CallbackExample Phương thức CallbackExample thực thi bất đồng phương thức LongRunningMethod truyền thể ủy nhiệm AsyncCallback (tham chiếu đến phương thức CallbackHandler) cho phương thức BeginInvoke Phương thức CallbackHandler gọi cách tự động phương thức LongRunningMethod kết thúc Kết phương thức CallbackExample tiếp tục thực công việc (101)Trang 101 { Console.WriteLine(Environment.NewLine + "*** Running Callback Example ***"); // Gọi LongRunningMethod cách bất đồng Truyền // thể ủy nhiệm AsyncCallback tham chiếu đến // phương thức CallbackHandler CallbackHandler // tự động gọi phương thức thực thi bất đồng // kết thúc Truyền tham chiếu đến thể ủy nhiệm // AsyncExampleDelegate trạng thái bất đồng bộ; // không, phương thức callback truy xuất // thể ủy nhiệm để gọi EndInvoke AsyncExampleDelegate longRunningMethod = new AsyncExampleDelegate(LongRunningMethod); IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Callback", new AsyncCallback(CallbackHandler), longRunningMethod); // Tiếp tục với công việc khác for (int count = 0; count < 15; count++) { Console.WriteLine("{0} : Continue processing ", DateTime.Now.ToString("HH:mm:ss.ffff")); Thread.Sleep(200); } } // Phương thức xử lý việc kết thúc bất đồng callbacks.public static void CallbackHandler(IAsyncResult result) { // Trích tham chiếu đến thể AsyncExampleDelegate // từ thể IAsyncResult AsyncExampleDelegate longRunningMethod = (AsyncExampleDelegate)result.AsyncState; // Thu lấy kết phương thức thực thi bất đồng DateTime completion = longRunningMethod.EndInvoke(result); // Hiển thị thông tin kết thúc (102)Trang 102 } V.3.3 Thực thi phương thức Timer Kỹ thuật giúp thực thi phương thức tiểu trình riêng theo chu kỳ hay thời điểm xác định Thơng thường, hữu ích thực thi phương thức thời điểm xác định hay thời khoảng xác định Ví dụ, cần lưu liệu lúc 1:00 AM ngày hay xóa vùng đệm liệu 20 phút Lớp Timer giúp việc định thời thực thi phương thức trở nên dễ dàng, cho phép thực thi phương thức tham chiếu ủy nhiệm TimerCallback thời khoảng định Phương thức tham chiếu thực thi ngữ cảnh tiểu trình thread-pool Cách thực Khai báo phương thức trả void nhận đối tượng làm đối số Sau đó, tạo thể ủy nhiệm System.Threading.TimerCallback tham chiếu đến phương thức Tiếp theo, tạo đối tượng System.Threading.Timer truyền cho thể ủy nhiệm TimerCallback với đối tượng trạng thái mà Timer truyền cho phương thức Timer hết hiệu lực Bộ thực thi chờ Timer hết hiệu lực sau gọi phương thức tiểu trình thread-pool Khi tạo đối tượng Timer, cần định hai thời khoảng (thời khoảng định giá trị kiểu int, long, uint, hay System.TimeSpan): ✓ Giá trị thời gian trễ (tính mili-giây) để phương thức thực thi lần Chỉ định giá trị để thực thi phương thức lập tức, định System.Threading.Timeout.Infinite để tạo Timer trạng thái chưa bắt đầu (unstarted) (103)Trang 103 Sau tạo đối tượng Timer, thay đổi thời khoảng sử dụng Timer phương thức Change, thay đổi phương thức gọi Khi dùng xong Timer, nên gọi phương thức Timer.Depose để giải phóng tài nguyên hệ thống bị chiếm giữ Timer Việc hủy Timer hủy phương thức định thời thực thi Ví dụ Lớp TimerExample trình bày cách sử dụng Timer để gọi phương thức có tên TimerHandler Ban đầu, Timer cấu hình để gọi TimerHandler sau hai giây lặp lại sau giây Ví dụ trình bày cách sử dụng phương thức Timer.Change để thay đổi thời khoảng Chương trình TimerExample using System; using System.Threading; public class TimerExample { // Phương thức thực Timer hết hiệu lực // Hiển thị thông báo cửa sổ Console private static void TimerHandler(object state) { Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), state); } public static void Main() { // Tạo thể ủy nhiệm TimerCallback // tham chiếu đến phương thức tĩnh TimerHandler // TimerHandler gọi Timer hết hiệu lực TimerCallback handler = new TimerCallback(TimerHandler); // Tạo đối tượng trạng thái, đối tượng // truyền cho phương thức TimerHandler // Trong trường hợp này, thông báo hiển thị string state = "Timer expired."; Console.WriteLine("{0} : Creating Timer.", DateTime.Now.ToString("HH:mm:ss.ffff")); // Tạo Timer, phát sinh lần sau hai giây // sau giây (104)Trang 104 { int period; // Đọc thời khoảng từ Console // người dùng nhập Các giá trị không hợp lệ // sử dụng giá trị mặc định (dừng ví dụ) { try { period = Int32.Parse(Console.ReadLine()); } catch { period = 0; } // Thay đổi Timer với thời khoảng if (period > 0) timer.Change(0, period); } while (period > 0); } // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } V.3.4 Thực thi phương thức tiểu trình Thực thi mã lệnh tiểu trình riêng, muốn kiểm sốt hồn tồn trình thực thi trạng thái tiểu trình Để tăng độ linh hoạt mức độ kiểm sốt thực ứng dụng hỗ-trợ-đa-tiểu-trình, bạn phải trực tiếp tạo quản lý tiểu trình Lớp Thread cung cấp chế mà qua bạn tạo kiểm sốt tiểu trình Các bước thực hiện: Tạo đối tượng ủy nhiệm ThreadStart tham chiếu đến phương thức chứa mã lệnh mà muốn dùng tiểu trình để chạy Giống ủy nhiệm khác, ThreadStart tham chiếu đến phương thức tĩnh hay phương thức đối tượng Phương thức tham chiếu phải trả void khơng có đối số (105)Trang 105 Gọi thực thi phương thức Start đối tượng Thread để chuyển trạng thái sang ThreadState.Running bắt đầu thực thi phương thức tham chiếu thể ủy nhiệm ThreadStart Vì ủy nhiệm ThreadStart khai báo khơng có đối số, nên khơng thể truyền liệu trực tiếp cho phương thức tham chiếu Để truyền liệu cho tiểu trình mới, cần phải cấu hình liệu khả truy xuất mã lệnh chạy tiểu trình Cách tiếp cận thơng thường tạo lớp đóng gói liệu cần cho tiểu trình phương thức thực thi tiểu trình Khi muốn chạy tiểu trình mới, tạo đối tượng lớp này, cấu hình trạng thái cho nó, chạy tiểu trình Ví dụ using System; using System.Threading; public class ThreadExample { // Các biến giữ thông tin trạng thái private int iterations; private string message; private int delay; public ThreadExample(int iterations, string message, int delay) { this.iterations = iterations; this.message = message; this.delay = delay; } public void Start() { // Tạo thể ủy nhiệm ThreadStart // tham chiếu đến DisplayMessage ThreadStart method = new ThreadStart(this.DisplayMessage); // Tạo đối tượng Thread truyền thể ủy nhiệm // ThreadStart cho phương thức khởi dựng Thread thread = new Thread(method); Console.WriteLine("{0} : Starting new thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); // Khởi chạy tiểu trình thread.Start(); } (106)Trang 106 { // Hiển thị thông báo cửa sổ Console với số lần // định (iterations), nghỉ thông báo // khoảng thời gian định (delay) for (int count = 0; count < iterations; count++) { Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), message); Thread.Sleep(delay); } } public static void Main() { // Tạo đối tượng ThreadExample ThreadExample example = new ThreadExample(5, "A thread example.", 500); // Khởi chạy đối tượng ThreadExample example.Start(); // Tiếp tục thực công việc khác for (int count = 0; count < 13; count++) { Console.WriteLine("{0} : Continue processing ", DateTime.Now.ToString("HH:mm:ss.ffff")); Thread.Sleep(200); } // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } V.3.5 Điều khiển trình thực thi tiểu trình Cần nắm quyền điều khiển tiểu trình chạy dừng, tạm dừng q trình thực thi tiểu trình (107)Trang 107 Phương Thức Mô Tả Abort() - Kết thúc tiểu trình cách ném ngoại lệ System.Threading ThreadAbortException mã lệnh chạy - Mã lệnh tiểu trình bị hủy bắt ngoại lệ ThreadAbortException để thực việc dọn dẹp, thực thi tự động ném ngoại lệ lần để bảo đảm tiểu trình kết thúc, trừ ResetAbort gọi Interrupt() - Ném ngoại lệ - System.Threading.ThreadInterruptedException (trong mã lệnh chạy) lúc tiểu trình trạng thái WaitSleepJoin Điều nghĩa tiểu trình gọi Sleep, Join đợi WaitHandle - Nếu tiểu trình khơng trạng thái WaitSleepJoin, ThreadInterruptedException bị ném sau tiểu trình vào trạng thái WaitSleepJoin Resume - Phục hồi trình thực thi tiểu trình bị tạm ngưng - Việc gọi Resume tiểu trình chưa bị tạm hoãn sinh ngoại lệ System.Threading.ThreadStateException tiểu trình gọi Start Khởi chạy tiểu trình mới; Suspend - Tạm hỗn q trình thực thi tiểu trình phương thức Resume gọi - Việc tạm hoãn tiểu trình bị tạm hỗn khơng có hiệu lực - Việc gọi Suspend tiểu trình chưa khởi chạy kết thúc sinh ngoại lệ ThreadStateException tiểu trình gọi (108)Trang 108 Lớp ThreadControlExample: ✓ Ví dụ khởi chạy tiểu trình thứ hai, hiển thị định kỳ thông báo cửa sổ Console vào trạng thái nghỉ (sleep) ✓ Bằng cách nhập lệnh dấu nhắc lệnh, ta gián đoạn, tạm hoãn, phục hồi, hủy bỏ tiểu trình thứ hai Chương trình ThreadControlExample using System; using System.Threading; public class ThreadControlExample { private static void DisplayMessage() { // Lặp lặp lại việc hiển thị thông báo cửa sổ Console while (true) { try { Console.WriteLine("{0} : Second thread running Enter (S)uspend, (R)esume, (I)nterrupt, or (E)xit.", DateTime.Now.ToString("HH:mm:ss.ffff")); // Nghỉ giây Thread.Sleep(2000); } catch (ThreadInterruptedException) { // Tiểu trình bị gián đoạn Việc bắt ngoại lệ // ThreadInterruptedException cho phép ví dụ // thực hành động phù hợp tiếp tục thực thi Console.WriteLine("{0} : Second thread interrupted.", DateTime.Now.ToString("HH:mm:ss.ffff")); } catch (ThreadAbortException abortEx) { // Đối tượng thuộc tính // ThreadAbortException.ExceptionState cung cấp // tiểu trình gọi Thread.Abort // Trong trường hợp này, chứa chuỗi // mô tả lý việc hủy bỏ Console.WriteLine("{0} : Second thread aborted ({1})", (109)Trang 109 // Mặc dù ThreadAbortException thụ lý, // thực thi ném lần để bảo đảmtiểu trình kết thúc } } } public static void Main() { // Tạo đối tượng Thread truyền cho thể // ủy nhiệm ThreadStart tham chiếu đến DisplayMessage Thread thread = new Thread(new ThreadStart(DisplayMessage)); Console.WriteLine("{0} : Starting second thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); // Khởi chạy tiểu trình thứ hai thread.Start(); // Lặp xử lý lệnh người dùng nhập char command = ' '; { string input = Console.ReadLine(); if (input.Length > 0) command = input.ToUpper()[0]; else command = ' '; switch (command) { case 'S': // Tạm hỗn tiểu trình thứ hai Console.WriteLine("{0} : Suspending second thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); thread.Suspend(); break; case 'R': // Phục hồi tiểu trình thứ hai try { Console.WriteLine("{0} : Resuming second thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); thread.Resume(); } catch (ThreadStateException) { Console.WriteLine("{0} : Thread wasn't suspended.", (110)Trang 110 } break; case 'I': // Gián đoạn tiểu trình thứ hai Console.WriteLine("{0} : Interrupting second thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); thread.Interrupt(); break; case 'E': // Hủy bỏ tiểu trình thứ hai truyền đối tượng // trạng thái cho tiểu trình bị hủy, // trường hợp thông báo Console.WriteLine("{0} : Aborting second thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); thread.Abort("Terminating example."); // Đợi tiểu trình thứ hai kết thúc thread.Join(); break; } } while (command != 'E'); // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } V.3.6 Nhận biết tiểu trình kết thúc Để kiểm tra tiểu trình kết thúc hay chưa kiểm tra thuộc tính Thread.IsAlive Thuộc tính trả true tiểu trình khởi chạy chưa kết thúc hay bị hủy Thông thường,cần tiểu trình để đợi tiểu trình khác hồn tất việc xử lý Thay kiểm tra thuộc tính IsAlive vịng lặp, sử dụng phương thức Thread.Join Phương thức khiến tiểu trình gọi dừng lại (block) tiểu trình tham chiếu kết thúc (111)Trang 111 gọi phục hồi lại Nếu định giá trị time-out, Join trả true tiểu trình kết thúc, false Join hết hiệu lực Ví dụ Ví dụ thực thi tiểu trình thứ hai gọi Join để đợi tiểu trình thứ hai kết thúc Vì tiểu trình thứ hai giây để thực thi, phương thức Join định giá trị time-out giây, nên Join hết hiệu lực ví dụ hiển thị thơng báo cửa sổ Console Chương trình ThreadFinishExample using System; using System.Threading; public class ThreadFinishExample { private static void DisplayMessage() { // Hiển thị thông báo cửa sổ Console lần for (int count = 0; count < 5; count++) { Console.WriteLine("{0} : Second thread", DateTime.Now.ToString("HH:mm:ss.ffff")); // Nghỉ giây Thread.Sleep(1000); } } public static void Main() { // Tạo thể ủy nhiệm ThreadStart // tham chiếu đến DisplayMessage ThreadStart method = new ThreadStart(DisplayMessage); // Tạo đối tượng Thread truyền thể ủy nhiệm // ThreadStart cho phương thức khởi dựng Thread thread = new Thread(method); Console.WriteLine("{0} : Starting second thread.", DateTime.Now.ToString("HH:mm:ss.ffff")); // Khởi chạy tiểu trình thứ hai thread.Start(); // Dừng tiểu trình thứ hai kết thúc, // Join hết hiệu lực sau giây (112)Trang 112 Console.WriteLine("{0} : Join timed out !!", DateTime.Now.ToString("HH:mm:ss.ffff")); } // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } V.3.7 Khởi chạy tiến trình Lớp Process cung cấp dạng biểu diễn quản lý cho tiến trình hệ điều hành Lớp Process thực bốn tải hàm phương thức Start Hai số phương thức tĩnh, cho phép định tên đối số cho tiến trình Ví dụ, hai lệnh thực thi Notepad tiến trình mới: // Thực thi notepad.exe, khơng có đối số Process.Start("notepad.exe"); // Thực thi notepad.exe, tên file cần mở đối số Process.Start("notepad.exe", "SomeFile.txt"); Hai dạng khác phương thức Start yêu cầu tạo đối tượng ProcessStartInfo cấu hình với chi tiết tiến trình cần chạy.Việc sử dụng đối tượng ProcessStartInfo cung cấp chế điều khiển tốt hành vi cấu hình tiến trình Tóm tắt vài thuộc tính thơng dụng lớp ProcessStartInfo: ✓ Arguments: Các đối số dùng để truyền cho tiến trình ✓ ErrorDialog :Nếu Process.Start khơng thể khởi chạy tiến trình định, ném ngoại lệ System.ComponentModel.Win32Exception Nếu ErrorDialog true, Start hiển thị thông báo lỗi trước ném ngoại lệ ✓ FileName : Tên ứng dụng ✓ WindowStyle :Điều khiển cách thức hiển thị cửa sổ Các giá trị hợp lệ bao gồm: Hidden, Maximized, Minimized, Normal ✓ WorkingDirectory : Tên đầy đủ thư mục làm việc (113)Trang 113 Ví dụ sau sử dụng Process để thực thi Notepad cửa sổ trạng thái phóng to mở file có tên C:\Temp\file.txt Sau tạo, ví dụ gọi phương thức Process.WaitForExit để dừng tiểu trình chạy tiến trình kết thúc giá trị time-out hết hiệu lực Chương trình StartProcessExample using System; using System.Diagnostics; public class StartProcessExample { public static void Main() { // Tạo đối tượng ProcessStartInfo cấu hình cho // với thông tin cần thiết để chạy tiến trình ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = "notepad.exe"; startInfo.Arguments = "file.txt"; startInfo.WorkingDirectory = "C:\\Temp"; startInfo.WindowStyle = ProcessWindowStyle.Maximized; startInfo.ErrorDialog = true; // Tạo đối tượng Process using (Process process = new Process()) { // Gán ProcessStartInfo vào Process process.StartInfo = startInfo; try { // Khởi chạy tiến trình process.Start(); // Đợi tiến trình kết thúc trước thoát Console.WriteLine("Waiting 30 seconds for process to finish."); process.WaitForExit(30000); } catch (Exception ex) { Console.WriteLine("Could not start process."); Console.WriteLine(ex); } } // Nhấn Enter để kết thúc (114)Trang 114 } } V.3.8 Kết thúc tiến trình Nếu khởi chạy tiến trình từ mã lệnh quản lý lớp Process , kết thúc tiến trình đối tượng Process mơ tả tiến trình Một có đối tượng Process mơ tả tiến trình cần kết thúc, cần gọi phương thức CloseMainWindow hay phương thức Kill() Phương thức CloseMainWindow gửi thơng điệp đến cửa sổ ứng dụng CloseMainWindow không kết thúc ứng dụng cửa sổ ứng dụng có cửa sổ bị vơ hiệu Với tình thế, CloseMainWindow trả false CloseMainWindow trả true thông điệp gửi thành công, không bảo đảm tiến trình thật kết thúc Phương thức Kill() kết thúc tiến trình lập tức; người dùng khơng có hội dừng việc kết thúc, tất liệu chưa lưu bị Ví dụ sau khởi chạy thể Notepad, đợi giây, sau kết thúc tiến trình Notepad Trước tiên, ví dụ kết thúc tiến trình CloseMainWindow Nếu CloseMainWindow trả false, tiến trình Notepad chạy sau CloseMainWindow gọi, ví dụ gọi Kill() buộc tiến trình Notepad kết thúc Có thể buộc CloseMainWindow trả false cách bỏ mặc hộp thoại File Open mở Chương trình TerminateProcessExample using System; using System.Threading; using System.Diagnostics; public class TerminateProcessExample { public static void Main() { // Tạo Process chạy notepad.exe using (Process process = Process.Start("notepad.exe")) { // Đợi giây kết thúc tiến trình Notepad (115)Trang 115 // Kết thúc tiến trình Notepad // Gửi thơng điệp đến cửa sổ if (!process.CloseMainWindow()) { // Không gửi thông điệp Kết thúc Notepad Kill process.Kill(); } else { // Thông điệp gửi thành công; đợi giây // để chứng thực việc kết thúc trước viện đến Kill if (!process.WaitForExit(2000)) { process.Kill(); } // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } } } V.4 Thực thi phương thức cách hiệu đối tượng WaitHandle Sử dụng lớp dẫn xuất từ WaitHandle để gọi thực thi phương thức Bằng phương thức RegisterWaitForSingleObject lớp ThreadPool, đăng ký thể ủy nhiệm WaitOrTimerCallback với thread-pool đối tượng dẫn xuất từ WaitHandle vào trạng thái signaled Có thể cấu hình thread-pool để thực thi phương thức lần hay tự động đăng ký lại phương thức WaitHandle vào trạng thái signaled Nếu WaitHandle trạng thái signaled gọi RegisterWaitForSingleObject, phương thức thực thi Phương thức Unregister đối tượng (116)Trang 116 (117)Trang 117 CHƯƠNG VI: ĐỒNG BỘ HĨA VI.1 Lý đồng hóa Trong hệ điều hành đa nhiệm cho phép nhiều công việc thực đồng thời Việc tồn lúc nhiều tiểu trình mơi trường dẫn đến tranh chấp, ngăn cản họat động lẫn tiểu trình Ví dụ : Với tiến trình đầu tạo lập tiểu trình có nội dung xử lý đồng nhất.: Mỗi tiểu trình tăng giá trị biến tồncục Count lên 250.000 lần Do Count tăng lên 1.000.000 lần tịan tiến trình Để hạn chế trình trạng tranh chấp tài nguyên cần có chế điều khiển tiểu trình truy xuất tài ngun cách thực đồng hóa tiểu trình Các trường hợp cần thực đồng hóa Khi tiểu trình truy xuất đến tài nguyên dùng chung , cần ý thực đồng hóa việc truy xuất tài nguyên để tránh xảy tranh chấp Các chế đồng hóa dựa ý tưởng cho phép tiểu trình truy xuất tài ngun thỏa điều kiện khơng có tranh chấp Những tiểu trình khơng hội đủ điều kiện để sử dụng tài nguyên hệ điều hành đặt vào trạng thái chờ (Không chiếm CPU), hệ điều hành ln kiểm sóat tình trạng truy xuất tài ngun tiểu trình khác để giải phóng kịp thời tiểu trình chờ vào thời điểm thích hợp VI.2 Các phương pháp đồng hóa Để thực chế đồng hóa , hệ điều hành sử dụng đối tượng đồng hóa gắn liền với tài nguyên để phản ánh tình trạng truy xuất tài nguyên Các đối tượng đồng hóa nhận trạng thái TRUE FALSE Khi đối tượng đồng hóa trạng thái TRUE tiểu trình phép truy cập tài ngun, ngược lại khơng (118)Trang 118 Khái niệm: Semaphore đối tượng đồng hóa lưu trữ biến đếm có giá trị từ đến Max, semaphore nhận trạng thái TRUE giá trị biến đếm > nhận trạng thái FALSE có giá trị biến đếm =0 Tình sử dụng: Sử dụng semaphore để kiểm soát việc cho phép số hữu hạn tiểu trình lúc truy xuất tài nguyên dùng chung Biến đếm đối tượng semaphore cho biết số tiểu trình truy xuất tài nguyên mà semaphore bảo vệ, biến đếm giảm có thêm tiểu trình truy xuất tài nguyên, ngược lại tăng giá trị lên có tiểu trình chấm dứt truy xuất tài nguyên Khi biến đếm đạt giá trị tài ngun bảo vệ, khơng tiểu trình ngịai Max tiểu trình đã đăng ký truy xuất Có thể dùng semaphore để đồng hóa tiểu trình khác tiến trình Cách tạo đối tượng Semaphore Class Semaphore Semaphore(int InitCount, int MaxCount) Đăng ký truy cập tài nguyên chung Phương thức WaitOne( ) Kết thúc truy cập tài nguyên chung Phương thức Release( ) Ví dụ: Tạo 10 tiểu trình thời điểm có tiểu trình truy cập tài nguyên chung: class SemaphoreTest { static Semaphore s = new Semaphore(3, 3); // Available=3; Capacity=3 static void Main() { for (int i = 0; i < 10; i++) { Thead thread = new Thread(new ThreadStart(Go)); thread.Start(); (119)Trang 119 static void Go() { while (true) { s.WaitOne(); Thread.Sleep(100); // Only threads can get here at once s.Release(); } } } VI.4 Phương pháp dùng lớp Monitor Một chế đồng hóa khác lớp Monitor Lớp cho phép tiểu trình đơn thu lấy khóa (lock) đối tượng cách gọi phương thức tĩnh Monitor.Enter Bằng cách thu lấy khóa trước truy xuất tài nguyên hay liệu dùng chung, ta chắn có tiểu trình truy xuất tài nguyên lúc Một hồn tất với tài ngun, tiểu trình giải phóng khóa để tiểu trình khác truy xuất phương thức tĩnh Monitor.Exit Khối mã gói lệnh lock tương đương với gọi Monitor.Enter vào khối mã này, gọi Monitor.Exit khối mã Tiểu trình chủ gọi Monitor.Wait để giải phóng lock đặt tiểu trình vào hàng chờ (wait queue) Các tiểu trình hàng chờ có trạng thái WaitSleepJoin tiếp tục block tiểu trình chủ gọi phương thức Pulse hay PulseAll lớp Monitor Phương thức Pulse di chuyển tiểu trình từ hàng chờ vào hàng sẵn sàng, cịn phương thức PulseAll di chuyển tất tiểu trình Khi tiểu trình di chuyển từ hàng chờ vào hàng sẵn sàng, thu lấy lock lần giải phóng (120)Trang 120 Chương trình ThreadSyncExample using System; using System.Threading; public class ThreadSyncExample { private static object consoleGate = new Object(); private static void DisplayMessage() { Console.WriteLine("{0} : Thread started, acquiring lock ", DateTime.Now.ToString("HH:mm:ss.ffff")); // Thu lấy chốt đối tượng consoleGate try { Monitor.Enter(consoleGate); Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), "Acquired consoleGate lock, waiting "); // Đợi Pulse gọi đối tượng consoleGate Monitor.Wait(consoleGate); Console.WriteLine("{0} : Thread pulsed, terminating.", DateTime.Now.ToString("HH:mm:ss.ffff")); } finally { Monitor.Exit(consoleGate); } } public static void Main() { // Thu lấy chốt đối tượng consoleGate lock (consoleGate) { // Tạo khởi chạy ba tiểu trình // (chạy phương thức DisplayMesssage) for (int count = 0; count < 3; count++) { (new Thread(new ThreadStart(DisplayMessage))).Start(); } } Thread.Sleep(1000); // Đánh thức tiểu trình chờ Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), (121)Trang 121 Console.ReadLine(); // Thu lấy chốt đối tượng consoleGate lock (consoleGate) { // Pulse tiểu trình chờ Monitor.Pulse(consoleGate); } // Đánh thức tất tiểu trình chờ Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), "Press Enter to pulse all waiting threads."); Console.ReadLine(); // Thu lấy chốt đối tượng consoleGate lock (consoleGate) { // Pulse tất tiểu trình chờ Monitor.PulseAll(consoleGate); } // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } VI.5 System.Threading.WaitHandle, bao gồm AutoResetEvent, ManualResetEvent Các lớp thông dụng khác dùng để đồng hóa tiểu trình lớp lớp System.Threading.WaitHandle, bao gồm AutoResetEvent, ManualResetEvent Thể lớp trạng thái signaled hay unsignaled Các tiểu trình sử dụng phương thức lớp liệt kê để vào trạng thái WaitSleepJoin đợi trạng thái hay nhiều đối tượng dẫn xuất từ WaitHandle biến thành signaled Phương Thức Mô Tả WaitAny() Tiểu trình gọi phương thức tĩnh vào trạng thái WaitSleepJoin đợi đối tượng WaitHandle thuộc mảng WaitHandle biến thành signaled Cũng định giá trị time-out (122)Trang 122 Phương Thức Mô Tả WaitSleepJoin đợi tất đối tượng WaitHandle một mảng WaitHandle biến thành signaled Bạn định giá trị time-out WaitOne() Tiểu trình gọi phương thức vào trạng thái WaitSleepJoin đợi đối tượng WaitHandle cụ thể biến thành signaled Điểm khác biệt lớp AutoResetEvent, ManualResetEvent, cách thức chúng chuyển trạng thái từ signaled thành unsignaled Lớp AutoResetEvent ManualResetEvent cục tiến trình Để hiệu AutoResetEvent, bạn gọi phương thức Set nó, phương thức giải phóng tiểu trình đợi kiện AutoResetEvent tự động trở trạng thái unsignaled Lớp ManualResetEvent phải chuyển đổi qua lại signaled unsignaled phương thức Set Reset Gọi Set ManualResetEvent đặt trạng thái signaled, giải phóng tất tiểu trình đợi kiện Chỉ gọi Reset làm cho ManualResetEvent trở thành unsignaled Sử dụng lớp dẫn xuất từ WaitHandle để gọi thực thi phương thức Bằng phương thức RegisterWaitForSingleObject lớp ThreadPool, đăng ký thể ủy nhiệm WaitOrTimerCallback với thread-pool đối tượng dẫn xuất từ WaitHandle vào trạng thái signaled Có thể cấu hình thread-pool để thực thi phương thức lần hay tự động đăng ký lại phương thức WaitHandle vào trạng thái signaled Nếu WaitHandle trạng thái signaled gọi RegisterWaitForSingleObject, phương thức thực thi Phương thức Unregister đối tượng (123)Trang 123 Lớp thường dùng làm kích hoạt AutoResetEvent, tự động chuyển sang trạng thái unsignaled sau trạng thái signaled Tuy nhiên, thay đổi trạng thái signaled theo ý muốn lớp ManualResetEvent hay Mutex Ví dụ trình bày cách sử dụng AutoResetEvent để kích hoạt thực thi phương thức có tên EventHandler: Chương trình EventExecutionExample using System.Threading; public class EventExecutionExample { // Phương thức thực thi AutoResetEvent vào trạng // thái signaled trình đợi hết thời gian (time-out) private static void EventHandler(object state, bool timedout) { // Hiển thị thông báo thích hợp cửa sổ Console // tùy vào trình đợi hết thời gian hay // AutoResetEvent trạng thái signaled if (timedout) { Console.WriteLine("{0} : Wait timed out.", DateTime.Now.ToString("HH:mm:ss.ffff")); } else { Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), state); } } public static void Main() { // Tạo AutoResetEvent trạng thái unsignaled AutoResetEvent[] autoEvent; autoEvent[0] = new AutoResetEvent(false); // Tạo thể ủy nhiệm WaitOrTimerCallback // tham chiếu đến phương thức tĩnh EventHandler // EventHandler gọi AutoResetEvent vào // trạng thái signaled hay trình đợi hết thời gian (124)Trang 124 // thụ lý kiện kích hoạt) Trong trường hợp // này, thông báo hiển thị string state = "AutoResetEvent signaled."; // Đăng ký thể ủy nhiệm để đợi AutoResetEvent vào // trạng thái signaled Thiết lập giá trị time-out giây RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(autoEvent, handler, state, 3000, false); Console.WriteLine("Press ENTER to signal the AutoResetEvent or enter \"Cancel\" to unregister the wait operation."); while (Console.ReadLine().ToUpper() != "CANCEL") { // Nếu "Cancel" không nhập vào Console, // AutoResetEvent vào trạng thái signal, // phương thức EventHandler thực thi // AutoResetEvent tự động trở trạng thái unsignaled autoEvent.Set(); } // Hủy bỏ việc đăng ký trình đợi Console.WriteLine("Unregistering wait operation."); handle.Unregister(null); // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } VI.6 Phương pháp Mutex Mutex cung cấp chế để đồng hóa q trình thực thi tiểu trình vượt qua biên tiến trình Một Mutex signaled khơng thuộc sở hữu tiểu trình Một tiểu trình giành quyền sở hữu Mutex lúc khởi dựng sử dụng phương thức liệt kê Quyền sở hữu Mutex giải phóng cách gọi phương thức Mutex.ReleaseMutex (ra hiệu Mutex cho phép tiểu trình khác thu lấy quyền sở hữu này) Ví dụ sử dụng Mutex có tên MutexExample để bảo đảm thể ví dụ thực thi Chương trình MutexExample using System; using System.Threading; (125)Trang 125 { public static void Main() { // Giá trị luận lý cho biết ứng dụng // có quyền sở hữu Mutex hay khơng bool ownsMutex; // Tạo lấy quyền sở hữu Mutex có tên MutexExample using (Mutex mutex = new Mutex(true, "MutexExample", out ownsMutex)) { // Nếu ứng dụng sở hữu Mutex, tiếp tục thực thi; // khơng, ứng dụng if (ownsMutex) { Console.WriteLine("This application currently owns the mutex named MutexExample Additional instances of this application will not run until you release the mutex by pressing Enter."); Console.ReadLine(); // Giải phóng Mutex.mutex.ReleaseMutex(); } else { Console.WriteLine("Another instance of this" + " application already owns the mutex named" + " MutexExample This instance of the" + " application will terminate."); } } // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); (126)Trang 126 CHƯƠNG VII: LẬP TRÌNH SOCKET BẤT ĐỒNG BỘ VII.1 Lập trình kiện Windows Trong lập trình kiện Windows, kiện xảy ra, phương thức gọi để thực thi dựa kiện hình đây: Hình VI.1: Lập trình kiện Windows Trong chương trước, lập trình Socket chế độ blocking Socket chế độ blocking chờ mãi hoàn thành nhiệm vụ Trong bị blocking chức khác chương trình khơng thực (127)Trang 127 VII.1.1 Sử dụng Event Delegate Event thông điệp gởi đối tượng mô tả hoạt động mà diễn Thơng điệp xác định hoạt động truyền liệu cần thiết cho hoạt động Event mơ tả hoạt động đó, chẳng hạn hoạt động click Button, hoạt động nhận gởi liệu Socket Event sender không cần thiết phải biết đối tượng điều khiển thơng điệp kiện gởi thơng qua hệ thống Windows Nó nhận kiện đăng ký với hệ thống Windows thông báo kiểu kiện mà nhận kiện muốn nhận hình minh họa sau: Hình VI.2: Gởi nhận kiện Windows (128)Trang 128 nghĩa delegate Sau phương thức hoàn tất, hệ thống Windows xử lý kiện xảy kiện kết thúc chương trình phát Ví dụ đơn giản sau mơ tả cách lập trình kiện Windows Forrm Chương trình WindowSample using System; using System.Drawing; using System.Windows.Forms; class WindowSample : Form { private TextBox data; private ListBox results; public WindowSample() { Text = "Sample Window Program"; Size = new Size(400, 380); Label label1 = new Label(); label1.Parent = this; label1.Text = "Enter text string:"; label1.AutoSize = true; label1.Location = new Point(10, 10); data = new TextBox(); data.Parent = this; data.Size = new Size(200, * Font.Height); data.Location = new Point(10, 35); results = new ListBox(); results.Parent = this; results.Location = new Point(10, 65); results.Size = new Size(350, 20 * Font.Height); Button checkit = new Button(); checkit.Parent = this; checkit.Text = "test"; checkit.Location = new Point(235, 32); checkit.Size = new Size(7 * Font.Height, * Font.Height); checkit.Click += new EventHandler(checkit_OnClick); } void checkit_OnClick(object obj, EventArgs ea) { results.Items.Add(data.Text); data.Clear(); } (129)Trang 129 Application.Run(new WindowSample()); } } Điểm chương trình EvenHandler đăng ký phương thức ButtonOnClick() cho đối tượng Button với kiện click: checkit.Click += new EventHandler(checkit_OnClick); Khi người dùng click button, điều khiển chương trình chyển đến phương thức ButtonOnClick() void checkit_OnClick(object obj, EventArgs ea) { results.Items.Add(data.Text); data.Clear(); } VII.1.2 Lớp AsyncCallback lập trình Windows Khi kiện kích hoạt delegate, NET cung cấp chế để kích hoạt delegate Lớp AsyncCallback cung cấp phương thức để bắt đầu chức bất đồng cung cấp phương thức delegate để gọi chức bất đồng kết thúc Tiến trình khác với cách lập trình kiện bản, kiện không phát sinh từ đối tượng Windows mà xuất phát từ phương thức khác chương trình Phương thức đăng ký delegate AsyncCallback để gọi phương thức hoàn tất chức Socket sử dụng phương thức định nghĩa lớp AsyncCallback phép chức mạng hoạt động cách bất đồng Nó phát tín hiệu cho hệ điều hành chức mạng hồn tất Trong mơi trường lập trình Windows, phương thức giúp cho ứng dụng khỏi bị treo chờ chức mạng hoàn tất VII.2 Sử dụng Socket bất đồng Đối tượng Socket có nhiều phương thức sử dụng lớp AsyncCallback để gọi phương thức khác chức mạng hồn tất Nó cho phép dùng tiếp tục xử lý kiện khác chờ cho chức mạng hịa thành cơng việc (130)Trang 130 ✓ Một phương thức Begin bắt đầu chức mạng đăng ký phương thức AsyncCallback ✓ Một phương thức End hoàn thành chức mạng phương thức AsyncCallback gọi Bảng sau cho biết phương thức bất đồng với Socket Mỗi phương thức Begin kết hợp với phương thức End để hoàn tất chức chúng: Phương thức bắt đầu Mô tả Phương thức kết thúc BeginAccept() Chấp nhận kết nối EndAccept() BeginConnect() Kết nối đến thiết bị xa EndConnect() BeginReceive() Nhận liệu từ Socket EndReceive() BeginReceiveFrom() Nhận liệu từ thiết bị xa EndReceiveFrom() BeginSend() Gởi liệu từ Socket EndSend() BeginSendTo() Gởi liệu đến thiết bị xa EndSendTo VII.2.1 Thành lập kết nối Phương thức dùng để thành lập kết nối với thiết bị xa phụ thuộc vào chương trình đóng vai trị Server hay Client Nếu Server phương thức BeginAccept() dùng, Client phương thức BeginConnect() dùng VII.2.1.1 Phương thức BeginAccept() EndAccept() Để chấp nhận kết nối từ thiết bị xa, phương thức BeginAccetp() dùng Định dạng sau: IAsyncResult BeginAccept(AsyncCallback callback, object state) Phương thức BeginAccept() nhận hai đối số: tên phương thức AsyncCallback dùng để kết thúc phương thức đối tượng state dùng để truyền thông tin phương thức bất đồng Phương thức dùng sau: Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); (131)Trang 131 sock.Listen(5); sock.BeginAccept(new AsyncCallback(CallAccept), sock); Đoạn code tạo đối tượng Socket gán đến địa IP cục port TCP cục để chấp nhận kết nối Phương thức BeginAccept() định nghĩa delegate dùng có kết nối Socket Tham số cuối truyền vào phương thức BeginAccept() Socket ban đầu tạo Sau phương thức BeginAccept() kết thúc, phương thức AsyncCallback định nghĩa gọi kết nối xảy Phương thức AsyncCallback phải bao gồm phương thức EndAccept() để kết thúc việc chấp nhận Socket Sau định dạng phương thức EndAccept(): Socket EndAccept(IAsyncResult iar); Đối tượng IasyncResult truyền giá trị bất đồng từ phương thức BeginAccept() đến phương thức EndAccept() Cũng giống phương thức đồng Accept(), phương thức EndAccept() trả đối tượng Socket dùng để kết nối với Client Tất thông tin liên lạc vói thiết bị xa thực thơng qua đối tượng Socket private static void CallAccept(IAsyncResult iar) { Socket server = (Socket)iar.AsyncState; Socket client = server.EndAccept(iar); } Tên phương thức AsyncCallback phải giống với tên phương thức dùng làm tham số phương thức BeginAccept() Bước phương thức nhận Socket ban đầu Server Việc thực cách dùng property AsyncState lớp IasyncResult Property có kiểu object phải ép kiểu sang đối tượng Socket Sau Socket ban đầu lấy về, phương thức EndAccept() lấy đối tượng Socket để truyền thông với thiết bị xa Đối số truyền vào của phương thức EndAccept() phải giống với đối số truyền vào phương thức (132)Trang 132 Đối tượng Client Socket sau tạo ra, dùng giống đối tượng Socket khác, sử dụng phương thức đồng bất đồng để gởi nhận liệu VII.2.1.2 Phương thức BeginConnect() EndConnect() Để ứng dụng Client kết nối đến Server xa phương thức bất đồng ta phải dùng phương thức BeginConnect() Định dạng phương thức nhu sau: IAsyncResult BeginConnect(EndPoint ep, AsyncCallback callback, Object state) Tham số truyền vào phương thức BeginConnect() EndPoint thiết bị xa cần kết nối đến Giống phương thức BeginAccept(), phương thức BeginConnect() tên phương thức delegate AsyncCallback tham chiếu đến phương thức gọi kết nối hoàn tất Tham số cuối đối tượng state, truyền vào phương thức EndConnect() để truyền tải liệu cần thiết Đây đoạn code ví dụ phương thức BeginConnect(): Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050); newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock); Đoạn code tạo đối tượng Socket newsock đối tượng IPEndPoint iep cho thiết bị xa Phương thức BeginConnect() tham chiếu đến phương thức AsyncCallback (Connect) truyền đối tượng Socket ban đầu newsock đến phương thức AsyncCallback Khi kết nối thành lập phương thức delegate AsyncCallback tham chiếu tới gọi Phương thức dùng phương thức EndConnect() để hoàn thành việc kết nối Định dạng phương thức EndConnect() sau: EndConnect(IAsyncResult iar) Đối tượng IasyncResult truyền giá trị từ phương thức BeginConnect() Sau ví dụ cách dùng phương thức này: (133)Trang 133 Socket sock = (Socket)iar.AsyncState; try { sock.EndConnect(iar); } catch (SocketException) { Console.WriteLine("Unable to connect to host"); } } Đầu tiên ta lấy Socket ban đầu sử dụng phương thức BeginConnect(), Socket lấy nhờ vào thuộc tính object đối tượng IAsyncResult truyền vào phương thức tham chiếu delegate AsyncCallback Sau Socket ban đầu tạo ra, phương thức EndConnect() gọi, đối số truyền vào phương thức EndConnect() đối tượng IAsyncResult chỏ ngược trở lại phương thức BeginConnect() ban đầu Bởi thiết bị xa kết nối khơng nên tốt đặt vào khối try – catch VII.2.2 Gởi liệu VII.2.2.1 Phương thức BeginSend() phương thức EndSend() Phương thức BeginSend() cho phép gởi liệu đến Socket kết nối Định dạng phương thức sau: IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags sockflag, AsyncCallback callback, object state) Hầu hết đối số phương thức giống đối số phương thức đồng Send() có thêm hai đối số AsyncCallback object ✓ Đối số AsyncCallback: xác định phương thức gọi phương thức BeginSend() thực thành công ✓ Đối số object: gởi thơng tin tình trạng đến phương thức EndSend() Sau ví dụ phương thức BeginSend(): sock.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendData), sock); (134)Trang 134 Phương thức EndSend() hoàn tất việc gởi liệu Định dạng phương thức sau: int EndSend(IAsyncResult iar) Phương thức EndSend() trả số byte gởi thành cơng thừ Socket Sau ví dụ phương thức EndSend(): private static void SendData(IAsyncResult iar) { Socket server = (Socket)iar.AsyncState; int sent = server.EndSend(iar); } Socket ban đầu tạo lại cách dùng thuộc tính AsyncState đối tượng IAsyncResult VII.2.2.2 Phương thức BeginSendTo() EndSendTo() Phương thức BeginSendTo() dùng với Socket phi kết nối để bắt đầu truyền tải liệu bất đồng tói thiết bị xa Định dạng phương thức BeginSendTo() sau: IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags sockflag, EndPoint ep, AsyncCallback callback, object state) Các đối số phương thức BeginSendTo() giống đối số phương thức SendTo() Sau ví dụ phương thức BeginSendTo(): IPEndPoint iep = new IPEndPoint(IPAddress.Parse("192.168.1.6"), 9050); sock.BeginSendTo(data, 0, data.Length, SocketFlags.None, iep, new AsynCallback(SendDataTo), sock); Phương thức SendDataTo() delegate AsyncCallback tham chiếu đến truyền vào làm đối số phương thức BeginSendTo() Phương thức thực thi liệu bắt đầu gởi từ Socket Phương thức EndSendTo() thực thi trình gởi liệu kết thúc Định dạng phương thức sau: (135)Trang 135 Đối số truyền vào phương thức EndSendTo() đối tượng IAsyncResult, đối tượng mang giá trị truyền từ phương thức BeginSendTo() Khi trình gởi liệu bất đồng kết thúc phương thức EndSendTo() trả số byte mà thực gởi VII.2.3 Nhận liệu VII.2.3.1 Phương thức BeginReceive(), EndReceive, BeginReceiveFrom(), EndReceiveFrom() Cách sử dụng phương thức tương tự cách sử dụng phương thức: BeginSend(), EndSend(), BeginSendTo() EndSendTo() VII.2.4 Chương trình WinForm gởi nhận liệu Client Server VII.2.4.1 Chương trình Server (136)Trang 136 Hình VI.4: Mơ hình chương trình Server VII.2.4.3 Lớp ServerProgram using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; namespace Server { class ServerProgram { private IPAddress serverIP; public IPAddress ServerIP { get { return serverIP; } set { this.serverIP = value; } } private int port; public int Port { (137)Trang 137 { return this.port; } set { this.port = value; } } //delegate để set liệu cho Control //Tại thời điểm ta chưa biết liệu hiển thị vào đâu nên ta phải dùng delegate public delegate void SetDataControl(string Data); public SetDataControl SetDataFunction = null; Socket serverSocket = null ; IPEndPoint serverEP = null; Socket clientSocket = null; //buffer để nhận gởi liệu byte[] buff = new byte[1024]; //Số byte thực nhận int byteReceive = 0; string stringReceive = ""; public ServerProgram(IPAddress ServerIP, int Port) { this.ServerIP = ServerIP; this.Port = Port; } //Lắng nghe kết nối public void Listen() { serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverEP = new IPEndPoint(ServerIP, Port); //Kết hợp Server Socket với Local Endpoint serverSocket.Bind(serverEP); //Lắng nghe kết nối Server Socket //-1: không giới hạn số lượng client kết nối đến serverSocket.Listen(-1); SetDataFunction("Dang cho ket noi"); //Bắt đầu chấp nhận Client kết nối đến serverSocket.BeginAccept(new AsyncCallback(AcceptScoket), serverSocket); (138)Trang 138 //Hàm callback chấp nhận Client kết nối private void AcceptScoket(IAsyncResult ia) { Socket s = (Socket)ia.AsyncState; //Hàm Accept block server lại chờ Client kết nối đến //Sau Client kết nối đến trả socket chứa thông tin Client clientSocket = s.EndAccept(ia); string hello = "Hello Client"; buff = Encoding.ASCII.GetBytes(hello); SetDataFunction("Client " + clientSocket.RemoteEndPoint.ToString() + "da ket noi den"); clientSocket.BeginSend(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(SendData), clientSocket); } private void SendData(IAsyncResult ia) { Socket s = (Socket)ia.AsyncState; s.EndSend(ia); //khởi tạo lại buffer để nhận liệu buff = new byte[1024]; //Bắt đầu nhận liệu s.BeginReceive(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(ReceiveData), s); } public void Close() { clientSocket.Close(); serverSocket.Close(); } private void ReceiveData(IAsyncResult ia) { Socket s = (Socket)ia.AsyncState; try { //Hàm EmdReceive bị block có liệu TCP buffer byteReceive = s.EndReceive(ia); } catch { (139)Trang 139 this.Close(); SetDataFunction("Client ngat ket noi"); this.Listen(); return; } //Nếu Client shutdown hàm EndReceive trả if (byteReceive == 0) { Close(); SetDataFunction("Clien dong ket noi"); } else { stringReceive = Encoding.ASCII.GetString(buff, 0, byteReceive); SetDataFunction(stringReceive); //Sau Server nhận liệu xong bắt đầu gởi liệu xuống cho Client s.BeginSend(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(SendData), s); } } } } VII.2.4.4 Lớp ServerForm using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net; using System.Net.Sockets; namespace Server { public partial class ServerForm : Form { (140)Trang 140 public ServerForm() { InitializeComponent(); CheckForIllegalCrossThreadCalls = false; server.SetDataFunction = new ServerProgram.SetDataControl(SetData); } private void SetData(string Data) { this.listBox1.Items.Add(Data); } private void cmdStart_Click(object sender, EventArgs e) { server.Listen(); } private void cmdStop_Click(object sender, EventArgs e) { this.server.Close(); SetData("Server dong ket noi"); } private void ServerForm_Load(object sender, EventArgs e) { } } } (141)Trang 141 Hình VI.5: Giao diện Client (142)Trang 142 Hình VI.6: Mơ hình chương trình Client VII.2.5.2 Lớp ClientProgram using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; namespace Client { class ClientProgram { //delegate để set liệu cho Control //Tại thời điểm ta chưa biết liệu hiển thị vào đâu nên ta phải dùng delegate public delegate void SetDataControl(string Data); public SetDataControl SetDataFunction = null; //buffer để nhận gởi liệu byte[] buff = new byte[1024]; //Số byte thực nhận int byteReceive = 0; //Chuỗi nhận (143)Trang 143 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint serverEP = null; //Lắng nghe kết nối public void Connect(IPAddress serverIP, int Port) { serverEP = new IPEndPoint(serverIP, Port); //Việc kết nối nhiều thời gian nên phải thực bất đồng serverSocket.BeginConnect( serverEP, new AsyncCallback(ConnectCallback), serverSocket); } //Hàm callback chấp nhận Client kết nối private void ConnectCallback(IAsyncResult ia) { //Lấy Socket thực việc kết nối bất đồng Socket s = (Socket)ia.AsyncState; try { //Set liệu cho Control SetDataFunction("Đang chờ kết nối"); //Hàm EndConnect bị block kết nối thành công s.EndConnect(ia); SetDataFunction("Kết nối thành công"); } catch { SetDataFunction("Kết nối thất bại"); return; } //Ngay sau kết nối xong bắt đầu nhận câu chào từ Server gởi xuống s.BeginReceive(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(ReceiveData), s); } private void ReceiveData(IAsyncResult ia) { Socket s = (Socket)ia.AsyncState; byteReceive = s.EndReceive(ia); stringReceive = Encoding.ASCII.GetString(buff, 0, byteReceive); SetDataFunction(stringReceive); (144)Trang 144 private void SendData(IAsyncResult ia) { Socket s = (Socket)ia.AsyncState; s.EndSend(ia); //khởi tạo lại buffer để nhận liệu buff = new byte[1024]; //Bắt đầu nhận liệu s.BeginReceive(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(ReceiveData), s); } //Hàm ngắt kết nối public bool Disconnect() { try { //Shutdown Soket kết nối đến Server serverSocket.Shutdown(SocketShutdown.Both); serverSocket.Close(); return true; } catch { return false; } } //Hàm gởi liệu public void SendData(string Data) { buff = Encoding.ASCII.GetBytes(Data); serverSocket.BeginSend(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(SendToServer), serverSocket); } //Hàm CallBack gởi liệu private void SendToServer(IAsyncResult ia) { Socket s = (Socket)ia.AsyncState; s.EndSend(ia); buff = new byte[1024]; s.BeginReceive(buff, 0, buff.Length, SocketFlags.None, new AsyncCallback(ReceiveData), s); (145)Trang 145 } VII.2.5.3 Lớp ClientForm using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net; using System.Net.Sockets; namespace Client { public partial class ClientForm : Form { ClientProgram client = new ClientProgram(); public ClientForm() { InitializeComponent(); CheckForIllegalCrossThreadCalls = false; client.SetDataFunction = new ClientProgram.SetDataControl(SetData); } private void SetData(string Data) { this.listBox1.Items.Add(Data); } private void cmdConnect_Click(object sender, EventArgs e) { client.Connect(IPAddress.Parse(this.txtServerIP.Text), int.Parse(this.txtPort.Text)); } private void cmdDisconnect_Click(object sender, EventArgs e) { client.Disconnect(); } private void cmdSend_Click_1(object sender, EventArgs e) { client.SendData(this.txtInput.Text); this.txtInput.Text = ""; (146)Trang 146 } VII.3 Lập trình Socket bất đồng sử dụng tiểu trình VII.3.1 Lập trình sử dụng hàng đợi gởi hàng đợi nhận thông điệp Trong cách lập trình này, dùng hai hàng đợi, hàng đợi gởi hàng đợi nhận để thực việc gởi nhận liệu Lớp Queue nằm namespace System.Collections giúp ta thực việc Queue inQueue = new Queue(); Queue outQueue = new Queue(); Hai phương thức quan trọng lớp Queue dùng lập trình mạng EnQueue() DeQueue() Phương thức EnQueue() đưa đối tượng vào hàng đợi phương thức DeQueue() lấy đối tượng từ hàng đợi Ý tưởng phương pháp lập trình ta dùng hai vịng lặp vơ hạn để kiểm tra hàng đợi gởi hàng đợi nhận, hàng đợi gởi có liệu liệu gởi ngồi mạng thơng qua Socket, tương tự hàng đợi nhận có liệu lấy liệu xử lý private void Send() { while (true) { if (OutQ.Count > 0) { streamWriter.WriteLine(OutQ.Dequeue().ToString()); streamWriter.Flush(); } } } private void Receive() { string s; while (true) { s = streamReader.ReadLine(); InQ.Enqueue(s); } } Để chạy song song hai vịng lặp vơ hạn ta phải tạo hai tiểu trình riêng, vịng lặp vơ hạn chạy tiểu trình riêng biệt hai vịng lặp vơ hạn chạy tiến trình làm công việc khác (147)Trang 147 Thread tSend = new Thread(new ThreadStart(Send)); tSend.Start(); Thread tReceive = new Thread(new ThreadStart(Receive)); tReceive.Start(); Ngoài ra, ta cịn sử dụng tiểu trình khác thực việc kết nối nhằm tránh gây treo tiến trình Thread tConnect = new Thread(new ThreadStart(WaitingConnect)); tConnect.Start(); Việc điều khiển kết nối thực tiểu trình khác xử lý phương thức WaitingConnect(): private void WaitingConnect() { tcpListener = new TcpListener(1001); tcpListener.Start(); socketForClient = tcpListener.AcceptSocket(); if (socketForClient.Connected) { MessageBox.Show("Client Connected"); netWorkStream = new NetworkStream(socketForClient); streamReader = new StreamReader(netWorkStream); streamWriter = new StreamWriter(netWorkStream); tSend = new Thread(new ThreadStart(Send)); tSend.Start(); tReceive = new Thread(new ThreadStart(Receive)); tReceive.Start(); } else { MessageBox.Show("Ket noi khong cong"); } (148)Trang 148 Việc nhập liệu thực phương thức InputData() public void InputData(string s) { InQ.Enqueue(s); OutQ.Enqueue(s); } Phương thức đơn giản đưa liệu nhập vào hàng đợi nhận để hiển thị liệu lên hình đưa liệu nhập vào hàng đợi gởi để gởi mạng Lớp ServerObject using System; using System.IO; using System.Windows.Forms; using System.Threading; using System.Collections; using System.Net.Sockets; using System.Collections.Generic; using System.Text; namespace Server { class ServerObject { Thread tSend, tReceive, tConnect; public Queue InQ = new Queue(); public Queue OutQ = new Queue(); private TcpListener tcpListener; Socket socketForClient; private NetworkStream netWorkStream; private StreamWriter streamWriter; private StreamReader streamReader; public void CreateConnection() { tConnect = new Thread(new ThreadStart(WaitingConnect)); tConnect.Start(); } private void WaitingConnect() { (149)Trang 149 tcpListener.Start(); socketForClient = tcpListener.AcceptSocket(); if (socketForClient.Connected) { MessageBox.Show("Client Connected"); netWorkStream = new NetworkStream(socketForClient); streamReader = new StreamReader(netWorkStream); streamWriter = new StreamWriter(netWorkStream); tSend = new Thread(new ThreadStart(Send)); tSend.Start(); tReceive = new Thread(new ThreadStart(Receive)); tReceive.Start(); } else { MessageBox.Show("Ket noi khong cong"); } //socketForClient.Close(); } private void Send() { while (true) { if (OutQ.Count > 0) { streamWriter.WriteLine(OutQ.Dequeue().ToString()); streamWriter.Flush(); } } } private void Receive() { string s; while (true) { s = streamReader.ReadLine(); InQ.Enqueue(s); (150)Trang 150 public void InputData(string s) { InQ.Enqueue(s); OutQ.Enqueue(s); } } } Lớp ClientObject using System; using System.Windows.Forms; using System.Collections; using System.Net.Sockets; using System.Threading; using System.IO; using System.Collections.Generic; using System.Text; namespace Client { class ClientObject { Thread tSend, tReceive, tConnect; public Queue InQ = new Queue(); public Queue OutQ = new Queue(); private TcpClient socketForServer; private NetworkStream networkStream; private StreamWriter streamWriter; private StreamReader streamReader; public void Connect() { tConnect = new Thread(new ThreadStart(WaitConnect)); tConnect.Start(); } public void WaitConnect() { try (151)Trang 151 socketForServer = new TcpClient("localhost", 1001); } catch { MessageBox.Show("Ket noi that bai"); } networkStream = socketForServer.GetStream(); streamReader = new StreamReader(networkStream); streamWriter = new StreamWriter(networkStream); tSend = new Thread(new ThreadStart(Send)); tSend.Start(); tReceive = new Thread(new ThreadStart(Receive)); tReceive.Start(); } private void Send() { while (true) { if (OutQ.Count > 0) { streamWriter.WriteLine(OutQ.Dequeue().ToString()); streamWriter.Flush(); } } } private void Receive() { string s; while (true) { s = streamReader.ReadLine(); InQ.Enqueue(s); } } public void InputData(string s) { InQ.Enqueue(s); OutQ.Enqueue(s); } (152)Trang 152 VII.3.2 Lập trình ứng dụng nhiều Client Một khó khăn lớn nhà lập trình mạng khả xử lý nhiều Client kết nối đến lúc Để ứng dụng server xử lý với nhiều Client đồng thời Client kết nối tới phải xử lý tiểu trình riêng biệt Mơ hình xử lý sau: Hình VI.7: Mơ hình xử lý Server nhiều Client Chương trình Server tạo đối tượng Socket chương trình chính, Client kết nối đến, chương trình tạo tiểu trình riêng biệt để điều khiển kết nối Bởi phương thức Accept() tạo đối tượng Socket cho kết nối nên đối tượng sử dùng để thông tin liên lạc với Client tiểu trình Socket ban đầu tự chấp nhận kết nối khác Chương trình ThreadedTcpSrvr using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Net; (153)Trang 153 class ThreadedTcpSrvr { private TcpListener client; public ThreadedTcpSrvr() { client = new TcpListener(5000); client.Start(); Console.WriteLine("Dang cho client "); while (true) { while (!client.Pending()) { Thread.Sleep(1000); } ConnectionThread newconnection = new ConnectionThread(); newconnection.threadListener = this.client; Thread newthread = new Thread(new ThreadStart(newconnection.HandleConnection)); newthread.Start(); } } public static void Main() { ThreadedTcpSrvr server = new ThreadedTcpSrvr(); } } class ConnectionThread { public TcpListener threadListener; private static int connections = 0; public void HandleConnection() { int recv; byte[] data = new byte[1024]; TcpClient client = threadListener.AcceptTcpClient(); NetworkStream ns = client.GetStream(); connections++; Console.WriteLine("Client moi duoc chap nhan, Hien tai co {0} ket noi", connections); (154)Trang 154 while (true) { data = new byte[1024]; recv = ns.Read(data, 0, data.Length); if (recv == 0) break; ns.Write(data, 0, recv); } ns.Close(); client.Close(); connection ; Console.WriteLine("Client disconnected: {0} active connections", connections); (155)Trang 155 CHƯƠNG VIII: LẬP TRÌNH VỚI CÁC GIAO THỨC VIII.1 LẬP TRÌNH VỚI GIAO THỨC ICMP VIII.1.1 Giao thức ICMP ICMP định nghĩa RFC 792, giao thức giúp xác định lỗi cac thiết bị mạng ICMP sử dụng IP để truyền thơng mạng Mặc dù dùng IP ICMP hoàn toàn giao thức độc lập với TCP, UDP Giao thức tầng gói tin IP xác định phần liệu sử dụng trường Type Các gói tin ICMP xác định trường Type gói tin IP, tồn gói tin ICMP kết hợp với phần liệu gói tin IP Định dạng gói tin ICMP IP VIII.1.1.1 Định dạng gói tin ICMP Tương tự TCP UDP, ICMP dùng đặc tả định dạng gói tin đặc biệt để xác định thơng tin Các gói tin ICMP gồm trường sau: Trường Type: kích thước byte, xác định loại thông điệp ICMP gói tin Nhiều loại gói tin ICMP dùng để gởi thông điệp điều khiển đến thiết bị xa Mỗi loại thơng điệp có định dạng riêng liệu cần thiết (156)Trang 156 Trường Checksum: kích thước byte, trường đảm bảo gói tin ICMP đến đích mà khơng bị hư hại Trường Message: có nhiều byte, byte chứa thành phần liệu cần thiết cho kiểu thông điệp ICMP Trường Message thường chứa đựng thông tin gởi nhận từ thiết bị xa Nhiều kiểu thông điệp ICMP định nghĩa trường Message Identifier số Sequense Các trường dùng để định danh nhât gói tin ICMP đến thiết bị VIII.1.1.2 Các tường Type gói tin ICMP Có nhiều kiểu gói tin ICMP, kiểu gói tin ICMP định nghĩa byte trường Type Bảng sau liệt kê danh sách kiểu ICMP ban đầu định nghĩa RFC 792 Type Code Mô Tả 0 Echo reply 3 Destination unreachable 4 Source quench 5 Redirect 8 Echo request 11 Time exceeded 12 Parameter problem 13 Timestamp request 14 Timestamp reply 15 Information request 16 Information reply Các trường Type gói tin ICMP Từ phát hành RFC 792 vào tháng năm 1981, nhiều trường ICMP tạo Các gói tin ICMP dùng cho việc thông báo lỗi mạng Những mô tả sau hay dùng gói tin ICMP: VIII.1.1.3 Echo Request and Echo Reply Packets (157)Trang 157 trên mạng Đây nhân lệnh ping hay dùng để kiểm tra tình trạng thiết bị mạng Gói tin Echo Request dùng Type ICMP với giá trị code Phần liệu Message gồm thành phần sau: ➢ byte Indentifier: xác định gói tin Echo Request ➢ byte số Sequence: cung cấp thêm định danh cho gói tin gói tin ICMP dòng byte chứa liệu trả gởi thiết bị nhận Khi thiết bị nhận nhận gói tin Echo Request, phải trả lời với gói tin Echo Reply, trường Type ICMP Gói tin Echo Reply phải chứa Identifier số Sequence gói tin Echo Request tương ứng Phần giá trị liệu phải giống gói tin Echo Request VIII.1.1.4 Gói tin Destination Unreachable Gói tin Destination Unreachable với trường Type thường trả thiết bị Router sau nhận gói tin IP mà khơng thể chuyển tới đích Phần liệu gói tin Destination Unreachable chứa IP Header cộng với 64 bit giản đồ Trong gói tin, trường Code xác định lý gói tin chuyển router Bảng sau cho biết số giá trị Code gặp phải: Code Mô Tả 0 Network unreachable 1 Host unreachable 2 Protocol unreachable 3 Port unreachable 4 Fragmentation needed and DF flag set 5 Source route failed 6 Destination network unknown 7 Destination host unknown 8 Source host isolated 9 Communication with destination network prohibited 10 Communication with destination host prohibited 11 Network unreachable for type of service (158)Trang 158 Các giá trị Code Destination Unreachable VIII.1.1.5 Gói tin Time Exceeded Gói tin Time Exceeded với trường Type ICMP 11 công cụ quan trọng để khắc phục vấn đề mạng Nó cho biết gói tin IP vượt giá trị thời gian sống (TTL) định nghĩa IP Header Mỗi gói tin IP đến router, giá trị TTL giảm Nếu giá trị TTL trước gói tin IP đến đích, router cuối nhận gói tin với giá trị TTL phải gởi gói tin Time Exceeded cho thiết bị gởi Lệnh tracert hay dùng gói tin VIII.1.2 Sử dụng Raw Socket Bởi gói tin ICMP không dùng TCP UDP, nên ta không dùng lớp hellper TcpClient, UdpClient Thay vậy, ta phải sử dụng Raw Socket, tính lớp Socket Raw Socket cho phép định nghĩa riêng gói tin mạng phía tầng IP Tất nhiên tả phải làm tất việc tay tạo tất trường gói tin ICMP khơng dùng thư viện có sẵn NET làm với TCP UDP VIII.1.2.1 Định dạng Raw Socket Để tạo Raw Socket, ta phải dùng SocketType.Raw Socket tạo Có nhiều giá trị ProtocolType ta dùng với Raw Socket liệt kê bảng sau: Giá Trị Mô Tả Ggp Gateway-to-Gateway Protocol Icmp Internet Control Message Protocol Idp IDP Protocol Igmp Internet Group Management Protocol IP A raw IP packet Ipx Novell IPX Protocol ND Net Disk Protocol Pup Xerox PARC Universal Protocol (PUP) Raw A raw IP packet (159)Trang 159 Giá Trị Mô Tả SpxII Novell SPX Version Protocol Unknown An unknown protocol Unspecified An unspecified protocol Các giá trị Raw Socket ProtocolType Những giao thức liệt kê cho Raw Socket cho phép thư viện NET tạo gói tin IP bên Bằng cách sử dụng giá trị ProtocolType.Icmp, gói tin IP tạo Socket xác định giao thức tầng ICMP (Trường Type 1) Điều cho phép thiết bị xa nhận gói tin gói tin ICMP xử lý cách tương ứng Sau lệnh tạo Socket cho gói tin ICMP: Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); VIII.1.2.2 Gởi gói tin Raw Bởi ICMP giao thức phi kết nối, nên ta kết nối socket đến port cục để gởi gói tin sử dụng phương thức Connect() để kết nối đến thiết bị xa Ta phải dùng phương thức SendTo() để đối tượng IPEndPoint địa đích ICMP khơng dùng port giá trị port đối tượng IPEndPoint khơng quan trọng Ví dụ sau tạo đối tượng IPEndPoint đích khơng có port gởi gói tin đến nó: IPEndPoint iep = new IPEndPoint(IPAddress.Parse("192.168.1.2"), 0); sock.SendTo(packet, iep); VIII.1.2.3 Nhận gói tin Raw Nhận liệu từ Raw Socket khó gởi liệu từ Raw Socket Để nhận liệu từ Raw Socket, ta phải dụng phương thức ReceiveFrom() Bởi Raw Socket khơng định nghĩa giao thức tầng cao hơn, liệu trả từ lần gọi phương thức ReceiveFrom() chứa toàn nội dung gói tin IP Dữ liệu gói tin IP byte thứ 20 để lấy liệu header gói tin ICMP, ta phải bắt đầu đọc từ byte thứ 20 liệu nhận (160)Trang 160 Như đề cập trước, Raw Socket khơng tự động định dạng gói tin ICMP ta phải tự định dạng Lớp ICMP phải định nghĩa biến cho thành phần gói tin ICMP Các biến định nghĩa mô tả gói tin ICMP Các thành phần lớp ICMP điển hình Biến Dữ Liệu Kích Thước Kiểu Type byte Byte Code byte Byte Checksum bytes Unsigned 16-bit integer Message multibyte Byte array class ICMP { public byte Type; public byte Code; public UInt16 Checksum; public int MessageSize; public byte[] Message = new byte[1024]; public ICMP() { } } Sau gởi gói tin ICMP nhận gói tin ICMP trả từ thiết bị xa Để dễ dàng giải mã nội dung gói tin, nên tạo phương thức tạo lập khác lớp ICMP nhận mảng byte Raw ICMP đặt giá trị vào phần liệu thích hợp lớp: public ICMP(byte[] data, int size) { Type = data[20]; Code = data[21]; Checksum = BitConverter.ToUInt16(data, 22); MessageSize = size - 24; Buffer.BlockCopy(data, 24, Message, 0, MessageSize); } (161)Trang 161 Sua tạo đối tượng ICMP với liệu nhận được, ta lấy thành phần liệu ra: int recv = ReceiveFrom(data, ref ep); ICMP response = new ICMP(data, recv); Console.WriteLine("Received ICMP packet:"); Console.WriteLine(" Type {0}", response.Type); Console.WriteLine(" Code: {0}", response.Code); Int16 Identifier = BitConverter.ToInt16(response.Message, 0); Int16 Sequence = BitConverter.ToInt16(response.Message, 2); Console.WriteLine(" Identifier: {0}", Identifier); Console.WriteLine(" Sequence: {0}", Sequence); stringData = Encoding.ASCII.GetString(response.Message, 4, response.MessageSize - 4); Console.WriteLine(" data: {0}", stringData); VIII.1.4 Tạo gói tin ICMP Sau đối tượng ICMP tạo phần liệu gói tin định nghĩa, ta chưa thể gởi trực tiếp đối tượng ICMP phương thức SendTo() được, phải chuyển thành mảng byte, việc thực nhờ vào phương thức Buffer.BlockCopy(): public byte[] getBytes() { byte[] data = new byte[MessageSize + 9]; Buffer.BlockCopy(BitConverter.GetBytes(Type), 0, data, 0, 1); Buffer.BlockCopy(BitConverter.GetBytes(Code), 0, data, 1, 1); Buffer.BlockCopy(BitConverter.GetBytes(Checksum), 0, data, 2, 2); Buffer.BlockCopy(Message, 0, data, 4, MessageSize); return data; } VIII.1.5 Tạo phương thức Checksum Có lẽ phần khó khăn việc tạo gói tin ICMP tính tốn giá trị checksum gói tin Cách dễ để thực điều tạo mọt phương thức để tính checksum đặt đặt vào lớp ICMP để sử dụng chương trình ứng dụng ICMP public UInt16 getChecksum() { UInt32 chcksm = 0; (162)Trang 162 int packetsize = MessageSize + 8; int index = 0; while (index < packetsize) { chcksm += Convert.ToUInt32(BitConverter.ToUInt16(data, index)); index += 2; } chcksm = (chcksm >> 16) + (chcksm & 0xffff); chcksm += (chcksm >> 16); return (UInt16)(~chcksm); } Bởi giá trị checksum sử dụng mộ số 16 bit, thuật toán đọc khúc byte gói tin ICMP lúc (sử dụng phương thức ToUInt16() lớp BitConverter) thực phép toán số học cần thiết byte Giá trị trả số nguyên dương 16 bit Để sử dụng giá trị checksum chương trình ứng dụng ICMP, điền tất thành phần liệu, thiết lập thành phần Checksum thành 0, gọi phương thức getChecksum() để tính tốn checksum gói tin ICMP sau đặt kết vào thành phần Checksum gói tin: packet.Checksum = 0; packet.Checksum = packet.getChecksum(); Sau thành phần Checksum tính tốn, gói tin sẵn sàng gởi ngồi thiết bị đích dùng phương thức SendTo() Khi gói tin ICMP nhận từ thiết bị xa, phải lấy phần Checksum so sánh với giá trị checksum tính được, giá trị khơng khớp có lỗi xảy q trình truyền gói tin phải truyền lại VIII.1.6 Lớp ICMP hoàn chỉnh using System; using System.Net; using System.Text; class ICMP { public byte Type; public byte Code; (163)Trang 163 public int MessageSize; public byte[] Message = new byte[1024]; public ICMP() { } public ICMP(byte[] data, int size) { Type = data[20]; Code = data[21]; Checksum = BitConverter.ToUInt16(data, 22); MessageSize = size - 24; Buffer.BlockCopy(data, 24, Message, 0, MessageSize); } public byte[] getBytes() { byte[] data = new byte[MessageSize + 9]; Buffer.BlockCopy(BitConverter.GetBytes(Type), 0, data, 0, 1); Buffer.BlockCopy(BitConverter.GetBytes(Code), 0, data, 1, 1); Buffer.BlockCopy(BitConverter.GetBytes(Checksum), 0, data, 2, 2); Buffer.BlockCopy(Message, 0, data, 4, MessageSize); return data; } public UInt16 getChecksum() { UInt32 chcksm = 0; byte[] data = getBytes(); int packetsize = MessageSize + 8; int index = 0; while (index < packetsize) { chcksm += Convert.ToUInt32(BitConverter.ToUInt16(data, index)); index += 2; } chcksm = (chcksm >> 16) + (chcksm & 0xffff); chcksm += (chcksm >> 16); return (UInt16)(~chcksm); } } VIII.1.7 Chương trình ping đơn giản (164)Trang 164 tin ICMP Echo Request (Type 8) để gởi thông điệp đơn giản đến thiết bị xa Khi thiết bị xa nhận thông điệp, trả lời lại với gói tin ICMP Echo Reply (Type 0) chứa thông điệp ban đầu Thơng điệp ICMP dùng lệnh ping Chương trình ping đơn giản: using System; using System.Net; using System.Net.Sockets; using System.Text; class SimplePing { public static void Main(string[] argv) { byte[] data = new byte[1024]; int recv; Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse(argv[0]), 0); EndPoint ep = (EndPoint)iep; ICMP packet = new ICMP(); packet.Type = 0x08; (165)Trang 165 Buffer.BlockCopy(BitConverter.GetBytes((short)1), 0, packet.Message, 0, 2); Buffer.BlockCopy(BitConverter.GetBytes((short)1), 0, packet.Message, 2, 2); data = Encoding.ASCII.GetBytes("goi tin test"); Buffer.BlockCopy(data, 0, packet.Message, 4, data.Length); packet.MessageSize = data.Length + 4; int packetsize = packet.MessageSize + 4; UInt16 chcksum = packet.getChecksum(); packet.Checksum = chcksum; host.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000); host.SendTo(packet.getBytes(), packetsize, SocketFlags.None, iep); try { data = new byte[1024]; recv = host.ReceiveFrom(data, ref ep); } catch (SocketException) { Console.WriteLine("Khong co tra loi tu thiet bi o xa"); return; } ICMP response = new ICMP(data, recv); Console.WriteLine("tra loi tu: {0}", ep.ToString()); Console.WriteLine(" Type {0}", response.Type); Console.WriteLine(" Code: {0}", response.Code); int Identifier = BitConverter.ToInt16(response.Message, 0); int Sequence = BitConverter.ToInt16(response.Message, 2); Console.WriteLine(" Identifier: {0}", Identifier); Console.WriteLine(" Sequence: {0}", Sequence); string stringData = Encoding.ASCII.GetString(response.Message, 4, response.MessageSize - 4); Console.WriteLine(" data: {0}", stringData); host.Close(); } } Chương trình ping đơn giản yêu cầu địa IP dùng (166)Trang 166 sử dụng trường Identifier Sequence để theo dõi gói tin riêng biệt cho phép nhập text vào phần liệu Cũng giống chương trình hướng phi kết nối UDP, giá trị time-out thiết lập cho Socket dùng phương thức SetSocketOption() Nếu gói tin ICMP trả giây, biệt lệ ném chương trình Gói tin ICMP trả tạo đối tượng ICMP mới, đối tượng dùng để định gói tin nhận khớp với gói tin ICMP gởi Trường Identifier, Sequence phần liệu gói tin nhận phải khớp giá trị gói tin gởi VIII.1.8 Chương trình TraceRoute đơn giản Chương trình traceroute gởi ICMP Echo Request đến máy xa để biết router mà qua đích Bằng cách thiết lập trường TTL (time to live) gói tin IP tăng lên giá trị qua router, chương trình traceroute bắt buộc gói tin ICMP bị loại bỏ router khác đường tới đích Mỗi lần trường TTL hết hạn, router cuối gởi trả gói tin (Type 11) cho thiết bị gởi Bằng cách đặt giá trị khởi đầu cho trường TTL 1, gói tin IP qua router giá trị TTL giảm Sau lần thiết bị gởi nhận gói tin ICMP Time Exceeded trường TTL tăng lên Bằng cách hiển thị địa gởi gói tin ICMP Time Exceeded, ta xác định chi tiết đường gói tin tới đích Chương trình traceroute sử dụng phương thức SetSocketOption() tùy chọn IPTimeToLive Socket để thay đổi giá trị TTL gói tin IP Bởi có trường TTL gói tin IP bị thay đổi, gói tin ICMP tạo lần sử dụng lần sử dụng gói tin IP Chương trình traceroute đơn giản: using System; using System.Net; using System.Net.Sockets; using System.Text; class TraceRoute { (167)Trang 167 byte[] data = new byte[1024]; int recv, timestart, timestop; Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); IPHostEntry iphe = Dns.Resolve(argv[0]); IPEndPoint iep = new IPEndPoint(iphe.AddressList[0], 0); EndPoint ep = (EndPoint)iep; ICMP packet = new ICMP(); packet.Type = 0x08; packet.Code = 0x00; packet.Checksum = 0; Buffer.BlockCopy(BitConverter.GetBytes(1), 0, packet.Message, 0, 2); Buffer.BlockCopy(BitConverter.GetBytes(1), 0, packet.Message, 2, 2); data = Encoding.ASCII.GetBytes("goi tin test"); Buffer.BlockCopy(data, 0, packet.Message, 4, data.Length); packet.MessageSize = data.Length + 4; int packetsize = packet.MessageSize + 4; UInt16 chcksum = packet.getCchecksum(); packet.Checksum = chcksum; host.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000); int badcount = 0; for (int i = 1; i < 50; i++) { host.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, i); timestart = Environment.TickCount; host.SendTo(packet.getBytes(), packetsize, SocketFlags.None, iep); try { data = new byte[1024]; recv = host.ReceiveFrom(data, ref ep); timestop = Environment.TickCount; ICMP response = new ICMP(data, recv); if (response.Type == 11) Console.WriteLine("Chang {0}: tra loi tu {1}, {2}ms", i, ep.ToString(), timestop - timestart); if (response.Type == 0) { Console.WriteLine("{0} den {1} chang, {2}ms.", ep.ToString(), i, timestop - timestart); break; } badcount = 0; } catch (SocketException) { Console.WriteLine("chang {0}: khong co tra loi tu thiet bi o xa", i); badcount++; if (badcount == 5) { Console.WriteLine("Khong the lien lac voi thiet bi o xa"); break; } } } host.Close(); } } (168)Trang 168 VIII.2.1 Cơ Email Hầu hết gói tin email Internet dùng mơ hình email Unix Mơ hình trở nên phổ biến sử dụng rộng rãi để phân phá thư đến người dùng cục người dùng xa Mơ hình email Unix chia chức email làm phần: ➢ The Message Transfer Agent (MTA) ➢ The Message Delivery Agent (MDA) ➢ The Message User Agent (MUA) Mỗi phần thực chức riêng biệt trình gởi, nhận hiển thị thư VIII.2.1.1 Hoạt động MTA MTA điều khiển mail gởi đến mail gởi đi, trước chia làm chức riêng biệt: Quyết định nơi cách gởi mail Quyết định noi chuyển mail đến Mỗi chức yêu cầu chức xử lý khác MTA (169)Trang 169 VIII.2.1.2 Gởi mail Với mail gởi ngồi, MTA phải định đích địa nhận Nếu đích hệ thống cục bộ, MTA chuyển mail trực tiếp hệ thống mailbox cục chuyển mail đến MDA để phát mail Tuy nhiên, đích domain xa, MTA phải thực chức sau: ➢ Quyết định mail server domain có sử dụng mục MX DNS hay không ➢ Thành lập liên kết thông tin với mail server xa chuyển mail Liên kết thông tin với mail server xa luôn dùng SMTP (Simple Mail Transfer Protocol) Giao thức chuẩn cung cấp ký thuật giao tiếp chung để chuyển mail hệ thống mail khác Internet VIII.2.1.3 Nhận mail MTA chịu trách nhiệm nhận thư gởi đến từ hệ thống cục từ người dùng xa Địa đích thư phải xem xét kỹ lưỡng định phải thực cho biết thư thực gởi từ hệ thống cục hay khơng Có danh mục địa đích dùng thư: tài khoản cục hệ thống, tài khoản bí danh cục bộ, tài khoản người dùng xa Các tài khoản cục hệ thống: hệ thống mail, dù Window hay Unix hay Macintosh có tập hợp tài khoản cục truy cập vào hệ thống MTA phải nhận thư mail có đích tài khỏa người dùng chuyển trực tiếp đến hộp thư người dùng đến MDA riêng biệt để chuyển Các tài khoản bí danh cục bộ: Nhiều MTA cho phép tên bí danh tạo Tên bí danh khơng thể lưu giữ thư, thay sử dụng trỏ đến nhiều người dùng hệ thống thực nơi mà thư lưu trữ Mỗi MTA định tên bí danh hợp lệ, chuyển đổi địa đích thành hệ thống tên tài khoản người dùng thực sự, (170)Trang 170 đòi hỏi phải có nhiều kỹ thuật, nhiều khó khăn để MTA điều khiển việc gởi mail đến hệ thống tài khoản xa Kỹ thuật gọi relying Một mail server chấp nhận thư gởi đến mà đích tài khoản máy xa tự động chuyển thư đến máy xa Nhiều ISP, tính cần thiết khác hàng khơng có khả gởi thư trực tiếp đến máy xa Thay vậy, họ gởi thư đến mail server ISP, mail server ISP tự động chuyển thư đến hệ thống đích Thật khơng may mắn, việc chuyển tiếp mail bị khai thác, người dùng sử dụng mail server chuyển tiếp để ẩn địa nguồn chuyển hàng loạt thư rác đến người dùng hệ thống email xa, email phải chặn lại người quản trị mail VIII.2.1.4 Hoạt động MDA Chức MDA chuyển thư có đích tài khoản người dùng hệ thống cục Để làm điều này, MDA phải biết kiểu vị trí mailbox cá nhân Hầu hết hệ thống email sử dùng số kiểu hệ thống sở liệu để theo dõi thư lưu giữ người dùng cục MDA phải truy cập đến mailbox người dùng để chèn thư gởi đến Nhiều MDA thực kỹ thuật nâng cao để chuyển tiếp thư: Tự động lọc thư: chức phổ biến MDA để lọc thu đến Đối với người dùng phải nhận hàng đống email ngày tính tuyệt vời Các thư tự động xếp theo thư mục riêng biệt dựa vào giá trị header, hay vài từ header Hầu hết MDA cho phép nhà quản trị mail cấu hình lọc tồn hệ thống để chặn thư rác thư có virus (171)Trang 171 Tự động chạy chương trình: khả khởi động chương trình hệ thống dựa vào số mail đến, công cụ quản trị nhà quản trị Nhà quản trị hệ thống mail khởi động hệ thống mail từ xa hay cấu hình mail server từ một máy khác mail server VIII.2.1.5 Hoạt động MUA MUA cho phép người dùng không ngồi trước mail server truy cập hộp thư họ MUA chịu trách nhiệm đọc thư hộp thư, khơng thể nhận gởi thư Có vấn đề phức tạp với MUA việc đọc lưu trữ thư đến: Lưu giữ thư Client Nhiều ISP thích người dùng tải thư họ Mỗi thư tải về, bị gở bỏ khỏi mail server Điều giúp cho người quản trị mail bảo toàn không gian lưu trữ server Giao thức dùng cho loại truy cập POP (Post Office Protocol, thường gọi POP3 cho version 3) Chương trình MUA POP3 cho phép người dùng kết nối đến server tải tất thư hộp thư Q trình mơ tả hình sau: (172)Trang 172 Điều trở ngại sử dụng POP3 thư lưu giữ trạm làm việc nơi mà người dùng kết nối tới mail server Nếu người dùng kết nối sử dụng nhiều trạm làm việc thư bị chia trạm làm việc làm cho người dùng khó truy cập thư sau tải thư xong Hiện số ISP cho phép tải thư sử dụng POP3 mà khơng xóa khỏi hộp thư Lưu giữ thư Server Một phương thức truy cập thư thay cho POP3 IMAP (Interactive Mail Access Protocol hay IMAPPrev4 cho version 4) IMAP cho phép người dùng xây dựng thư mục mail server lưu giữ thư thư mục thay phải tải xuống trạm làm việc Bởi thư lưu giữ server, người dùng kết nối từ trạm làm việc để đọc thư Q trình mơ tả hình sau: Lưu giữ thư server thư mục sử dụng IMAP Điều trở ngại hệ thống việc tiêu tốn dung lượng đĩa server (173)Trang 173 VIII.2.2 SMTP Windows Thư viện mail NET sử dụng Microsoft CDOSYS để gởi thư đến máy xa dùng SMTP VIII.2.2.1 Collaboration Data Objects (CDO) Hệ thống mail Microsoft Exchange sử dụng thư viện Active Message (OLEMSG32.DLL) cho phép lập trình viên viết code dùng hệ thống Exchange chuyển thư đến hộp thư người dùng đến hệ thống Exchange khác mạng Exchange Với phát hành Exchange 5.5, hệ thống thư giới thiệu CDO (Collaboration Data Object) Không giống Active Messaging, CDO sử dụng MAPI (Message Application Program Interface) để cung cấp cho lập trình viên cách dễ để gởi thư CDO tồn qua vài năm với nhiều cải tiến bao gồm phiên 1.1, 1.2 1.2.1 Nền tảng Microsoft NT Server sau sử dụng thư viện CDO NT Server (CDONTS) Thư viện không dùng chuẩn MAPI sử dụng CDO mà bắt đầu sử dụng chuẩn Internet SMTP để chuyển thư đến máy xa Phiên CDO (CDO 2) phát hành chung với Windows 2000 dùng Windows XP Nó sử dụng CDONTS để cung cấp thư viện mail news cho lập trình viên Các thành phần CDO bao gồm khả xử lý nhiều file đính kèm thư nhiều kiện giao thức mail khác Những tính giúp cho lập trình viên dễ dàng việc lập trình Trong Windows 2000, XP, thư viện CDO CDOSYS.DLL, tất chức thư viện mail NET phải cần tập tin Bởi CDO hồn tồn khác với phiên CDO 1.x nên hệ thống cần thư viện CDO khơng tương thích lùi với tảng Windows cũ VIII.2.2.2 Dịch vụ mail SMTP (174)Trang 174 (Internet Information Services) Các lớp mail NET sử dụng IIS SMTP Server để gởi thư trực tiếp đến mail server xa Trước sử dụng SMTP, phải cấu hình cho nó, việc cấu hình thực từ cửa sổ Computer Management Các bước cấu sau: 1 Nhấn chuột phải vào My Computer, chọn Manage 2 Khi cửa sổ Computer Management xuất hiện, mở Services and Applications sau mở Internet Information Services 3 Nhấn chuột phải vào Default SMTP Virtual Server chọn Properties Cửa sổ Properties nơi để cấu hình thiết lập cho dịch vụ SMTP Trong thẻ General, chọn card mạng cho phép kết nối SMTP tới số lượng kết nối SMTP đồng thời phép Chúng ta bật/tắt việc ghi log cho dịch vụ SMTP (175)Trang 175 Default SMTP Virtual Properties VIII.2.3 Lớp SmtpMail Lớp SmtpMail nằm namespace System.Web.Mail cho phép gởi thư theo giao thức SMTP chương trình C# VIII.2.4 Các phương thức thuộc tính lớp SmtpMail Lớp SmtpMail cung cấp giao tiếp NET cho thư viện mail CDOSYS hệ thống Windows 200 Windows XP Nó khơng dùng phương thức tạo lập để tạo thể lớp Thay vậy, ta phải dùng phương thức thuộc tính static để truyền thơng tin cho thư viện CDOSYS Phương thức Send() lớp SmtpMail hay dùng nhất, phương thức Send() tải hàm có định dạng sau: Send(MailMessage message) (176)Trang 176 Định dạng cho phép gởi đối tượng MailMessage Lớp MailMessage chứa thông điệp email, thông điệp tạo cách gán giá trị cho thuộc tính lớp với thơng tin liên quan đến thơng điệp địa đích Định dạng thứ cho phép gởi thông điệp dạng raw, phải trường header thông điệp email: From: người gởi thông điệp To: địa nhận thông điệp, ngăn cách dấu phẩy Subject: tiêu đề thư Đối số cuối phương thức Send() phần thân thư Phần text HTML Thuộc tính lớp SmtpMail SmtpServer Nó thuộc tính static địa server chuyển tiếp thư dùng để chuyển tiếp thơng điệp mail ngồi Mặc định, thuộc tính SmtpSerrver thiết lập cho dịch vụ SMTP IIS IIS cài Nếu IIS không cài, thuộc tính SmtpServer thiết lập giá trị null phát sinh lỗi gởi thông điệp Nếu sử dụng mail server chuyển tiếp, phải thiết lập giá trị cho thuộc tính SmtpServer trước gởi thơng điệp SmtpMail.SmtpServer = "mailsrvr.myisp.net"; Khi tât giá trị thiết lập, tất thư gởi chuyển tiếp qua mail server Tất nhiên, phải đảm bảo server cho phép chuyển tiếp mail qua VIII.2.4.1 Sử dụng lớp SmtpMail (177)Trang 177 TÀI LIỆU THAM KHẢO [1] C Sharp Network Programming, Sybex [2] Microsoft Network Programming For The Microsoft Dot Net Framework, Microsoft Press Socket (AddressFamily, SocketType, ProtocolType)
- Xem thêm -

Xem thêm: Bài Giảng Tóm Tắt Lập Trình Mạng,

Hình ảnh liên quan

Hình I.1: Các tầng giao thức mạng trong các gĩi dữ liệu - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

I.1: Các tầng giao thức mạng trong các gĩi dữ liệu Xem tại trang 6 của tài liệu.
Hình I.2: Ethernet Header - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

I.2: Ethernet Header Xem tại trang 7 của tài liệu.
Hình I.3: Thơng tin tầng IP Các trường trong tầng IP:  - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

I.3: Thơng tin tầng IP Các trường trong tầng IP: Xem tại trang 10 của tài liệu.
Hình I.4: Các trường của TCP Header - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

I.4: Các trường của TCP Header Xem tại trang 14 của tài liệu.
Hình I.5: Kết nối TCP đơn giản - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

I.5: Kết nối TCP đơn giản Xem tại trang 15 của tài liệu.
Trong hình trên thì thiết bị A đang chạy hai ứng dụng Server, hai ứng dụng này đang chờ các gĩi tin từ Client - Bài Giảng Tóm Tắt Lập Trình Mạng

rong.

hình trên thì thiết bị A đang chạy hai ứng dụng Server, hai ứng dụng này đang chờ các gĩi tin từ Client Xem tại trang 15 của tài liệu.
Hình I.6: Các bước bắt tay của giao thức TCP - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

I.6: Các bước bắt tay của giao thức TCP Xem tại trang 18 của tài liệu.
Hình II.1: Mơ hình lập trình Socket hướng kết nối - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.1: Mơ hình lập trình Socket hướng kết nối Xem tại trang 26 của tài liệu.
Hình II.2: Kết quả trả về sau khi telnet vào Server local tại port 5000 - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.2: Kết quả trả về sau khi telnet vào Server local tại port 5000 Xem tại trang 29 của tài liệu.
Hình II.3: TCP Buffer - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.3: TCP Buffer Xem tại trang 33 của tài liệu.
Hình II.4: Kết quả trả về khi chạy chương trình với buffer nhỏ - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.4: Kết quả trả về khi chạy chương trình với buffer nhỏ Xem tại trang 34 của tài liệu.
Hình II.5: Client Send hai lần rồi Server mới Receive - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.5: Client Send hai lần rồi Server mới Receive Xem tại trang 35 của tài liệu.
Kết quả chương trình như hình bên dưới - Bài Giảng Tóm Tắt Lập Trình Mạng

t.

quả chương trình như hình bên dưới Xem tại trang 38 của tài liệu.
Hình II.7: Kết quả gởi và nhận dữ liệu với kích thước cố định - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.7: Kết quả gởi và nhận dữ liệu với kích thước cố định Xem tại trang 44 của tài liệu.
Hình II.8: Kết quả gởi và thơng điệp cùng với kích thước - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

II.8: Kết quả gởi và thơng điệp cùng với kích thước Xem tại trang 50 của tài liệu.
Hình V.1: Mơ hình lập trình Socket phi kết nối - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

V.1: Mơ hình lập trình Socket phi kết nối Xem tại trang 59 của tài liệu.
Hình V.2: UDP Server nhận biết được các thơng điệp riêng rẽ - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

V.2: UDP Server nhận biết được các thơng điệp riêng rẽ Xem tại trang 67 của tài liệu.
V.2. Mơ hình - Bài Giảng Tóm Tắt Lập Trình Mạng

2..

Mơ hình Xem tại trang 89 của tài liệu.
Hình VI.1: Lập trình sự kiện trên Windows - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

VI.1: Lập trình sự kiện trên Windows Xem tại trang 126 của tài liệu.
Hình VI.2: Gởi và nhận sự kiện trong Windows - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

VI.2: Gởi và nhận sự kiện trong Windows Xem tại trang 127 của tài liệu.
Bảng sau cho biết các phương thức bất đồng bộ cĩ thể được giữa với Socket. Mỗi phương thức Begin được kết hợp với một phương thức End để hồn tất chức năng  của chúng:   - Bài Giảng Tóm Tắt Lập Trình Mạng

Bảng sau.

cho biết các phương thức bất đồng bộ cĩ thể được giữa với Socket. Mỗi phương thức Begin được kết hợp với một phương thức End để hồn tất chức năng của chúng: Xem tại trang 130 của tài liệu.
Hình VI.3: Giao diện Server - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

VI.3: Giao diện Server Xem tại trang 135 của tài liệu.
Hình VI.4: Mơ hình chương trình Server - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

VI.4: Mơ hình chương trình Server Xem tại trang 136 của tài liệu.
Hình VI.5: Giao diện Client - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

VI.5: Giao diện Client Xem tại trang 141 của tài liệu.
Hình VI.6: Mơ hình chương trình Client - Bài Giảng Tóm Tắt Lập Trình Mạng

nh.

VI.6: Mơ hình chương trình Client Xem tại trang 142 của tài liệu.
Mơ hình xử lý như sau: - Bài Giảng Tóm Tắt Lập Trình Mạng

h.

ình xử lý như sau: Xem tại trang 152 của tài liệu.
Hầu hết mọi gĩi tin email trên Internet đều dùng mơ hình email của Unix. Mơ hình này trở nên phổ biến và được sử dụng rộng rãi để phân phá thư đến cả người dùng  cục bộ và người dùng ở xa - Bài Giảng Tóm Tắt Lập Trình Mạng

u.

hết mọi gĩi tin email trên Internet đều dùng mơ hình email của Unix. Mơ hình này trở nên phổ biến và được sử dụng rộng rãi để phân phá thư đến cả người dùng cục bộ và người dùng ở xa Xem tại trang 168 của tài liệu.