Giáo trình Lập trình mạng - Hà Mạnh Đào

pdf 206 trang ngocly 2980
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Lập trình mạng - Hà Mạnh Đào", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_lap_trinh_mang_ha_manh_dao.pdf

Nội dung text: Giáo trình Lập trình mạng - Hà Mạnh Đào

  1. HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THÔNG (PTIT) GIÁO TRÌNH LẬP TRÌNH MẠNG (PHẦN LẬP TRÌNH MẠNG CƠ SỞ) HÀ MẠNH ĐÀO PTIT HÀ NỘI, 07/2010
  2. MỞ ĐẦU Ngày nay do nhu cầu thực tế và do sự phát triển mạnh mẽ của nhiều công nghệ tích hợp, dẫn đến các chương trình ứng dụng hiện này hầu hết đều có khả năng thực hiện trên môi trường mạng máy tính nói riêng và mạng tích hợp nói chung. Chính vì vậy giáo trình này nhằm cung cấp cho sinh viên những kiến thức và kỹ thuật cơ bản nhất để phát triển các chương trình ứng dụng mạng. Giáo trình này bao gồm 4 phần lớn và 7 chương: Phần thứ nhất trình bày các kiến thức cơ sở cho lập trình mạng, chủ yếu là kiến thức mạng máy tính, ngôn ngữ lập trình và mô hình lập trình mạng. Phần 2 và 3 cung cấp cho sinh viên 2 kỹ thuật lập trình cơ bản nhất và phổ biến nhất hiện này là lập trình mạng với socket và lập trình phân tán thông qua ngôn ngữ Java. Đồng thời cũng rèn sinh viên cách lập trình với giao thức truyền thông có sẵn và khả năng tích hợp trong các ứng dụng khác nhau, nhất là các giao thức truyền thông thời gian thực(RTP). Phần 4 sẽ đề cập đến lập trình truyền thông qua mạng điện thoại công cộng, để sinh viên bước đầu làm quen với kỹ thuật lập trình cơ bản truyền thông qua hệ thống mạng này. Từ đó sinh viên dễ dàng tiếp cận phát triển các ứng dụng trên cơ sở mạng này như hội thoại video, các dịch vụ truy cập từ xa, VPN, IPTV và nói chung là công nghệ IP. Phần 5 cung cấp cho sinh viên làm quen kiến thức lập trình mạng an toàn bảo mật mà chủ yếu là giao thức SSL. Cách bố trí của chúng tôi thành từng phần rõ ràng, mỗi phần có thể có một hoặc nhiều chương với mục đích hướng mở cho từng phần trong tương lai. Để nắm được kiến thức lập trình mạng, sinh viên phải học qua kiến thức các môn: Mạng máy tính, lập trình OOP, phân tích và thiết kế hệ thống, ngôn ngữ lập trình java cơ bản. Giáo trình biên soạn phiên bản đầu, chắc không tránh khỏi lỗi, rất mong nhận được ý kiến đóng góp của đồng nghiệp vàPTIT những độc giả quan tâm. Xin chân thành cảm ơn! Hà Nội, tháng 07 năm 2010 Tác giả
  3. MỤC LỤC PHẦN I. KIẾN THỨC CƠ SỞ CHO LẬP TRÌNH MẠNG 1 CHƯƠNG I MỘT SỐ KIẾN THỨC CƠ SỞ CHO LẬP TRÌNH MẠNG 1 I. GIỚI THIỆU VỀ LẬP TRÌNH MẠNG 1 II. MỘT SỐ KIẾN THỨC MẠNG CƠ SỞ LẬP TRÌNH MANG 1 1. Mô hình OSI./ISO và họ giao thức TCP/IP 2 1.2. Giao thức truyền thông và phân loại 2 1.3. Địa chỉ IP, mặt nạ 2 1.4. Địa chỉ cổng 4 1.5. Giao diện socket, địa chỉ socket 5 II. CÁC MÔ HÌNH LẬP TRÌNH MẠNG 6 1. Mô hình client/server 6 1.1. Chương trình client 6 1.2. Chương trình server 6 2. Mô hình peer-to-peer 6 3. Mô hình đa tầng 6 III. NGÔN NGỮ LẬP TRÌNH MẠNG 7 1. Giới thiệu chung 7 2. Lập trình bằng ngôn ngữ JAVA 8 IV. KỸ THUẬT LẬP TRÌNH MẠNG 8 PHẦN II. KỸ THUẬT LẬP TRÌNH MẠNG VỚI SOCKET 10 CHƯƠNG II. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI SOCKET 10 I. GIỚI THIỆU CHUNG 10 II. LẬP TRÌNH THAO TÁC VỚI ĐỊA CHỈ MÁY TRẠM 10 1. Lập trình thao tác với PTITđịa chỉ IP 10 1.1. Lớp Address 10 1.2. Ví dụ sử dụng các phương thức lớp InetAddress 15 III. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI TCPSOCKET 17 1. Giao thức TCP và cơ chế truyền thông TCP 17 2. Một số lớp Java hỗ trợ lập trình TCPSocket 17 2.1. Lớp Socket 17 2.2. Lớp ServerSocket 19 3. Kỹ thuật lập trình truyền thông với giao thức TCP 20 3.1. Chương trình phía server 20 3.2. Chương trình phía client 20 3.3. Luồng I/O mạng và đọc/ghi dữ liệu qua luồng I/O 22
  4. 4. Một số chương trình ví dụ 23 4.1. Chương trình quét cổng sử dụng Socket 23 4.2. Chương trình quét cổng cục bộ dùng lớp ServerSocket 24 4.3. Chương trình finger client 24 4.4. Chương trình cho phép lấy thời gian server về client 25 IV. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI UDPSOCKET 28 1. Giao thức UDP và cơ chế truyền thông UDP 28 2. Một số lớp Java hỗ trợ lập trình với UDPSocket 28 2.1. Lớp DatagramPacket 28 2.2. Lớp DatagramSocket 30 3. Kỹ thuật lập trình truyền thông với giao thức UDP 33 3.1. Phía server 33 3.2. Phía client 33 3.3. Lưu ý 33 4. Một số chương trình ví dụ 34 V. LẬP TRÌNH VỚI THẺ GIAO TIẾP MẠNG(NIC) 35 1. Giới thiệu về thẻ giao tiếp mạng 35 2. Lớp NetworkInterface 35 3. Lập trình với giao tiếp mạng 38 4. Một số chương trình ví dụ 41 VI. LẬP TRÌNH TRUYỀN THÔNG MULTICAST 43 1. Giới thiệu truyền thông multicast và lớp MulticastSocket 43 2. Một số ví dụ gửi/nhận dữ liệu multicast 45 VII. KẾT LUẬN 47 CHƯƠNG III. KỸ THUẬT XẬY DỰNG ỨNG DỤNG MẠNG PHÍA SERVER 48 I. GIỚI THIỆU CÁC KỂU SERVER 48 1. Server chạy chế độ đồngPTIT thời hưóng kết nối 48 2. Server chạy chế độ lặp hướng không kết nối 49 II. XÂY DỰNG SERVER PHỤC VỤ NHIỀU CLIENT HƯỚNG KẾT NỐI 49 1. Giới thiệu 49 2. Kỹ thuật lập trình đa luồng trong Java 50 3. Xây dựng chương trình server phục vụ nhiều client đồng thời 53 III. KẾT LUẬN 57 CHƯƠNG IV. LẬP TRÌNH GIAO THỨC DỊCH VỤ MẠNG PHÍA CLIENT 58 I. GIỚI THIỆU 58 II. LẬP TRÌNH GIAO THỨC DỊCH VỤ TELNET 58 1. Một số khái niệm và đặc điểm dịch vụ Telnet 58 2. Một số kiến thức giao thức Telnet cơ bản 60
  5. 3. Cài đặt dịch vụ Telnet Client với Java 63 4. Chạy thử chương trình 68 III. LẬP TRÌNH DỊCH VỤ TRUYỀN TỆP VỚI GIAO THỨC FTP 68 1. Dịch vụ truyền tệp FTP 68 2. Kỹ thuật cài đặt giao thức FTP với Java 73 IV. LẬP TRÌNH GỬI/NHẬN THƯ VỚI GIAO THỨC SMTP/POP3 76 1. Giao thức SMTP 76 2. Giao thức POP3 84 V. KẾT LUẬN 87 PHẦN III. LẬP TRÌNH PHÂN TÁN 88 CHƯƠNG V. KỸ THUẬT LẬP TRÌNH PHÂN TÁN ĐỐI TƯỢNG RMI 88 I. GIỚI THIỆU LẬP TRÌNH PHÂN TÁN VÀ RMI 88 1. Giới thiệu kỹ thuật lập trình phân tán 88 2. Giới thiệu kỹ thuật lập trình RMI 88 3. Các lớp hỗ trợ lập trình với RMI 91 II. XÂY DỰNG CHƯƠNG TRÌNH PHÂN TÁN RMI 92 1. Kỹ thuật lập trình RMI 92 2. Biên dịch chương trình 95 3. Thực thi chương trình 95 III. CƠ CHẾ TRUYỀN THÔNG RMI 96 IV. VẤN ĐỀ TRUYỀN THAM SỐ CHO PHƯƠNG THỨC GỌI TỪ XA 97 1. Giới thiệu truyền tham số tham trị và tham chiếu 97 2. Truyền đối tượng theo kiểu tham trị 97 3. Truyền đối tượng theo kiểu tham chiếu 99 V. KỸ THUẬT SỬ DỤNG MỘT ĐỐI TƯỢNG SẢN SINH NHIỀU 102 1. Giới thiệu 102 2. Kỹ thuật ứng dụng FactPTITory 103 VI. KẾT LUẬN 107 II. XÂY DỰNG CHƯƠNG TRÌNH PHÂN TÁN RMI 98 1. Kỹ thuật lập trình RMI 98 2. Biên dịch chương trình 101 3. Thực thi chương trình ứng dụng 102 III. KẾT LUẬN 102 PHẦN IV. LẬP TRÌNH TRUYỀN THÔNG QUA MẠNG PSTN 108 CHƯƠNG V. LẬP TRÌNH ỨNG DỤNG TRUYỀN THÔNG MẠNG ĐTCC 108 I. KỸ THUẬT LẬP TRÌNH VỚI JTAPI 108 1. Giới thiệu thư viện JTAPI 108 2. Cơ sở của JTAPI 110
  6. 3. Các cấu hình cuộc gọi tiêu biểu 111 4. Mô hình cuộc gọi Java 113 II. CẤU HÌNH HỆ THỐNG 118 1. Cấu hình máy tính mạng 118 2. Cấu hình desktop 118 III. MỘT SỐ CHƯƠNG TRÌNH VÍ DỤ LẬP TRÌNH VỚI JTAPI 118 1. Ví dụ thiết lập một cuộc gọi điện thoại 118 2. Thực hiện gọi một cuộc điện thoại từ một số 119 3. Một ứng dụng trả lời cuộc điện thoại 120 4. Ví dụng xây dựng dịch vụ RAS với JTAPI 122 IV. KẾT LUẬN 130 PHẦN IV. LẬP TRÌNH MẠNG AN TOÀN BẢO MẬT 131 CHƯƠNG VII. LẬP TRÌNH MẠNG AN TOÀN BẢO MẬT VỚI SSL 131 I. GIỚI THIỆU SSL VÀ MỘT SỐ KHAI NIỆM 131 1. Giới thiệu về SSL 131 2. Khoá(key) 131 3. Thuật toán mã hoá 132 4. Cơ chế làm việc của SSL 134 5. Bảo mật của giao thức SSL 135 II. LẬP TRÌNH MẠNG AN TOÀN BẢO MẬT VỚI SSL 136 1. Thư viện Java hỗ trợ lập trình với SSL 136 2. Ví dụ sử dụng các lớp SSL 137 III. KẾT LUẬN 141 TÀI LIỆU THAM KHẢO 142 PTIT
  7. PHẦN I. KIẾN THỨC CƠ SỞ CHO LẬP TRÌNH CHƯƠNG I MỘT SỐ KIẾN THỨC CƠ SỞ CHO LẬP TRÌNH I. GIỚI THIỆU VỀ LẬP TRÌNH MẠNG(LTM) Ngày này khi nói đến phát triển các ứng dụng phần mềm, đa số là người ta muốn nói đến chương trình có khả năng làm việc trong môi trường mạng tích hợp nói chung và mạng máy tính nói riêng. Từ các chương trình kế toán doanh nghiệp, quản lý, trò chơi, điều khiển đều là các chương trình ứng dụng mạng. Vấn đề lập trình mạng liên quan đế nhiều lĩnh vực kiến thức khác nhau. Từ kiến thức sử dụng ngôn ngữ lập trình, phân tích thiết kế hệ thống, kiến thức hệ thống mạng, mô hình xây dựng chương trình ứng dụng mạng, kiến thức về cơ sở dữ liệu cho đến kiến thức truyền thông, các kiến thức các lĩnh vực liên quan khác như mạng điện thoại di động, PSTN, hệ thống GPS, các mạng như BlueTooth, WUSB, mạng sensor Nhưng có thể nói vấn đề lập trình mạng có 3 vấn đề chính cốt lõi tích hợp trong lập trình ứng dụng mạng và được thể hiện như hình 1. PTIT Hình 1.1. Các kiến thức cơ sở cho lập trình mạng Hay nói cách khác, vấn đề lập trình mạng có thể được định nghĩa với công thức sau: LTM=KTM+MH+NN Trong đó: . LTM: Lập trình mạng . KTM: Kiến thức mạng truyền thông( mạng máy tính, PSTN ) . MH: Mô hình lập trình mạng . NN: Ngôn ngữ lập trình mạng
  8. Trong giao trình này, chúng tôi tập trung chủ yếu vào các kỹ thuật phát triển chương trình ứng dụng mạng. Còn các vấn đề khác can thiệp sâu xuống phía thấp hơn trong hệ thống mạng như các trình tiện ích mạng, thu thập bắt và phân tích gói tin các bạn có thể tham khảo các tài liệu khác, nhất là các tài liệu liên quan đến lập trình với Raw socket. II. MỘT SỐ KIẾN THỨC MẠNG CƠ SỞ LẬP TRÌNH MẠNG 1. Mô hình OSI/ISO và họ giao thức TCP/IP Hình 1.2. Mô hình OSI/ISO và họ giao thức TCP/IP 1.2. Giao thức truyền thông và phân loại(protocol) Giao thức truyền thông là tập cácPTIT qui tắc, qui ước mà mọi thực thể tham ra truyền thông phải tuân theo để mạng có thể hoạt động tốt. Hai máy tính nối mạng muốn truyền thông với nhau phải cài đặt và sử dụng cùng một giao thức thì mới "hiểu" nhau được. Dựa vào phương thức hoạt động, người ta có thể chia giao thức truyền thông thành 2 loại: Giao thức hướng kết nối và giao thức hướng không kết nối. 1.2.1. Giao thức hoạt động theo hướng có kết nối Loại giao thức truyền thông này sử dụng kết nối(ảo) để truyền thông. Đặc điểm của loại giao thức này là: Truyền thông theo kiểu điểm-điểm Dữ liệu truyền qua mạng là một dòng các byte liên tục truyền từ nơi gửi tới nơi nhận, mỗi byte có một chỉ số xác định.
  9. Quá trình truyền thông được thực hiện thông qua 3 giai đoạn:  Thiết lập kết nối  Truyền dữ liệu kèm theo cơ chế kiểm soát chặt chẽ  Huỷ bỏ kết nối Giao thức tiêu biểu là giao thức TCP 1.2.2. Giao thức hoạt động hướng không kết nối Kiểu giao thức này khi thực hiện truyền thông không cần kết nối (ảo) để truyền dữ liệu. Giao thức kiểu này có đặc điểm sau: Truyền thông theo kiểu điểm-đa điểm Quá trình truyền thông chỉ có một giai đoạn duy nhất là truyền dữ liệu, không có giai đoạn thiết lập kết nối cũng như huỷ bỏ kết nối. Dữ liệu truyền được tổ chức thành các tin gói tin độc lập, trong mỗi gói dữ liệu có chứa địa chỉ nơi nhận. Giao thức tiêu biểu loại này là giao thức UDP 1.2.3. Một số giao thức truyền thông Internet phổ biến Giao thức tầng Internet: IP, ARP, RARP, ICMP, IGMP Giao thức tầng giao vận: TCP, UDP Giao thức dịch vụ: Telnet, FTP, TFTP, SMTP, POP3, IMAP4, DNS, HTTP 1.3. Địa chỉ IP, mặt nạ(mask) 1.3.1. Địa chỉ IP Hai phiên bản địa chỉ IP thông dụng: IPv4 và IPv6. Hiện thế giới cũng như Việt Nam đang chuyển dần sang sử dụng IPv6. 1.3.2. Mặt nạ(mask) Mặt nạ là một giá trị hằng( một số nhị phân 32 bít) cho phép phân tách địa chỉ mạng từ địa chỉ IP(địa chỉ đầu khối địa chỉ IP). PTITCụ thể khi cho bất kỳ một địa chỉ IP nào trong khối địa chỉ, bằng cách thực hiện phép toán AND mức bít, mặt nạ sẽ giữ nguyên phần netid và xoá toàn bộ các bít phần hostid về giá trị 0, tức là trả về địa chỉ đầu khối địa chỉ đó. Mặt nạ của một mạng con có thể là mặt nạ có chiều dài cố định hoặc biến đổi. Các mặt nạ mặc định của các lớp địa chỉ A, B, C tương ứng là: 255.0.0.0, 255.255.0.0, 255.255.255.0. Trong kỹ thuật chia một mạng thành nhiều mạng con(subnet), hoặc để tạo thành siêu mạng(supernet) đối với lớp C, người ta phải tìm được mặt nạ mạng và định danh cho các mạng đó bằng cách mượn một số bít phần hostid(subnet) hoặc phần netid(supernet). Mặt nạ có vai trò quan trọng trong việc định tuyến cho một gói tin đi đến đúng mạng đích 1.3.3. Một số địa chỉ IP đặc biệt . Địa chỉ mạng: nettid là định danh của mạng, các bít hostid đều băng 0.
  10. . Địa chỉ Broadcast trực tiếp: Là địa chỉ đích, có phần netid của mạng, các bít phần hostid đều có giá trị 1. . Điạ chỉ Broadcast hạn chế: Là địa chỉ đích và có tất cả các bít phần netid và hostid đều có giá trị 1. Gói tin có địa chỉ này sẽ bị chặn bởi các router. Địa chỉ this host on this network: có tất cả các bít netid và hostid đều bằng 0. Địa chỉ này là địa chỉ nguồn được máy trạm sử dụng tại thời điểm Bootstrap để truyền thông khi nó biết địa chỉ IP của nó. . Địa chỉ máy trạm cụ thể trong một mạng: có tất cả các bít netid bằng 0 và phần hostid là địa chỉ host cụ thể trong mạng. . Địa chỉ Loopback: Địa chỉ này có byte đầu tiên là 127, còn các byte còn lại có thể có giá trị bất kỳ: 127.X.Y.Z. Địa chỉ này được dùng để chạy thử các chương trình ứng dụng mạng trên cùng một máy, nhất là khi không có mạng. Địa chỉ loopback là địa chỉ đích, khi địa chỉ này được sử dụng, gói tin sẽ không bao giờ truyền ra khỏi máy. Địa chỉ loopback tiêu biểu là 127.0.0.1 hoặc có thể dùng chuỗi “localhost” thay thế. . Địa chỉ riêng: Một số khối địa chỉ trong các lớp được qui định chỉ sử dụng cho mạng riêng(mạng cục bộ) mà không được phép sử dụng trên mạng Internet. Khi các gói tin truyền thông trên mạng Internet, các router và switch trên mạng xương sống Internetđược cấu hình loại bỏ gói tin sử dụng các địa chỉ trong các khối địa chỉ riêng này. Các dải địa chỉ riêng: - Lớp A: 10.0.0.0 -> 10.255.255.255 - Lớp B: 172.16.0.0 -> 172.31.255.255 - Lớp C: 192.168.0.0 -> 192.168.255.255 Ngoài ra người ta còn sử dụng các địa chỉ không theo lớp mà cho các khối địa chỉ có chiều dài biến đổi, các địa chỉ này có dạng CIDR: a.b.c.d/n. 1.4. Địa chỉ cổng(port) Đa số các hệ điều hành mạng hiện nay đều đa nhiệm nên cho phép nhiều tiến trình truyền thông chạy đồng thời trên cùng một máyPTIT tính và đều chung một địa chỉ IP. Chình vì như vậy, 2 tiến trình trên 2 máy tính muốn truyền thông với nhau mà chỉ sử dụng địa chỉ IP là chưa thể thực hiện được. Để phân biệt các tiến trình chạy trên cùng một máy tính đồng thời, người ta gán cho mỗi tiến trình một nhãn duy nhất để phân biệt các tiến trình với nhau. Trong kỹ thuật mạng máy tính, người ta sử dụng một số nguyên 16 bít để làm nhãn và nó được gọi là số hiệu cổng hoặc địa chỉ cổng(port). Địa chỉ cổng này được sử dụng và được quản lý bởi tầng giao vận và nó có giá trị từ 0 đến 65535, được chia làm 3 giải:
  11. Hình 1.3. Các dải địa chỉ cổng  Giải địa chỉ từ 0 đến 1023: Gải này dùng cho hệ thống, người sử dụng không nên dùng. Các địa chỉ cổng trong dải này thường được gán mặc định cho các giao thức truyền thông phổ biến như bảng sau: port Giao thức Mô tả 7 Echo Phản hồi Datagram nhận được trở lại nơi gửi 9 Discard Loại bỏ mọi Datagram nhận được 13 Daytime Trả về ngày và giờ 19 Chargen Trả về một chuỗi ký tự 20 FTP,Data Phía server FTP(Kết nối dữ liêu) 21 FTP,Control Phía server FTP(Kết nối điều khiển) 23 Telnet Mạng đầu cuối 25 SMTP Giao thức gửi thư Internet 53 DNS Giao thức DNS 67 BOOTP Giao thức Bootrap 79 Finger PTITFinger 80 HTTP Giao thức truyền siêu văn bản 111 RPC Giao thức gọi thủ tục từ xa 110 POP3 Giao thức truy cập Email 143 IMAP4 Giao thức truy cập Email  Giải địa chỉ từ 1024 đến 49151: Giải địa chỉ cổng này người sử dụng được phép dùng, nhưng phải đăng ký để tránh trùng lặp.
  12.  Giải địa chỉ từ 49152 đến 65535: Đây là giải địa chỉ động hoặc dùng riêng. Người sử dụng dùng địa chỉ trong giải này không phải đăng ký và cũng không phải chịu trách nhiệm khi xẩy ra xung đột địa chỉ. 1.5. Giao diện socket, địa chỉ socket Socket là gì? Chúng ta có thể hiểu socket là giao diện và là một cấu trúc truyền thông đóng vai trò như là một điểm cuối(end point) để truyền thông. Mỗi tiến trình khi muốn truyền thông bằng socket, đầu tiên nó phải tạo ra một socket và socket đó phải được gán một định danh duy nhất được gọi là địa chỉ socket. Một địa chỉ socket là một tổ hợp gồm 2 địa chỉ: địa chỉ IP và địa chỉ cổng(port). Như vậy địa chỉ socket xác định một đầu mút cuối truyền thông. Nó chỉ ra tiến trình truyền thông nào(port) và chạy trên trên máy nào(IP) sẽ thực hiện truyền thông. Để hỗ trợ nguời phát triển ứng dụng mạng sử dụng socket, các nhà sản xuất phần mềm đã xây dựng sẵn một tập các hàm thư viện API và gọi là tập hàm thư viện giao diện socket. Giao diện socket được phân làm 3 loại socket(hình 2). ICMP Hình 1.4. Các kiểu giao diện socket  Stream socket: cho phép truyền thông với các giao thức truyền thông hướng kết nối mà tiêu biểu là giao thức TCP(TCPSocket). TCP sử dụng một cặp stream socket để kết nối một chương trình ứng dụng với một chương trình ứng dụng khác qua mạng Internet.  Datagram socket: Cho phépPTIT truyền thông với các giao thức hướng không kết nối, tiêu biểu là giao thức UDP (UDP socket). UDP sử dụng một cặp datagram socket để gửi thông điệp từ một chương trình ứng dụng tới một chương trình ứng dụng khác qua mạng Internet.  Raw socket: Đây là kiểu giao socket cho phép truyền thống đến các giao thức ở tầng mạng thập hơn cả tầng giao vậnmà tiêu biểu nhất là giao thức ICMP của tầng Internet hoặc OSPF. Ví dụ chương trình ping sử dụng kiểu socket này. II. CÁC MÔ HÌNH LẬP TRÌNH MẠNG 1. Mô hình client/server Chương trình ứng dụng mạng tổ chức theo mô hình client/server được sử dụng phổ biến trong thực tế. Chương trình ứng dụng mạng theo mô hình này gồm có 2 phần mềm: Phần mềm
  13. server(phục vụ) và phần mềm client(máy khách) và nó thể hiện như hình 2. Một chương trình server có thể phục vụ nhiều chương trình client đồng thời hoặc tuần tự(kiểu lặp). Server ···· Client_1 Client_2 ···· Client_n Hình 1.5. Mô hình client/server 1.1. Chương trình client: client là một chương trình chạy trên máy cục bộ mà đưa ra yêu cầu dịch vụ đối với server. Chương trình client có thời gian chạy hữu hạn. Nó được khởi đầu bởi người sử dụng( hoặc một chương trình ứng dụng khác) và kết thúc khi dịch vụ đã thực hiện hoàn thành. Sau khi khởi tạo, client thực hiện mở một kênh truyền thông sử dụng địa chỉ IP của máy trạm từ xa và địa chỉ cổng(nhãn) đã biết rõ của chương trình server cụ thể chạy trên máy tính từ xa đó. Cách mở đó của client được gọi là mở tích cực( active open). Sau khi kênh truyền thông được mở client sẽ gửi yêu cầu tới server và nhận đáp ứng trả về từ server. 1.2. Chương trình server: Chương trình này có đặc điểm là có thời gian chạy vô tận và chỉ dừng chạy bởi người sử dụng hoặc tắt máy tính. Chương trình này sau khi khởi tạo, nó sẽ thực hiện mở thụ động(passive Open) và được đặt ở trạng thái “nghe” chờ tín hiệu gửi tới từ client, nếu có, nó sẽ nhận yêu cầu gửi tới từ client, thực hiện xử lý và đáp ứng yêu cầu đó. 2. Mô hình peer-to-peer Chương trình ứng dụng mạng làm việc theo mô hình peer-to-peer(ngang cấp, bình đẳng) có thể nói là các chương trình mà có thể thực hiện vai trò của cả server và của client. Chương trình này khi chạy có thể yêu cầu chươngPTIT trình khác phục vụ nó và nó cũng có thể phục vụ yêu cầu gừi tới từ chương trình khác. 3. Mô hình đa tầng Mô hình đa tầng gồm nhiều tầng mà tiêu biểu nhất là mô hình 3 tầng. Trong mô hình này, tầng thấp nhất là tầng thông tin, tầng trung gian và tầng đỉnh. Một ví dụ tiểu biểu của mô hình 3 tầng đó là dịch vụ Web với tầng đỉnh là trình duyệt, tầng trung gian là webserver và tầng thông tin là cơ sở dữ liệu. Mô hình nhiều tầng sẽ được khảo sát kỹ trong phần lập trình ứng dụng mạng nâng cao với các kỹ thuật Servlet, EJB, Portlet III . NGÔN NGỮ LẬP TRÌNH MẠNG 1. Giới thiệu chung
  14. Nói chung tất cả các ngôn ngữ lập trình đều có thể sử dụng để lập trình mạng. Nhưng mỗi ngôn ngữ có những ưu, nhược điểm khác nhau và được hỗ trợ thư viện API ở các mức độ khác nhau. Tuỳ từng ứng dụng mạng cụ thể, hệ điều hành mạng cụ thể và thói quen lập trình mà người lập trình có thể chọn ngôn ngữ phù hợp để phát triển các ứng dụng mạng. Các ngôn ngữ lập trình phổ biến hiện nay gồm những ngôn ngữ sau: . Hợp ngữ( Assembly Language) . C/C++ . VC++, VB, Delphi . Java . .NET . ASP Đối với phát triển ứng dụng mạng hiện nay có 2 ngôn ngữ lập trình được sử dụng phổ biến nhất, đó là .NET và JAVA. Người lập trình có thể sử dụng thành thạo một trong 2 dòng ngôn ngữ đó để phát triển ứng dụng mạng(ở với Việt Nam nói chung nên nắm tốt cả 2 công nghệ này). Trong giáo trình này chúng tôi sẽ sử dụng ngôn ngữ lập trình JAVA và các công nghệ liên quan đến nó để phát triển ứng dụng mạng. Sau khi nắm chắc kỹ thuật, tư tưởng lập trình mạng thông qua ngôn ngữ Java, sinh viên có thể sử dụng bất kể ngôn ngữ lập trình nào phù hợp như VB.NET, C#, 2. Lập trình mạng bằng ngôn ngữ Java Để lập trình mạng bằng ngôn ngữ Java, sinh viên phải nắm chắc một số kiến thức lập trình java sau: Tổng quan công nghệ Java, các gói thư viện(J2SE, J2ME, J2EE) Lập trình Java cơ sở Lập trình Java OOP Lập trình giao diện đồ hoạ người sử dụng(GUI) và applet I/O theo luồng và thao tác tệp Lập trình kết nối với cơ sPTITở dữ liệu Kỹ thuật lập trình đa luồng Ngoại lệ và xử lý ngoại lệ Lập trình an toàn bảo mật trong Java Ngoài ra sinh viên còn phải hiểu về máy ảo java dành cho các ứng dụng java khác nhau(JVM, KVM, máy ảo cho dòng SPOT ). IV. KỸ THUẬT LẬP TRÌNH MẠNG Có nhiều kỹ thuật lập trình mạng khác nhau, nhưng trong giáo trình này chủ yếu chỉ tập trung vào 3 kỹ thuật lập trình mạng chính: - Kỹ thuật lập trình mạng với socket: Trong kỹ thuật này, chương trình ứng dụng mạng sẽ được xây dựng với các kiểu socket khác nhau. Kỹ thuật này cho phép mối
  15. quan hệ qua mạng giữa các chương trình chạy lỏng lẻo vì bản thân socket là giao diện mạng , không phải cơ chế truyền thông. - Kỹ thuật lập trình phân tán: Trái với kỹ thuật lập trình socket, trong kỹ thuật này mối quan hệ giữa chương trình client và server là gắn kết chặt chẽ. Kỹ thuật lập trình này thực chất là kỹ thuật lập trình phân tán mã lệnh(đối tượng), cho phép phân tải tính toán lên các máy tính kết nối với nhau với quan hệ hữu cơ thay vì tập trung trên cùng một máy. Điều này cho phép tận dụng tài nguyên mạng để giải quyết các bài toán với khối lượng tính toán lớn, thời gian thực. - Kỹ thuật lập trình truyền thông qua mạng điện thoại công cộng PSTN. Các kỹ thuật này sẽ được khảo sát chi tiết trong các chương tiếp theo. V. THIẾT KẾ VÀ CÀI ĐẶT THEO MÔ HÌNH MVC 1. Giới thiệu mô hình MVC Mô hình MVC (Model – View - Control) được sử dụng khá rộng rãi để thiết kế các phần mềm hiện nay. Theo đó, hệ thống được nhóm thành 3 thành phần chính (Hình 1.6): PTIT Hình 1.6: Mô hình MVC tổng quan - Thành phần Model (M): mô hình, hay còn được gọi với nhiều tên khác như thực thể (entity, bean). Là các lớp chứa thông tin để xử lí của hệ thống. Các thông tin không nên để riêng lẻ mà nên hợp lại thành các lớp thực thể để trao đổi, truyền/nhận, và xử lí giữa các lớp thuộc các phần còn lại như Control và View cho tiện lợi. - Thành phần View (V): trình diễn, hay còn được gọi với các tên khác như giao diện (interface), biên (boundary). C nhiệm vụ hiển thị các form để nhập dữ liệu và hiển thị kết quả xử lí từ hệ thống cho người dùng. - Thành phần Control (C): điều khiển, hay còn được gọi là nghiệp vụ (business). Chứa toàn bộ các hoạt động xử lí, tính toán, điều khiển luồng, điều khiển form, và có thể cả các thao tác truy cập cơ sở dữ liệu.
  16. 2. Case study: thiết kế ứng dụng login theo mô hình MVC Bài toán đặt ra như sau: Xây dựng một ứng dụng cho phép người dung đăng nhập theo tài khoản của mình - Trên giao diện đang nhập có 2 ô văn bản cho phép người dùng nhập username/password, và một nút nhấn Login để người dùng click vào đăng nhập. - Khi người dùng click vào nút Login, hệ thống phải kiểm tra trong cơ sở dữ liệu xem có username/password đấy không. Nếu có thì thông báo thành công, nếu sai thì thông báo username/password không hợp lệ. - Hệ thống phải được thiết kế và cài đặt theo mô hình MVC Hình 1. 7: Sơ đồ lớp của hệ thống Sơ đồ lớp của hệ thống được thiết kế theo mô hình MVC trong Hình 1.7, bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C như sau: - Lớp LoginModel: là PTITlớp tương ứng với thành phần model (M), bao gồm hai thuộc tính username và password, các hàm khởi tạo và các cặp getter/setter tương ứng với các thuộc tính. - Lớp LoginView: là lớp tương ứng với thành phần view (V), là lớp form nên phải kế thừa từ lớp JFrame của Java, nó chứa các thuộc tính là các thành phần đồ họa bao gồm ô text nhập username, ô text nhập password, nút nhất Login. - Lớp LoginControl: là lớp tướng ứng với thành phần control (C), nó chứa một lớp nội tại là LoginListener. Khi nút Login trên tầng view bị click thì nó sẽ chuyển tiếp sự kiện xuống lớp nội tại này để xử lí. Tất cả các xử lí đều gọi từ trong phương thức actionPerformed của lớp nội tại này. Điều này đảm bảo nguyên tắc control điều khiển các phần còn lại trong hệ thống, đúng theo nguyên tắc của mô hình MVC. Tuần tự các bước xử lí như sau:
  17. 1. Người dùng nhập username/password và click vào giao diện của lớp LoginView 2. Lớp Loginview sẽ đóng gói thông tin username/password trên form vào một đối tượng model LoginModel bằng phương thức getUser() và chuyển xuống cho lớp LoginControl xử lí 3. Lớp LoginControl chuyển sang cho lớp nội tại LoginListener xử lí trong phương thức actionPerformed 4. Lớp LoginListener sẽ gọi phương thức checkLogin() của lớp LoginControl để kểm tra thông tin đăng nhập trong cơ sở dữ liệu. 5. Kết quả kiểm tra sẽ được chuyển cho lớp LoginView hiển thị bằng phương thức showMessage() 6. Lớp LoginView hiển thị kết quả đăng nhập lên cho người dùng 3. Cài đặt ứng dụng login theo mô hình MVC Lớp LoginModel.java package login_GUI_MVC; public class LoginModel { private String userName; private String password; public LoginModel(){ } public LoginModel(String username, String password){ this.userName = username; this.password = password; } public String getPassword() { return password; } public void setPassword(StringPTIT password) { this.password = password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } Lớp LoginView.java package login_GUI_MVC; import java.awt.FlowLayout;
  18. import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; public class LoginView extends JFrame implements ActionListener{ private JTextField txtUsername; private JPasswordField txtPassword; private JButton btnLogin; private LoginModel model; public LoginView(){ super("Login MVC"); txtUsername = new JTextField(15); txtPassword = new JPasswordField(15); txtPassword.setEchoChar('*'); btnLogin = new JButton("Login"); JPanel content = new JPanel(); content.setLayout(new FlowLayout()); content.add(new JLabel("Username:")); content.add(txtUsername); content.add(new JLabel("Password:")); content.add(txtPassword); content.add(btnLogin); btnLogin.addActionListener(this); this.setContentPane(content); this.pack(); this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.PTITexit(0); } }); } public void actionPerformed(ActionEvent e) { } public LoginModel getUser(){ model = new LoginModel(txtUsername.getText(), txtPassword.getText()); return model; } public void showMessage(String msg){ JOptionPane.showMessageDialog(this, msg); }
  19. public void addLoginListener(ActionListener log) { btnLogin.addActionListener(log); } } Lớp LoginControl.java package login_GUI_MVC; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class LoginControl { private LoginModel model; private LoginView view; public LoginControl(LoginView view){ this.view = view; view.addLoginListener(new LoginListener()); } class LoginListener implements ActionListener { public void actionPerformed(ActionEvent e) { try { model = view.getUser(); if(checkUser(model)){ view.showMessage("Login succesfully!"); }else{ view.showMessage("Invalid username and/or password!"); } } catch (Exception ex) { view.showMessage(ex.getStackTrace().toString()); } } } PTIT public boolean checkUser(LoginModel user) throws Exception { String dbUrl = "jdbc:mysql://localhost:3306/usermanagement"; String dbClass = "com.mysql.jdbc.Driver"; String query = "Select * FROM users WHERE username ='" + user.getUserName() + "' AND password ='" + user.getPassword() + "'"; try { Class.forName(dbClass); Connection con = DriverManager.getConnection(dbUrl, "root", "12345678"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query);
  20. if (rs.next()) { return true; } con.close(); }catch(Exception e) { throw e; } return false; } } Lớp Test.java package login_GUI_MVC; public class Test { public static void main(String[] args) { LoginView view = new LoginView(); LoginControl controller = new LoginControl(view); view.setVisible(true); } } Kết quả Login thành công: PTIT Login lỗi: VI. KẾT LUẬN
  21. Trong chương này chúng ta đã điểm qua một số kiến thức cơ sở cho lập trình mạng bao gồm kiến thức mạng truyền thông, mô hình lập trình mạng và ngôn ngữ lập trình mạng. Và thông qua chương này sinh viên cũng nắm được mục đích của môn lập trình mạng. Các chương tiếp theo sẽ làm rõ các kỹ thuật lập trình mạng cơ bản và chỉ ra lập trình mạng an toàn bảo mật. Còn những kỹ thuật lập trình mạng phức tạp khác như CORBA, EJB, PORTAL, JAVAMAIL hoặc công nghệ đám mây(cloud) cũng như mô hình đa tầng, kỹ thuật lập trình hướng dịch vụ SOA sẽ được xét trong giáo trình lập trình mạng nâng cao. Còn kỹ thuật lập trình các dịch vụ mạng di động như SMS, MMS, các dịch vụ mạng di động khác và mạng Bluetooth, mạng Sensor, ZeeBig, WUSB, GPS sinh viên sẽ được cung cấp qua môn lập trình thiết bị di động, qua các bài tập thực hành và hệ thống bài tập lớn của môn lập trình mạng. PTIT
  22. PHẦN II. KỸ THUẬT LẬP TRÌNH MẠNG VỚI SOCKET CHƯƠNG II LẬP TRÌNH ỨNG DỤNG MẠNG VỚI SOCKET I. GIỚI THIỆU CHUNG Lập trình ứng dụng mạng với socket là kỹ thuật hiện nay được sử dụng cực kỳ phổ biến trong thực tế. Các ngôn ngữ lập trình mạng hầu hết đều có thư viện hỗ trợ lập trình với socket như: Ngôn ngữ c/c++ có thư viện socket, VC++ có , VB có thư viện WinSock, C# có thư viện system.socket Trong Java các lớp thư viện hỗ trợ lập trình với socket hầu hết nằm trong gói java.net. Khi phát triển các ứng dụng mạng thì java và .NET hỗ trợ rất mạnh đối với socket sử dụng giao thức TCP( TCPsocket) và UDP(UDPsocket), nhưng lập trình Raw socket với java thì cực kỳ phức tạp. Chính vì vậy, khi lập trình các ứng dụng tiện ích mạng như chương trình ping, tracer, hoặc các ứng dụng can thiệt sâu hệ thống mạng mà sử dụng raw socket thì tốt nhất sử dụng ngôn ngữ C/C++(Linux), VC++ hoặc .NET(Windows). Trong chương này chúng tôi sẽ tập trung lập trình ứng dụng mạng sử dụng TCPSocket, UDPSocket và sử dụng ngôn ngữ lập trình Java. Đối với các ứng dụng này, Java hỗ trợ rất mạnh trong các gói java.net, java.nio. Các lớp quan trong nhất trong gói java.net gồm 6 lớp: InetAddress, ServerSocket, Socket, DatagramPacket, DatagramSocket, URL. Với 6 lớp này Java cho phép phát triển tất cả các ứng dụng mạng từ chương trình ứng dụng đơn giản cho đến phức tạp, từ các ứng dụng cỡ nhỏ đến các ứng dụng lớn. Ngoài ra còn một số lớp khác cũng được sử dụng phổ biến như NetworkInterface Sau đây chúng ta sẽ khảo sát những kỹ thuật lập trình mạng cơ bản nhất sử dụng socket trong Java. II. LẬP TRÌNH THAO TÁC VỚI ĐỊA CHỈ MÁY TRẠM 1. Lập trình thao tác với địa chỉ IP 1.1. Lớp InetAddress Java có các lớp quan trọng để thao tác với địa chỉ IP trong gói java.net. Lớp quan trọng nhất là lớp InetAddress. Lớp này cho phépPTIT lấy địa chỉ của một máy trạm bất kỳ trên mạng và cho phép dễ dàng hoán chuyển giữa địa chỉ IP và tên của một máy trạm(host). Mỗi đối tượng InetAddress chứa 2 thành phần chính của một máy trạm là hostname và địa chỉ IP của máy trạm đó. Ngoài ra còn có 2 lớp khác kết thừa trực tiếp từ lớp InetAddress dành cho các phiên bản IPv4 và IPv6 là lớp Inet4Address, Inet6Address và 2 lớp khác là lớp SocketAddress , InetSocketAddress liên quan tới địa chỉ socket .
  23. Hình 2.1. Lớp kế thừa từ lớp InetAddress và SocketAddress Lớp InetAddress được sử dụng phổ biến trong các lớp Socket, ServerSocket, URL, DatagramSocket, DatagramPacket và nó được kế thừa từ lớp Object: public class InetAddress extends Object implements Serializable Đặc điểm của lớp InetAddress là lớp không có cấu tử nên không thể tạo ra đối tượng InetAddress bằng toán tử new. Nhưng bù lại, lớp InetAddress có một số phương thức có thuộc tính static cho phép lấy địa chỉ của máy trạm bất kỳ trên mạng, cụ thể là có các phương thức sau: Tóm tắt các phương thức của lớp InetAddress boolean equals(Object obj) So sánh đối tượng với đối tượng obj byte[] getAddress() Trả về địa chỉ IP chứa trong đối tượng InetAddress dạng mảng byte static InetAddress[] getAllByName(String host) Trả về mảng địa chỉ của tất cả các máy trạm có cùng tên trên mạng static InetAddress getByAddress(byte[] addr) Trả về đối tượng InetAddress tương ứng với địa chỉ IP truyền cho phương thức dưới dạng mảng byte static InetAddress getByAddress(String host,byte[] addr) Tạo đối tượng InetAddress dựa trên tên và địa chỉ IP static InetAddress getByName(String host) Xác định địa chỉ IP của máy trạm từ tên của máy trạm(host) String getCanonicalHostNamePTIT() Lấy tên miền của địa chỉ IP String getHostAddress() Trả về địa chỉ IP chứa trong đối tượng INetAddress là chuỗi dạng a.b.c.d String getHostName() Trả về tên máy trạm chưa trong đối tượng static InetAddress getLocalHost() Lấy đối tượng InetAddress của máy cục bộ int hashCode()
  24. Trả về hashcode của địa chỉ IP cục thể boolean isAnyLocalAddress() Kiểm tra địa chỉ InetAddress có phải địa chỉ wildcard không? boolean isLinkLocalAddress() Kiểm tra địa chỉ có phải là một địa chỉ link-local hay không. boolean isLoopbackAddress() Kiểm tra địa chỉ có phải là địa chỉ Loopback không. boolean isMCGlobal() Kiểm tra địa chỉ multicast có phạm vi toàn cục hay không? boolean isMCLinkLocal() Kiểm tra địa chỉ multicast có phải là địa chỉ có phạm vi liên kết hay không? boolean isMCNodeLocal() Kiểm tra địa chỉ multicast có phải là địa chỉ phạm vi nút mạng hay không? boolean isMulticastAddress() Kiểm tra địa chỉ InetAddress có phải là địa chỉ IP multicast hay không. String toString() Chuyển địa chỉ IP thành chuỗi. Phương thức getByName():PTIT Phương thức này có cú pháp sau: public static InetAddress getByName(String hostName) throws UnknownHostException Phương thức này cho phép trả về địa chỉ của một máy trạm bất kỳ trên mạng được chỉ ra bởi tham số hostName. Tham số này có thể PCname, là tên miền DNS hoặc địa chỉ IP. Trong trường hợp không tồn tại máy trạm có tên chỉ ra trên mạng, phương thức ném trả về ngoại lệ UnknownHostException. Ví dụ đoạn chương trình sau để lấy địa chỉ của máy trạm có tên miền là www.yahoo.com và hiển thị địa chỉ ra màn hình: try { InetAddress address = InetAddress.getByName("www.yahoo.com");
  25. System.out.println(address); } catch (UnknownHostException ex) { System.out.println("Could not find www.yahoo.com"); } Lệnh InetAddress.getByName() sử dụng được do phương thức getByName() có thuộc tính static. Nếu máy trạm với tên miền chỉ ra không tồn tại thì ngoại lệ UnknownHostException được ném trả về và được xử lý. Phương thức getAllByName(): Phương thức này cho phép trả về địa chỉ của tất cả các máy trạm có cùng tên trên mạng dưới dạng là một mảng đối tượng InetAddress. Phương thức có cú pháp sau: InetAddress[] addresses = InetAddress.getAllByName(String name) throws UnknownHostException Ví dụ: Hãy in ra địa chỉ của tất cả các máy trạm trên mạng mà có cùng tên miền www.microsoft.com: //AllAddr.java import java.net.*; public class AllAddr{ public static void main (String[] args) { try { InetAddress[] addresses = InetAddress.getAllByName("www.microsoft.com"); for (int i = 0; i < addresses.length; i++) { System.out.println(addresses[i]); } } catch (UnknownHostExceptionPTIT ex) { System.out.println("Could not find www.microsoft.com"); } } } Dịch chạy chương trình trên máy tính có kết nối mạng Internet, kết quả trả về như sau: www.microsoft.com/63.211.66.123 www.microsoft.com/63.211.66.124 www.microsoft.com/63.211.66.131 www.microsoft.com/63.211.66.117
  26. www.microsoft.com/63.211.66.116 www.microsoft.com/63.211.66.107 www.microsoft.com/63.211.66.118 www.microsoft.com/63.211.66.115 www.microsoft.com/63.211.66.110 Phương thức getLocalHost(): Phương thức này cho phép trả về địa chỉ của máy cục bộ, nếu không tìm thấy nó cũng ném trả về ngoại lệ tượng tự như phưong thức getByName(). Nó cũng có cú pháp: public static InetAddress getLocalHost( ) throws UnknownHostException Ngoài các phương thức static trên, một số phương thức khác cho phép trả về địa chỉ IP hoặc tên của một máy trạm từ đối tượng InetAddress của máy trạm sau khi đã lấy được địa chỉ của máy trạm. Các phương thức tiêu biểu là: Phương thức getHosName():Trả về tên máy trạm từ đối tượng InetAddress của máy trạm đó. Cú pháp: public String getHostName( ) Ví dụ: Cho địa chỉ, in ra tên máy trạm: import java.net.*; public class ReverseTest { public static void main (String[] args) { try { InetAddress ia = InetAddress.getByName("208.201.239.37"); System.out.println(ia.getHostName( )); } catch (Exception ex) { System.err.println(ex); } } } PTIT Phương thức getHostAddress(): Trả về địa chỉ IP của máy trạm từ đối tượng InetAddress tương ứng là chuỗi địa chỉ dạng a.b.c.d. Phương thức có cú pháp: public String getHostAddress( ) Ví dụ: In ra địa chỉ IP của máy cục bộ import java.net.*; public class MyAddress { public static void main(String[] args) { try { InetAddress me = InetAddress.getLocalHost( ); String dottedQuad = me.getHostAddress( ); System.out.println("My address is " + dottedQuad);
  27. } catch (UnknownHostException ex) { System.out.println("I'm sorry. I don't know my own address."); } } } Phương thức getAddress(): Trả về địa chỉ IP của máy trạm từ đối tượng InetAddress của máy trạm tương ứng dưới dạng mảng byte. Phương thức có cú pháp: public byte[] getAddress( ) Ví dụ: Phương thức getVersion() lấy phiên bản địa chỉ IP của máy cục bộ: import java.net.*; public class AddressTests { public static int getVersion(InetAddress ia) { byte[] address = ia.getAddress( ); if (address.length == 4) return 4; else if (address.length == 16) return 6; else return -1; } Lưu ý: Khi in ra các byte địa chỉ IP, nếu giá trị của byte địa chỉ mà vượt qua 127 thi phải cộng với 256 để ra giá trị đúng( vì kiểu byte chỉ có giá trị trong khoảng từ 0128 đến +127), nếu không nó sẽ có giá trị âm. Ví dụ với mảng address trong ví dụ trên: for(int i=0;i 0)?address[i]: (address[i]+256)); Các phương thức khác của lớp InetAddress: public boolean isAnyLocalAddress( ): Phương thức này trả về giá trị true với địa chỉ wildcard, false nếu không phải. Địa chỉ wildcard tương hợp với bất cứ địa chỉ nào của máy cục bộ. Phương thức này quan trọng nếu hệ thốngPTIT cục bộ có nhiều card giao tiếp mạng, nhất là đối với server và gateway. Trong IPv4, địa chỉ wildcard là 0.0.0.0, trong IPv6 là 0:0:0:0:0:0:0:0. public boolean isLoopbackAddress( ): Phương thức này kiểm tra một địa chỉ có phải là địa chỉ loopback hay không, nếu không phải trả về false. Địa chỉ loopback kết nối trực tiếp trong máy trạm trong lớp IP mà không sử dụng bất kỳ phần cứng vật lý nào. Với IPv4, địa chỉ loopback là 127.0.0.1, với IPv6 là 0:0:0:0:0:0:0:1. public boolean isLinkLocalAddress( ): Phương thức này trả về giá trị true nếu một địa chỉ là địa chỉ link-local IPv6, nếu không phải thì trả về giá trị false. Địa chỉ link-local là địa chỉ chỉ được hỗ trợ trong mạng IPv6 để tự cấu hình, tương tự như DHCP trên mạng IPv4 nhưng không cần server. Bộ định tuyến sẽ không cho phép truyền qua các gói tin có địa chỉ này ra khỏi mạng con cục bộ. Tất cả địa chỉ link-local đều bắt đầu với 8 byte: FE80:0000:0000:0000
  28. 8 byte tiếp theo sẽ là địa chỉ cục bộ thường là địa chỉ lấy từ địa chỉ MAC trong thẻ Ethernet(NIC). public boolean isMulticastAddress( ): Trae về true nếu địa chỉ là địa chỉ multicast, nếu không trả về giá trị false. Trong IPv4, địa chỉ multicast nằm trong dải địa chỉ IP: 224.0.0.0- >239.255.255.255(lớp D), trong IPv6 thì chúng được bắt đầu với byte có giá trị FF. 1. 2. Ví dụ sử dụng các phương thức lớp InetAddress Chương trình sau cho phép sử dụng các phương thức của lớp InetAddresss để hiển thị các đặc trưng của một địa chỉ IP được nhập vào từ trên dòng lệnh. Mã chương trình ví dụ được thể hiện như sau: //IPCharacteristics.java import java.net.*; public class IPCharacteristics { public static void main(String[] args) { try { InetAddress address = InetAddress.getByName(args[0]); if (address.isAnyLocalAddress( )) { System.out.println(address + " is a wildcard address."); } if (address.isLoopbackAddress( )) { System.out.println(address + " is loopback address."); } if (address.isLinkLocalAddress( )) { System.out.println(address + " is a link-local address."); } else if (address.isSiteLocalAddress( )) { System.out.println(address + " is a site-local address."); } else { System.out.println(addressPTIT + " is a global address."); } if (address.isMulticastAddress( )) { if (address.isMCGlobal( )) { System.out.println(address + " is a global multicast address."); } else if (address.isMCOrgLocal( )) { System.out.println(address + " is an organization wide multicast address."); } else if (address.isMCSiteLocal( )) { System.out.println(address + " is a site wide multicast
  29. address."); } else if (address.isMCLinkLocal( )) { System.out.println(address + " is a subnet wide multicast address."); } else if (address.isMCNodeLocal( )) { System.out.println(address + " is an interface-local multicast address."); } else { System.out.println(address + " is an unknown multicast address type."); } } else { System.out.println(address + " is a unicast address."); } } catch (UnknownHostException ex) { System.err.println("Could not resolve " + args[0]); } } } Sau khi biên dịch chương trình, chạy chương trình với lệnh: java IPCharacteristics [Enter] III. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI TCPSOCKET 1. Giao thức TCP và cơ chế truyền thông của TCP 2. Một số lớp Java hỗ trợ lập trình với TCPSocket 2.1. Lớp Socket Lớp Socket dùng để tạo đối tượng socket cho phép truyền thông với giao thức TCP hoặc UDP. (Với giao thức UDP người ta thưòng sử dụng lớp DatagramSocket thay vì lớp Socket). 2.1.1. Các cấu tử: public Socket(String host, int port) throws UnknownHostException, IOException Cấu tử này cho phép tạo ra đối tượng Socket truyền thông với giao thức TCP và thực hiện kết nối với máy trạm từ xa có địa chỉ và số cổng được chỉ ra bởi tham số host và port tương ứng. Tham số host có thể là tên máy trạm, tên miền hoặc địa chỉ IP. Nếu không tìm thấy máy trạm từ
  30. xa hoặc đối tuợng Socket không được mở thì nó ném trả về ngoại lệ UnknownHostException hoặc IOException. Ví dụ đoạn chưong trình sau cho phép mở socket và kết nối tới máy trạm từ xa có tên miền www.yahoo.com và số cổng là 80. try { Socket toYahoo = new Socket("www.yahoo.com", 80); // Hoạt động gửi /nhận dữ liệu } catch (UnknownHostException ex) { System.err.println(ex); } catch (IOException ex) { System.err.println(ex); } public Socket(InetAddress host, int port) throws IOException Cấu tử này tương tự như cấu tử trên, nhưng tham số thứ nhất là đối tượng InetAddress của máy trạm từ xa. Đối tượng InetAddress của máy trạm từ xa có thể lấy được bằng phương thức getByName() của lớp InetAddress. public Socket(String host, int port, InetAddress interface, int localPort) throws IOException, UnknownHostException Cấu tử này cho phép tạo ra đối tượng Socket và kết nối với máy trạm từ xa. Hai tham số đầu là tên và số cổng của máy trạm từ xa, 2 tham số sau là giao tiếp mạng vật lý(NIC) hoặc ảo và số cổng được sử dụng trên máy cục bộ. Nếu số cổng cục bộ localPort mà bằng 0 thì Java sẽ chọn sử dụng một số cổng cho phép ngẫu nhiên trong khoảng 1024 đến 65535. public Socket(InetAddress host, int port, InetAddress interface, int localPort) throws IOException Tương tự như cấu tử trên, nhưng tham số thứ nhất là đối tượng InetAddress của máy trạm từ xa. protected Socket( ) PTIT Cấu tử này tạo đối tượng socket mà không kết nối với máy trạm từ xa. Cấu tử này được sử dụng khi chương trình có các socket lớp con. 2.1.2. Một số phương thức quan trọng của lớp Socket public InetAddress getInetAddress( ): Phương thức cho phép trả về địa chỉ của máy trạm từ xa hiện đang kết nối với socket. public int getPort( ): Trả về số cổng trên máy trạm từ xa mà hiện đang kết nối với socket. public int getLocalPort( ): Trả về số cổng trên máy cục bộ public InputStream getInputStream( ) throws IOException: Trả về luồng nhập của socket là đối tượng InputStream.
  31. public OutputStream getOutputStream( ) throws IOException: Trả về luồng xuất của socket là đối tượng OutputStream. public void close( ) throws IOException: Đóng socket 2.1.3. Thiết đặt các tuỳ chọn Socket Tuỳ chọn socket chỉ ra làm thế nào lớp Java Socket có thể gửi /nhận dữ liệu trên native socket. Socket két có các tuỳ chọn sau: TCP_NODELAY SO_BINDADDR SO_TIMEOUT SO_LINGER SO_SNDBUF (Java 1.2 and later) SO_RCVBUF (Java 1.2 and later) SO_KEEPALIVE (Java 1.3 and later) OOBINLINE (Java 1.4 and later) Để thiết lập các tuỳ chọn và trả về trạng thái các tuỳ chọn, lớp socket có các phương thức tương ứng. Ví dụ để thiết đặt và trả về trạng thái tuỳ chọn TCP_NODELAY, lớp Socket có các phương thức sau: public void setTcpNoDelay(boolean on) throws SocketException public boolean getTcpNoDelay( ) throws SocketException 2.2. Lớp ServerSocket Lớp ServerSocket cho phép tạo đối tượng socket phía server và truyền thông với giao thức TCP. Sau khi được tạo ra, nó được đặt ở trạng thái lắng nghe( trạng thái thụ động) chờ tín hiệu kết nới gửi tới từ client. 2.2.1 Các cấu tử public ServerSocket(int port) throws BindException, IOException Cấu tử này cho phép tạo ra đối PTITtượng ServerSocket với số cổng xác định được chỉ ra bởi tham số port. Nếu số cổng port=0 thì nó cho phép sử dụng một số cổng cho phép nào đó(anonymous port ). Cấu tử sẽ ném trả về ngoại lệ khi socket không thể tạo ra được. Socket được tạo bởi cấu tử này cho phép đáp ứng cực đại tới 50 kết nối đồng thời. public ServerSocket(int port, int queueLength) throws IOException, BindException Tương tự như cấu tử trên nhưng cho phép chỉ ra số kết nối cực đại mà socket có thể đáp ứng đồng thời bởi tham số queueLenth. public ServerSocket( ) throws IOException
  32. Cấu tử này cho phép tạo đối tượng ServerSocket nhưng không gắn kết thực sự socket với một số cổng cụ thể nào cả. Và như vậy nó sẽ không thể chấp nhận bất cứ kết nối nào gửi tới. Nó sẽ được gắn kết địa chỉ sau sử dụng phương thức bind(). Ví dụ: ServerSocket ss = new ServerSocket( ); // set socket options SocketAddress http = new InetSocketAddress(80); ss.bind(http); 2.2.2. Phương thức Phương thức accept() Phương thức này có cú pháp sau: public Socket accept( ) throws IOException Phương thức này khi thực hiện nó đặt đối tượng ServerSocket ở trạng thái “nghe” tại số cổng xác định chờ tín hiệu kết nối gửi đến từ client. Khi có tín hiệu kết nối gửi tới phương thức sẽ trả về đối tượng Socket mới để phực vụ kết nối đó. Khi xảy ra lỗi nhập/xuất, phương thức sẽ ném trả về ngoại lệ IOException. Ví dụ: ServerSocket server = new ServerSocket(5776); while (true) { Socket connection = server.accept( ); OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream( )); out.write("You've connected to this server. Bye-bye now.\r\n"); connection.close( ); PTIT } Phương thức close() Phương thức close() có cú pháp sau: public void close( ) throws IOException Phương thức này cho phép đóng soccket và giải phóng tài nguyên cấp cho socket. 3. Kỹ thuật lập trình truyền thông với giao thức TCP
  33. Trong chương trình ứng dụng mạng xây dựng theo mô hình client/server, để chương trình client và chương trình server có thể truyền thông được với nhau thì mỗi phía phải thực hiện tối thiểu các thao tác cơ bản sau đây(Hình 2.2 ): Hình 2.2. Quá trình khởi tạo truyền thông với TCPSocket 3.1. Chương trình phía server: Tạo đối tượng ServerSocket với một số hiệu cổng xác định Đặt đối tượng ServerSocket ở trạng thái nghe tín hiệu đến kết nối bằng phương thức accept(). Nếu có tín hiệu đến kết nối phương thức accept() tạo ra đối tượng Socket mới để phục vụ kết nối đó. Khai báo luồng nhập/xuất cho đối tượng Socket mới( tạo ra ở bước trên). Luồng nhập/xuất có thể là luồng kiểu byte hoặc kiểu char. Thực hiện truyền dữ liệu với client thông qua luồng nhập/xuất Server hoặc client hoặc cả 2 đóng kết nối Server trở về bước 2 và đợi kết nối tiếp theo. 3.2. Chương trình client PTIT Tạo đối tượng Socket và thiết lập kết nối tới server bằng cách chỉ ra các tham số của server. Khai báo lưồng nhập/xuất cho Socket. Luồng nhập/xuất có thể là luồng kiểu byte hoặc kiểu char. Thực hiện truyền dữ liệu qua mạng thông qua luồng nhập/xuất Đóng Socket, giải phóng các tài nguyên khác, kết thúc chương trình nếu cần. Lưu ý: Bình thường chương trình server luôn chạy trước chương trình client Một chương trình server có thể phục vụ nhiều client đồng thời hoặc lặp.
  34. Ví dụ: import java.io.*; import java.net.*; public class EchoClient { public static void main(String[] args) throws IOException { Socket echoSocket = null; PrintWriter out = null; BufferedReader in = null; try { echoSocket = new Socket("taranis", 7); out = new PrintWriter(echoSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader( echoSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println("Don't know about host: taranis."); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for " + "the connection to: taranis."); System.exit(1); } BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in)); String userInput; PTIT while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("echo: " + in.readLine()); } out.close(); in.close(); stdIn.close(); echoSocket.close(); }}
  35. 3.3. Luồng nhập/xuất mạng và đọc/ghi dữ liệu qua luồng nhập/xuất Luồng nhập/xuất mạng cho phép chương trình client và server trao đổi dữ liệu với nhau qua mạng. Luồng nhập/xuất của socket có thể là luồng kiểu byte hoặc kiểu ký tự. Ở đây chúng tôi nêu lên một cách thông dụng nhất tạo luồng kiểu byte và kiểu ký tự để chuơng trình thực hiện đọc ghi dữ liệu với mạng. Luồng kiểu byte Giả sử đối tượng Socket được tạo ra với biến tham chiếu là cl. - Với luồng nhập: + Tạo luồng nhập cho socket: InputStream inp=cl.getInputStream(); + Đọc dữ liệu: Có ba cách -/ Đọc mỗi lần một byte: inp.read() -/Đọc một khối dữ liệu và cất vào mảng b: byte b=new byte[1024]; inp.read(b) hoặc inp.read(b,offset, len) - Với luồng xuất: +Tạo luồng xuất: OutputStream outp=cl.getOutputStream(); + Viết dữ liệu: -/Viết mỗi lần một byte b: outp.write(b); -/ Viết cả khối dữ liệu chứa trong mảng b kiểu byte: //byte[] b; outp.write(b) hoặc outp.write(b,offset,len); Luồng kiểu char: - Với luồng nhập: PTIT +Tạo luồng nhập: BufferedReader inp=new BuferedReader( new InputStreamReader(cl.getInputStream())); + Đọc dữ liệu: -/Đọc từng ký tự: int ch=inp.read() -/ Đọc chuỗi: String s=inp.readLine(); - Với luồng xuất: + Tạo luồng xuất: PrintWriter outp=new PrintWriter(cl.getOutputStream(),true); + Viết dữ liệu:
  36. outp.println( ); 4. Một số ví dụ 4.1. Chương trình quét cổng sử dụng Socket //PortScanner.java import java.net.*; import java.io.*; public class PortScanner { public static void main(String[] args) { String host = "localhost"; if (args.length > 0) { host = args[0]; } try { InetAddress theAddress = InetAddress.getByName(host); for (int i = 1; i < 65536; i++) { Socket connection = null; try { connection = new Socket(host, i); System.out.println("There is a server on port " + i + " of " + host); } catch (IOException ex) { // must not be a server on this port } finally { try { if (connection != null) connection.close( ); } catch (IOExceptionPTIT ex) {} } } // end for } // end try catch (UnknownHostException ex) { System.err.println(ex); } } // end main } // end PortScanner 4.2. Chương trình quét cổng cục bộ dùng lớp ServerSocket import java.net.*;
  37. import java.io.*; public class LocalPortScanner { public static void main(String[] args) { for (int port = 1; port <= 65535; port++) { try { // the next line will fail and drop into the catch block if // there is already a server running on the port ServerSocket server = new ServerSocket(port); } catch (IOException ex) { System.out.println("There is a server on port " + port + "."); } // end catch } // end for } } 4.3. Chương trình finger client Finger là một giao thức truyền thẳng theo RFC 1288, client tạo kết nối TCP tới server với số cổng 79 và gửi một truy vấn on-line tới server. Server đáp ứng truy vấn và đóng kết nối. import java.net.*; import java.io.*; public class FingerClient { public final static int DEFAULT_PORT = 79; public static void main(String[] args) { String hostname = "localhost"; try { hostname = args[0]; } catch (ArrayIndexOutOfBoundsExceptionPTIT ex) { hostname = "localhost"; } Socket connection = null; try { connection = new Socket(hostname, DEFAULT_PORT); Writer out = new OutputStreamWriter( connection.getOutputStream( ), "8859_1"); for (int i = 1; i < args.length; i++) out.write(args[i] + " "); out.write("\r\n"); out.flush( ); InputStream raw = connection.getInputStream( );
  38. BufferedInputStream buffer = new BufferedInputStream(raw); InputStreamReader in = new InputStreamReader(buffer, "8859_1"); int c; while ((c = in.read( )) != -1) { // filter non-printable and non-ASCII as recommended by RFC 1288 if ((c >= 32 && c 0) { hostname = args[0]; } if (args.length > 1) { try { port = Integer.parseInt(args[1]); }
  39. catch (NumberFormatException ex) { // Stay with the default port } } // The time protocol sets the epoch at 1900, // the Java Date class at 1970. This number // converts between them. long differenceBetweenEpochs = 2208988800L; // If you'd rather not use the magic number, uncomment // the following section which calculates it directly. /* TimeZone gmt = TimeZone.getTimeZone("GMT"); Calendar epoch1900 = Calendar.getInstance(gmt); epoch1900.set(1900, 01, 01, 00, 00, 00); long epoch1900ms = epoch1900.getTime( ).getTime( ); Calendar epoch1970 = Calendar.getInstance(gmt); epoch1970.set(1970, 01, 01, 00, 00, 00); long epoch1970ms = epoch1970.getTime( ).getTime( ); long differenceInMS = epoch1970ms - epoch1900ms; long differenceBetweenEpochs = differenceInMS/1000; */ InputStream raw = null; try { Socket theSocket = new Socket(hostname, port); raw = theSocket.getInputStream( ); long secondsSince1900 = 0; for (int i = 0; i < 4; i++) { secondsSince1900 =PTIT (secondsSince1900 << 8) | raw.read( ); } long secondsSince1970 = secondsSince1900 - differenceBetweenEpochs; long msSince1970 = secondsSince1970 * 1000; Date time = new Date(msSince1970); System.out.println("It is " + time + " at " + hostname); } // end try catch (UnknownHostException ex) { System.err.println(ex); } catch (IOException ex) {
  40. System.err.println(ex); } finally { try { if (raw != null) raw.close( ); } catch (IOException ex) {} } } // end main } // end TimeClient //TimeServe.java import java.net.*; import java.io.*; import java.util.Date; public class TimeServer { public final static int DEFAULT_PORT = 37; public static void main(String[] args) { int port = DEFAULT_PORT; if (args.length > 0) { try { port = Integer.parseInt(args[0]); if (port = 65536) { System.out.println("Port must between 0 and 65535"); return; } } catch (NumberFormatException ex) {} } // The time protocol sets the epoch at 1900, // the Date class at 1970.PTIT This number // converts between them. long differenceBetweenEpochs = 2208988800L; try { ServerSocket server = new ServerSocket(port); while (true) { Socket connection = null; try { connection = server.accept( ); OutputStream out = connection.getOutputStream( ); Date now = new Date( ); long msSince1970 = now.getTime( );
  41. long secondsSince1970 = msSince1970/1000; long secondsSince1900 = secondsSince1970 + differenceBetweenEpochs; byte[] time = new byte[4]; time[0]= (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24); time[1 = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16); time[2] = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8); time[3] = (byte) (secondsSince1900 & 0x00000000000000FFL); out.write(time); out.flush( ); } // end try catch (IOException ex) { } // end catch finally { if (connection != null) connection.close( ); } } // end while } // end try catch (IOException ex) { System.err.println(ex); } // end catch } // end main } // end TimeServer 5. Case study: Login từ xa dùng giao thức TCP/IP 5.1 Bài toán Bài toán login từ xa dùng giao thức TCP/IP đặt ra như sau: - Cở sở dữ liệu đợc lưu trữ và quản lí trên server TCP, trong đó có bảng users chứa ít nhất hai cột: cột username và cột password. - Chương trình phía clientPTIT TCP phải hiện giao diện đồ họa, trong đó có một ô text để nhập username, một ô text để nhập password, và một nút nhấn Login. - Khi nút Login được click, chương trình client sẽ gửi thông tin đăng nhập (username/password) trên form giao diện, và gửi sang server theo giao thức TCP - Tại phía server, mỗi khi nhận được thông tin đăng nhập gửi từ client, nó sẽ tiến hành kiểm tra trong cơ sở dữ liệu xem có tài khoản nào trùng với thông tin đăng nhập nhận được hay không. - Sau khi có kết quả kiểm tra (đăng nhập đúng, hoặc sai), server TCP sẽ gửi kết quả này về cho client tương ứng, theo đúng giao thức TCP. - Ở phía client, sau khi nhận được kết quả đăng nhập (đăng nhập đúng, hoặc sai) từ server, nó sẽ hiển thị thông báo tương ứng với kết quả nhận được: nếu đăng nhập
  42. đúng thì thông báo login thành công. Nếu đăng nhập sai thì thông báo là username/password không đúng. - Yêu cầu kiến trúc hệ thống ở cả hai phía client và server đều được thiết kế theo mô hình MVC 5.2 Kiến trúc hệ thống theo mô hình MVC Vì hệ thống được thiết kế theo mô hình client/server dùng giao thức TCP/IP nên mỗi phía client, server sẽ có một sơ đồ lớp riêng, các sơ đồ này được thiết kế theo mô hình MVC. 5.2.1 Sơ đồ lớp phía client Hình 2.3: Sơ đồ lớp phía client TCP/IP Sơ đồ lớp của phía client được thiết kế theo mô hình MVC trong Hình 2.3, bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C như sau: - Lớp User: là lớp tươPTITng ứng với thành phần model (M), bao gồm hai thuộc tính username và password, các hàm khởi tạo và các cặp getter/setter tương ứng với các thuộc tính. - Lớp ClientView: là lớp tương ứng với thành phần view (V), là lớp form nên phải kế thừa từ lớp JFrame của Java, nó chứa các thuộc tính là các thành phần đồ họa bao gồm ô text nhập username, ô text nhập password, nút nhất Login. - Lớp ClientControl: là lớp tương ứng với thành phần control (C), nó chứa một lớp nội tại là LoginListener. Khi nút Login trên tầng view bị click thì nó sẽ chuyển tiếp sự kiện xuống lớp nội tại này để xử lí. Tất cả các xử lí đều gọi từ trong phương thức actionPerformed của lớp nội tại này, bao gồm: lấy thông tin trên form giao diện và gửi sang server theo giao thức TCP/IP, nhận kết quả đăng nhập từ server về và yêu cầu
  43. form giao diện hiển thị. Điều này đảm bảo nguyên tắc control điều khiển các phần còn lại trong hệ thống, đúng theo nguyên tắc của mô hình MVC. 5.2.2 Sơ đồ lớp phía server Sơ đồ lớp của phía server được thiết kế theo mô hình MVC trong Hình 2.4, bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C như sau: - Lớp User: là lớp thực thể, dùng chung thống nhất với lớp phía bên client. - Lớp ServerView: là lớp tương ứng với thành phần view (V), là lớp dùng hiển thị các thông báo và trạng thái hoạt động bên server TCP. - Lớp ServerControl: là lớp tương ứng với thành phần control (C), nó đảm nhiệm vai trò xử lí của server TCP, bao gồm: nhận thông tin đăng nhập từ phía các client, kiểm tra trong cơ sở dữ liệu xem các thng tin này đúng hay sai, sau đó gửi kết quả đăng nhập về cho client tương ứng. PTIT Hình 2.4: Sơ đồ lớp phía server TCP/IP 5.2.3 Tuần tự các bước thực hiện
  44. Hình 2.5: Tuần tự các bước thực hiện theo giao thức TCP/IP Tuần tự các bước xử lí như sau (Hình 2.5): 1. Ở phía client, người dùng nhập username/password và click vào giao diện của lớp ClientView 2. Lớp ClientView sẽ đóng gói thông tin username/password trên form vào một đối tượng model User bằng phương thức getUser() và chuyển xuống cho lớp ClientControl xử lí 3. Lớp ClientControl gửi thông tin chứa trong đối tượng User này sang phía server để kiểm tra đăng nhập 4. Bên phía server, khi nhận được thông tin đăng nhập trong đối tượng User, nó sẽ gọi phương thức checkLogin() để kểm tra thông tin đăng nhập trong cơ sở dữ liệu. 5. Kết quả kiểm tra sẽ được trả về cho lớp ClientControl 6. Ở phía client, khi nhận được kết quả kiểm tra đăng nhập, lớp ClientControl sẽ chuyển cho lớp LoginView hiển thị bằng phương thức showMessage() 7. Lớp LoginView PTIThiển thị kết quả đăng nhập lên cho người dùng 5.3 Cài đặt 5.3.1 Các lớp phía client User.java package tcp.client; import java.io.Serializable; public class User implements Serializable{ private String userName; private String password; public User(){ }
  45. public User(String username, String password){ this.userName = username; this.password = password; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } ClientView.java package tcp.client; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; public class ClientView extends JFrame implements ActionListener{ private JTextField txtUsername; private JPasswordFieldPTIT txtPassword; private JButton btnLogin; public ClientView(){ super("TCP Login MVC"); txtUsername = new JTextField(15); txtPassword = new JPasswordField(15); txtPassword.setEchoChar('*'); btnLogin = new JButton("Login"); JPanel content = new JPanel(); content.setLayout(new FlowLayout()); content.add(new JLabel("Username:")); content.add(txtUsername); content.add(new JLabel("Password:")); content.add(txtPassword);
  46. content.add(btnLogin); this.setContentPane(content); this.pack(); this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } public void actionPerformed(ActionEvent e) { } public User getUser(){ User model = new User(txtUsername.getText(), txtPassword.getText()); return model; } public void showMessage(String msg){ JOptionPane.showMessageDialog(this, msg); } public void addLoginListener(ActionListener log) { btnLogin.addActionListener(log); } } ClientControl.java package tcp.client; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; public class ClientControl { private ClientView view; private String serverHostPTIT = "localhost"; private int serverPort = 8888; public ClientControl(ClientView view){ this.view = view; this.view.addLoginListener(new LoginListener()); } class LoginListener implements ActionListener { public void actionPerformed(ActionEvent e) { try { User user = view.getUser(); Socket mySocket = new Socket(serverHost, serverPort); ObjectOutputStream oos = new ObjectOutputStream(mySocket.getOutputStream()); oos.writeObject(user);
  47. ObjectInputStream ois = new ObjectInputStream(mySocket.getInputStream()); Object o = ois.readObject(); if(o instanceof String){ String result = (String)o; if(result.equals("ok")) view.showMessage("Login succesfully!"); else view.showMessage("Invalid username and/or password!"); } mySocket.close(); } catch (Exception ex) { view.showMessage(ex.getStackTrace().toString()); } } } } ClientRun.java package tcp.client; public class ClientRun { public static void main(String[] args) { ClientView view = new ClientView(); ClientControl control = new ClientControl(view); view.setVisible(true); } } 5.3.2 Các lớp phía server ServerView.java package tcp.server; public class ServerView { public ServerView(){ } public void showMessage(String msg){ System.out.println(msg);PTIT } } ServerControl.java package tcp.server; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement;
  48. import tcp.client.User; public class ServerControl { private ServerView view; private Connection con; private ServerSocket myServer; private Socket clientSocket; private int serverPort = 8888; public ServerControl(ServerView view){ this.view = view; getDBConnection("usermanagement", "root", "12345678"); openServer(serverPort); view.showMessage("TCP server is running "); while(true){ listenning(); } } private void getDBConnection(String dbName, String username, String password){ String dbUrl = "jdbc:mysql://localhost:3306/" + dbName; String dbClass = "com.mysql.jdbc.Driver"; try { Class.forName(dbClass); con = DriverManager.getConnection (dbUrl, username, password); }catch(Exception e) { view.showMessage(e.getStackTrace().toString()); } } private void openServer(int portNumber){ try { myServer = new ServerSocket(portNumber); }catch(IOException e) { view.showMessage(e.toString()); } } private void listenning(){PTIT try { clientSocket = myServer.accept(); ObjectInputStream ois = new ObjectInputStream(clientSocket.getInputStream()); ObjectOutputStream oos = new ObjectOutputStream(clientSocket.getOutputStream()); Object o = ois.readObject(); if(o instanceof User){ User user = (User)o; if(checkUser(user)){ oos.writeObject("ok"); } else oos.writeObject("false");
  49. } }catch (Exception e) { view.showMessage(e.toString()); } } private boolean checkUser(User user) throws Exception { String query = "Select * FROM users WHERE username ='" + user.getUserName() + "' AND password ='" + user.getPassword() + "'"; try { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query); if (rs.next()) { return true; } }catch(Exception e) { throw e; } return false; } } ServerRun.java package tcp.server; public class ServerRun { public static void main(String[] args) { ServerView view = new ServerView(); ServerControl control = new ServerControl(view); } } 5.5 Kết quả PTIT Login thành công:
  50. Login lỗi: IV. LẬP TRÌNH ỨNG DỤNG MẠNG VỚI UDPSOCKET 1. Giao thức UDP và cơ chế truyền thông của UDP 2. Một số lớp Java hỗ trợ lập trình với UDPSocket 2.1. Lớp DatagramPacket Lớp này cho phép tạo gói tin truyền thông với giao thức UDP. Lớp này kết thừa trực tiếp từ lớp Object. public final class DatagramPacket extends Object Gói tin là đối tượng của lớp này chứa 4 thành phần quan trọng: Địa chỉ, dữ liệu truyền thật sự, kích thước của gói tin và số hiệu cổng chứa trong gói tin. 2.1.1. Cấu tử Lớp này có các cấu tử tạo gói tin gửi và gói tin nhận khác nhau: * Cấu tử tạo gói tin nhận từ mạng: public DatagramPacket(byte[] inBuffer, int length) Tham số: . inBuffer: Bộ đệm nhập, chứa dữ liệu của gói tin nhận . length: kích cỡ của dữ liệu của gói tin nhận, nó thường được xác định bằng lệnh: length= buffer.length. PTIT Ví dụ tạo gói tin nhận: byte[] inBuff=new byte[512];//bộ đệm nhập DatagramPacket inData=new DatagramPacket(inBuf, inBuff.length); * Cấu tử tạo gói tinh gửi: public DatagramPacket(byte[] outBuffer , int length, InetAddress destination, int port) Tham số: . outBuffer: Bộ đệm xuất chưa dữ liệu của gói tin gửi . length: kích cỡ dữ liệu của gói tin gửi tính theo số byte và thường bằng outBuffer.length.
  51. . destination: Địa chỉ nơi nhận gói tin . port: Số hiệu cổng đích, nơi nhận gói tin. Ví dụ: String s=” Hello World!”; //Bộ đệm xuất và gán dữ liệu cho bộ đệm xuất byte[] outBuff=s.getBytes(); //Địa chỉ đích InetAddress addrDest=InetAddress.getByName(“localhost”); //Số cổng đích int portDest=3456; //Tạo gói tin gửi DatagramPacket outData=new DatagramPacket(outBuff, outBuff.length, addrDest, portDest); 2.1.2. Phương thức . public InetAddress getAddress( ): Phương thức này trả về đối tượng InetAddress của máy trạm từ xa chứa trong gói tin nhận. . public int getPort( ): Trả về số hiệu cổng của máy trạm từ xa chứa trong gói tin. . public byte[] getData( ): Trả về dữ liệu chứa trong gói tin dưới dạng mảng byte. . public int getLength( ): Trả về kích cỡ của dữ liệu chưa trong gói tin tính theo số byte. Tương ứng với 4 phương thức getXXXX (), lớp DatagramPacket có 4 phương thức setXXXX () để thiết lập 4 tham số cho gói tin gửi. 2.2. Lớp DatagramSocket Lớp DatagramSocket cho phép tạo ra đối tượng socket truyền thông với giao thức UDP. Socket này cho phép gửi/nhận gói tin DatagramPacket. Lớp này được khai báo kế thừa từ lớp Object. public class DatagramSocket extends Object 2.2.1. Các cấu tử (phương thức khởi tạo) . public DatagramSocket( ) throPTITws SocketException: Cấu tử này cho phép tạo ra socket với số cổng nào đó(anonymous) và thường được sử dụng phía chương trình client. Nếu tạo socket không thành công, nó ném trả về ngoại lệ SocketException. Ví dụ: try { DatagramSocket client = new DatagramSocket( ); // send packets }
  52. catch (SocketException ex) { System.err.println(ex); } . public DatagramSocket(int port) throws SocketException: Cấu tử này cho phép tạo socket với số cổng xác định và chờ nhận gói tín truyền tới. Cấu tử này được sử dụng phía server trong mô hình client/server. Ví dụ chương trình sau sẽ cho phép hiển thị các cổng cục bộ đã được sử dụng: //UDPPortScanner.java import java.net.*; public class UDPPortScanner { public static void main(String[] args) { for (int port = 1024; port 0) { hostname = args[0];
  53. try { port = Integer.parseInt(args[1]); } catch (Exception ex) { // use default port } } else { hostname = "localhost"; } try { InetAddress server = InetAddress.getByName(hostname); BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in)); DatagramSocket theSocket = new DatagramSocket( ); while (true) { String theLine = userInput.readLine( ); if (theLine.equals(".")) break; byte[] data = theLine.getBytes( ); DatagramPacket theOutput = new DatagramPacket(data, data.length, server, port); theSocket.send(theOutput); } // end while } // end try catch (UnknownHostException uhex) { System.err.println(uhex); } catch (SocketException sex) { System.err.println(sex); } PTIT catch (IOException ioex) { System.err.println(ioex); } } // end main } . public void receive(DatagramPacket dp) throws IOException: Phương thức nhận gói tin UDP qua mạng. Ví dụ chương trình sau sẽ tạo đối tượng DatagramSocket với số cổng xác định, nghe nhận gói dữ liệu gửi đến, hiển thị nội dung gói tin và địa chỉ, số cổng của máy trạm gửi gói tin.
  54. //UDPDiscardServer.java import java.net.*; import java.io.*; public class UDPDiscardServer { public final static int DEFAULT_PORT = 9; public final static int MAX_PACKET_SIZE = 65507; public static void main(String[] args) { int port = DEFAULT_PORT; byte[] buffer = new byte[MAX_PACKET_SIZE]; try { port = Integer.parseInt(args[0]); } catch (Exception ex) { // use default port } try { DatagramSocket server = new DatagramSocket(port); DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while (true) { try { server.receive(packet); String s = new String(packet.getData( ), 0, packet.getLength( )); System.out.println(packet.getAddress( ) + " at port " + packet.getPort( ) + " says " + s); // reset the length for the next packet packet.setLength(buffer.length); } catch (IOException ex) { System.err.println(ex); } PTIT } // end while } // end try catch (SocketException ex) { System.err.println(ex); } // end catch } // end main } . public void close( ): Phương thức đóng socket. Các phương thức khác thể hiện trong bảng sau:
  55. Một số phương thức của lớp DatagramSocket void bind(SocketAddress addr) Gắn kết DatagramSocket với địa chỉ và số cổng cụ thể void connect(InetAddress address,int port) Kết nối socket với địa chỉ máy trạm từ xa void connect(SocketAddress addr) Kết nối socket với địa chỉ socket từ xa. void disconnect() Huỷ bỏ kết nối boolean isBound() Trả về trạng thái kết nối của socket. boolean isClosed() Kiểm tra socket đã đóng hay chưa boolean isConnected() Kiểm tra trạng thái kết nối 3. Kỹ thuật lập trình truyền thông với giao thức UDP Trong mô hình client/server, để chương trình client và server có thể truyền thông được với nhau, mỗi phía phải thực hiện một số thaoPTIT tác cơ bản sau đây(Hình 2.3) Hình 2.6. Quá trình khởi tạo truyền thông UDPSocket 3.1. Phía server: . Tạo đối tượng DatagramSocket với số cổng xác định được chỉ ra . Khai báo bộ đệm nhập /xuất inBuffer/outBuffer dạng mảng kiểu byte
  56. . Khai báo gói tin nhận gửi inData/outData là đối tượng DatagramPacket. . Thực hiện nhận/gửi gói tin với phương thức receive()/send() . Đóng socket, giải phóng các tài nguyên khác, kết thúc chương trình nếu cần, không quay về bước 3. 3.2. Phía client . Tạo đối tượng DatagramSocket với số cổng nào đó . Khai báo bộ đệm xuất/nhập outBuffer/inBuffer dạng mảng kiểu byte . Khai báo gói tin gửi/nhận outData/inData là đối tượng DatagramPacket. . Thực hiện gửi /nhận gói tin với phương thức send()/receive() . Đóng socket, giải phóng các tài nguyên khác, kết thúc chương trình nếu cần, không quay về bước 3. 3.3. Một số lưu ý: . Chương trình server phải chạy trước chương trình client và chương trình client phải gửi gói tin đến server trước. Để từ gói tín nhận được phía server, server mới tách được địa chỉ và số hiệu cổng phía client , từ đó mới tạo gói tin gửi cho client. . Chương trình server có thể phục vụ nhiều máy khách kiểu lặp. 4. Một số chương trình ví dụ 4.1. Chương trình minh hoạ //UDPEchoClient.java import java.net.*; import java.io.*; public class UDPEchoClient { public final static int DEFAULT_PORT = 7; public static void main(String[] args) { String hostname = "localhost"; int port = DEFAULT_PORT; if (args.length >PTIT 0) { hostname = args[0]; } try { InetAddress ia = InetAddress.getByName(hostname); Thread sender = new SenderThread(ia, DEFAULT_PORT); sender.start( ); Thread receiver = new ReceiverThread(sender.getSocket( )); receiver.start( ); } catch (UnknownHostException ex) { System.err.println(ex);
  57. } catch (SocketException ex) { System.err.println(ex); } } // end main } //UDPEchoServer.java import java.net.*; import java.io.*; public class extends UDPServer { public final static int DEFAULT_PORT = 7; public UDPEchoServer( ) throws SocketException { super(DEFAULT_PORT); } public void respond(DatagramPacket packet) { try { DatagramPacket outgoing = new DatagramPacket(packet.getData( ), packet.getLength( ), packet.getAddress( ), packet.getPort( )); socket.send(outgoing); } catch (IOException ex) { System.err.println(ex); } } public static void main(String[] args) { try { UDPServer server = new UDPEchoServer( ); server.start( ); } PTIT catch (SocketException ex) { System.err.println(ex); } } } V. LẬP TRÌNH VỞI THẺ GIAO TIẾP MẠNG(NIC) 1. Giới thiệu về thẻ giao tiếp mạng( network interface card-NIC) Thẻ giao tiếp mạng là điểm liên kết giữa máy tính với mạng riêng hoặc mạng công cộng. Giao tiếp mạng nới chung là một thẻ giao tiếp mạng(NIC) nhưng nó cũng có thể không phải giao tiếp vật lý. Mà thay vào đó giao tiếp mạng có thể được thực hiện trong dạng phần mềm. Ví dụ giao
  58. tiếp loopback(127.0.0.1 đối với IPv4 và ::1 đối với IPv6) không phải là dạng thiết bị vật lý mà là một phần mềm phỏng theo giao tiếp mạng vật lý. Giao tiếp loopback noi chung được sử dụng trong môi trường thử nghiệm. 2. Lớp NetworkInterface Lớp này dùng cho cả thẻ giao tiếp vật lý như Ethernet Card hoặc thẻ giao tiếp ảo mà được tạo ra tương tự giống như thẻ giao tiếp vật lý. Lớp NetworkInterface cung cấp các phương thức để liệt kê tất cả các địa chỉ cục bộ và tạo ra đối tượng InetAddress từ chúng. Các đối tượng InetAddress này có thể được sử dụng để tạo các socket, server socket Đối tượng NetworkInterface thể hiện phần cứng vật lý hoặc địa chỉ ảo và chúng không thể được xây dựng tuỳ ý. Cũng tương tự như lớp InetAdddress, nó cũng có một số phương thức có thuộc tính static cho phép trả về đối tượng NetworkInterface gắn kết với bộ giao tiếp mạng cụ thể. Sau đây chúng ta sẽ khảo sát một số phương thức quan trọng của lớp NetworkInterface. 2.1. Các phương thức static Phương thức getByName(): Cú pháp: public static NetworkInterface getByName(String name) throws SocketException Phương thức này trả về đối tượng NetworkInterface biểu diễn một bộ giao tiếp mạng với tên cụ thể. Nếu không có tên đó thì nó trả về giá trị null. Nếu các tầng mạng nền tảng xẩy ra vấn đề, phương thức trả về ngoại lệ SocketException. Dạng tên giao tiếp mạng phụ thuộc vào nền cụ thể. Với hệ điều hành Unix, tên của giao tiếp Ethernet có dạng eth0, eth1, Địa chỉ loopback cục bộ có thể đặt tên chẳng hạn như "lo". Đối với hệ điều hành Windows, tên là các chuỗi "CE31", "ELX100" mà được lấy từ các nhà cung cấp và mô hình phần cứng trên phần cứng giao tiếp mạng đó. Ví dụ đoạn chương trình sau thực hiện tìm giao tiếp mạng Etthernet cơ sở trên hệ điều hành Unix: try { NetworkInterface niPTIT = NetworkInterface.getByName("eth0"); if (ni == null) { System.err.println("No such interface: eth0" ); } } catch (SocketException ex) { System.err.println("Could not list sockets." );
  59. } Phương thức getByInetAddress(): Cú pháp: public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException Phương thức này trả về đối tượng NeworkInterface biểu diễn giao tiếp mạng được gắn với với một địa chỉ IP cụ thể, Nếu không có giao tiếp mạng gắn với địa chỉ IP đó trên máy trạm cục bộ thì nó trả về null. Khi xẩy ra lỗi nó ném trả về ngoại lệ SocketException. ví dụ đoạn chương trình sau minh hoạ cách sử dụng phương thức để tìm giao tiếp mạng đối với địa chỉ loopback cục bộ: try { InetAddress local = InetAddress.getByName("127.0.0.1"); NetworkInterface ni = NetworkInterface.getByName(local); if (ni == null) { System.err.println("That's weird. No local loopback address."); } } catch (SocketException ex) { System.err.println("Could not list sockets." ); } catch (UnknownHostException ex) { System.err.println("That's weird. No local loopback address."); } Phương thức getNetworkInterfaces( ): Cú pháp: public static Enumeration getNetworkInterfaces( ) throws SocketException Phương thức này trả về đối tượng java.util.Enumeration là một danh sách liệt kê tất cả các giao tiếp mạng có trên máy cục bộ. PTITChương trình ví dụ sau minh hoạ cách sử dụng phương thức để đưa ra một danh sách tất cả các giao tiếp mạng trên máy cục bộ: //InterfaceLister.java import java.net.*; import java.util.*; public class InterfaceLister { public static void main(String[] args) throws Exception { Enumeration interfaces = NetworkInterface.getNetworkInterfaces( ); while (interfaces.hasMoreElements( )) { NetworkInterface ni = (NetworkInterface) interfaces.nextElement( ); System.out.println(ni);
  60. } } } 2.2. Các phương thức khác: public Enumeration getInetAddresses( ): Phương thức này trả về đối tượng java.util.Enumeration chứa đối tượng InetAddress đối với mỗi địa chỉ IP mà giao tiếp mạng được với nó. Mà mỗi giao tiếp mạng đơn có thể gắn với các địa chỉ IP khác nhau. Ví dụ sau hiển thị tất cả các địa chỉ IP gắn với giao diện mạng eth0: NetworkInterface eth0 = NetworkInterrface.getByName("eth0"); Enumeration addresses = eth0.getInetAddresses( ); while (addresses.hasMoreElements( )) { System.out.println(addresses.nextElement( )); } public String getName( ): Phương thức này trả về tên của đối tượng NetworkInterface cụ thể, chẳng hạn như eth0 hoặc lo. public String getDisplayName( ): Phương thức trả về tên "thân thiện" hơn của một giao tiếp mạng cụ thể. Trong mạng Unix, nó trả về chuỗi giống như phương thức getName(), Trong mạng Windows, no trả về chuỗi tên "thân thiện" như "Local Area Connection" hoặc "Local Area Connection 2". Ngoài ra trong lớp NetworkInterface còn định nghĩa các phương thức equals(), hashCode(), toString(). 3. Lập trình với giao tiếp mạng(NIC) Lớp NetworkInterface thể hiện cả 2 kiểu giao diện vật lý và giao tiếp mềm. Lớp này đầy hữu ích đối với các hệ thống multihome có nhiều NIC. Với lớp này, chương trình có thể chỉ ra NIC cho một hoạt động mạng cụ thể. Để gửi dữ liệu, hệ thống xác định giao tiếp nào sẽ được sử dụng. Nhưng cũng có thể truy vấn hệ thống đối với các giao tiếp phù hợp và tìm một địa chỉ trên giao tiếp muốn sử dụng. Khi chương trình tạo ra một socket và gắn nóPTIT với địa chỉ đó, hệ thống sẽ sử dụng giao tiếp được gắn kết đó. Ví dụ: NetworkInterface nif = NetworkInterface.getByName("bge0"); Enumeration nifAddresses = nif.getInetAddresses(); Socket soc = new java.net.Socket(); soc.bind(nifAddresses.nextElement()); soc.connect(new InetSocketAddress(address, port)); Người sử dụng cũng có thể sử dụng NetworkInterface để nhận biết giao tiếp cục bộ mà một nhóm multicast được ghép nối, ví dụ: NetworkInterface nif = NetworkInterface.getByName("bge0"); MulticastSocket() ms = new MulticastSocket(); ms.joinGroup(new InetSocketAddress(hostname, port) , nif);
  61. 3.1. Lấy các giao tiếp mạng Lớp NetworkInterface không có cấu tử public. Do đó không thể tạo được đối tượng với toán tử new. Thay vào đó nó có các phương thức static(giống InetAddress) cho phép lấy được các chi tiết giao tiếp từ hệ thống: getByInetAddress(), getByName() và getNetworkInterfaces(). Hai phương thức đầu tiên được sử dụng khi có sẵn địa chỉ IP hoặc tên của giao tiếp mạng cục thể. Phương thức thứ 3, getNetworkInterfaces( ), trả về một danh sách đầy đủ các giao tiếp mạng trên máy tính. Giao tiếp mạng cũng có thể tổ chức theo kiểu phân cấp. Lớp NetworkInterface sử dụng 2 phương thức getParent() và getSubInterface() đối với cấu trúc giao tiếp mạng phân cấp. Nếu giao tiếp mạng là giao tiếp con, getParent() trả về giá trị none-null. Phương thức getSubInterfaces() sẽ trả về tất cả các giao tiếp con của giao tiếp mạng. Ví dụ sau đây sẽ hiển thị tên của tất cả các giao tiếp mạng và giao tiếp con(n nếu nó tồn tại) trên một máy: //ListNIFs.java import java.io.*; import java.net.*; import java.util.*; import static java.lang.System.out; public class ListNIFs { public static void main(String args[]) throws SocketException { Enumeration nets = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface netIf : Collections.list(nets)) { out.printf("Display name: %s\n", netIf.getDisplayName()); out.printf("Name: %s\n", netIf.getName()); displaySubInterfaces(netIf); out.printf("\n"); } } static void displaySubInterfaces(NetworkInterface netIf) throws SocketException { Enumeration subIfs = netIf.getSubInterfaces(); for (NetworkInterface subIf : Collections.list(subIfs)) { out.printf("PTIT\tSub Interface Display name: %s\n", subIf.getDisplayName()); out.printf("\tSub Interface Name: %s\n", subIf.getName()); } } } Kết quả chạy trên máy tính của chúng tôi hiện ra như sau: Display name: bge0 Name: bge0 Sub Interface Display name: bge0:3 Sub Interface Name: bge0:3 Sub Interface Display name: bge0:2 Sub Interface Name: bge0:2 Sub Interface Display name: bge0:1 Sub Interface Name: bge0:1 Display name: lo0
  62. Name: lo0 3.2. Lấy danh sách địa chỉ giao tiếp mạng Một phần thông tin cực kỳ hữu ích mà người sử dụng cần lấy được từ giao tiếp mạng là danh sách địa chỉ IP mà được gắn cho các giao tiếp mạng. Người sử dụng có thể thu được thông tin từ một thể hiện NetworkInterface bằng cách sử dụng một trong 2 phương thức sau: Phương thức getInetAddresses() trả về một Enumeration của các đối tượng InetAddress, còn phương thức getInterfaceAddresses() trả về một danh sách của các thể hiện java.net.InterfaceAddress. Phương thứcc này được sử dụng khi người sử dụng cần thông tin nhiều hơn về địa chỉ giao tiếp ngoài địa chỉ IP của nó. Ví dụ, khi bạn cần thông tin bổ sung về mặt nạ mạng con và địa chỉ broardcast khi địa chỉ là một địa chỉ IPv4 và chiều dài prefix mạng trong địa chỉ IPv6. Ví dụ sau đây hiển thị danh sách tất cả các giao tiếp mạng và địa chỉ của chúng trên một máy: import java.io.*; import java.net.*; import java.util.*; import static java.lang.System.out; public class ListNets { public static void main(String args[]) throws SocketException { Enumeration nets = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface netint : Collections.list(nets)) displayInterfaceInformation(netint); } static void displayInterfaceInformation(NetworkInterface netint) throws SocketException { out.printf("Display name: %s\n", netint.getDisplayName()); out.printf("Name: %s\n", netint.getName()); Enumeration inetAddresses = netint.getInetAddresses(); for (InetAddress inetAddress : Collections.list(inetAddresses)) { out.printf("InetAddress: %s\n", inetAddress); } out.printf("\n"); } PTIT } Kết quả chạy chương trình trên máy tính của chúng tôi như sau: Display name: bge0 Name: bge0 InetAddress: /fe80:0:0:0:203:baff:fef2:e99d%2 InetAddress: /121.153.225.59 Display name: lo0 Name: lo0 InetAddress: /0:0:0:0:0:0:0:1%1 InetAddress: /127.0.0.1 3.3. Truy cập các tham số giao tiếp mạng
  63. Người sử dụng có thể truy cập các tham số về giao tiếp mạng ngoài tên và địa chỉ IP gán cho nó. Và chương trình có thể phát hiện giao tiếp mạng đang chạy với phương thức isUp(). các phương thức sau chỉ thị kiểu giao tiếp mạng: isLoopback(): chỉ thị giao tiếp mạng là một giao tiếp loopback. isPointToPoint() chỉ thị nếu giao tiếp là giao tiếp point-to-point. isVirrtual(): chỉ thị nếu giao tiếp là giao tiếp ảo(giao tiếp mềm). Phương thức supportsMulticast() chỉ thị một khi giao tiếp mạng hỗ trợ multicast. Phương thức getHardwareAddress() trả về địa chỉ phần cứng vật lý của giao tiếp mạng, địa chỉ MAC, khi nó có khả năng. Phương thức getMTU() trả về đơn vị truyền cực đại(MTU) là kích cỡ gói tin lớn nhất. Ví dụ sau mở rộng của ví dụ trên bằng cách thêm các tham số mạng bổ sung: //ListNetsEx.java import java.io.*; import java.net.*; import java.util.*; import static java.lang.System.out; public class ListNetsEx { public static void main(String args[]) throws SocketException { Enumeration nets = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface netint : Collections.list(nets)) displayInterfaceInformation(netint); } static void displayInterfaceInformation(NetworkInterface netint) throws SocketException { out.printf("Display name: %s\n", netint.getDisplayName()); out.printf("Name: %s\n", netint.getName()); Enumeration inetAddresses = netint.getInetAddresses(); for (InetAddress inetAddress : Collections.list(inetAddresses)) { out.printf("InetAddress: %s\n", inetAddress); } out.printf("Up? %s\n", netint.isUp()); out.printf("Loopback? %s\n", netint.isLoopback()); out.printf("PointToPoint?PTIT %s\n", netint.isPointToPoint()); out.printf("Supports multicast? %s\n", netint.supportsMulticast()); out.printf("Virtual? %s\n", netint.isVirtual()); out.printf("Hardware address: %s\n", Arrays.toString(netint.getHardwareAddress())); out.printf("MTU: %s\n", netint.getMTU()); out.printf("\n"); } } Kết quả chạy chương trình trên máy tính của chúng tôi như sau: Display name: bge0 Name: bge0 InetAddress: /fe80:0:0:0:203:baff:fef2:e99d%2
  64. InetAddress: /129.156.225.59 Up? true Loopback? false PointToPoint? false Supports multicast? false Virtual? false Hardware address: [0, 3, 4, 5, 6, 7] MTU: 1500 Display name: lo0 Name: lo0 InetAddress: /0:0:0:0:0:0:0:1%1 InetAddress: /127.0.0.1 Up? true Loopback? true PointToPoint? false Supports multicast? false Virtual? false Hardware address: null MTU: 8232 4. Một số chương trình ví dụ minh hoạ sử dụng lớp NetworkInterface và InetAddress //InetExample.java import java.util.Enumeration; import java.net.*; public class InetExample { public static void main(String[] args) { // Get the network interfaces and associated addresses for this host try { Enumeration interfaceList = NetworkInterface.getNetworkInterfaces(); if (interfaceList == null) { System.out.println(" No interfaces found "); } else { PTIT while (interfaceList.hasMoreElements()) { NetworkInterface iface = interfaceList.nextElement(); System.out.println("Interface " + iface.getName() + ":"); Enumeration addrList = iface.getInetAddresses(); if (!addrList.hasMoreElements()) { System.out.println("\t(No addresses for this interface)"); } while (addrList.hasMoreElements()) { InetAddress address = addrList.nextElement(); System.out.print("\tAddress " + ((address instanceof Inet4Address ? "(v4)"
  65. : (address instanceof Inet6Address ? "(v6)" : "(?)")))); System.out.println(": " + address.getHostAddress()); } } } } catch (SocketException se) { System.out.println("Error getting network interfaces:" + se.getMessage()); } // Get name(s)/address(es) of hosts given on command line for (String host : args) { try { System.out.println(host + ":"); InetAddress[] addressList = InetAddress.getAllByName(host); for (InetAddress address : addressList) { System.out.println("\t" + address.getHostName() + "/" + address.getHostAddress()); } } catch (UnknownHostException e) { System.out.println("\tUnable to find address for " + host); } } } } 5. Case study: Login từ xa dùngPTIT giao thức UDP 5.1 Bài toán Bài toán login từ xa dùng giao thức UDP đặt ra như sau: - Cở sở dữ liệu đợc lưu trữ và quản lí trên server UDP, trong đó có bảng users chứa ít nhất hai cột: cột username và cột password. - Chương trình phía client UDP phải hiện giao diện đồ họa, trong đó có một ô text để nhập username, một ô text để nhập password, và một nút nhấn Login. - Khi nút Login được click, chương trình client sẽ gửi thông tin đăng nhập (username/password) trên form giao diện, và gửi sang server theo giao thức UDP - Tại phía server, mỗi khi nhận được thông tin đăng nhập gửi từ client, nó sẽ tiến hành kiểm tra trong cơ sở dữ liệu xem có tài khoản nào trùng với thông tin đăng nhập nhận được hay không.
  66. - Sau khi có kết quả kiểm tra (đăng nhập đúng, hoặc sai), server UDP sẽ gửi kết quả này về cho client tương ứng, theo đúng giao thức UDP. - Ở phía client, sau khi nhận được kết quả đăng nhập (đăng nhập đúng, hoặc sai) từ server, nó sẽ hiển thị thông báo tương ứng với kết quả nhận được: nếu đăng nhập đúng thì thông báo login thành công. Nếu đăng nhập sai thì thông báo là username/password không đúng. - Yêu cầu kiến trúc hệ thống ở cả hai phía client và server đều được thiết kế theo mô hình MVC 5.2 Kiến trúc hệ thống theo mô hình MVC Vì hệ thống được thiết kế theo mô hình client/server dùng giao thức UDP nên mỗi phía client, server sẽ có một sơ đồ lớp riêng, các sơ đồ này được thiết kế theo mô hình MVC. 5.2.1 Sơ đồ lớp phía client PTIT Hình 2.7: Sơ đồ lớp phía client UDP Sơ đồ lớp của phía client được thiết kế theo mô hình MVC trong Hình 2.7, bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C như sau: - Lớp User: là lớp tương ứng với thành phần model (M), bao gồm hai thuộc tính username và password, các hàm khởi tạo và các cặp getter/setter tương ứng với các thuộc tính. - Lớp ClientView: là lớp tương ứng với thành phần view (V), là lớp form nên phải kế thừa từ lớp JFrame của Java, nó chứa các thuộc tính là các thành phần đồ họa bao gồm ô text nhập username, ô text nhập password, nút nhất Login.
  67. - Lớp ClientControl: là lớp tương ứng với thành phần control (C), nó chứa một lớp nội tại là LoginListener. Khi nút Login trên tầng view bị click thì nó sẽ chuyển tiếp sự kiện xuống lớp nội tại này để xử lí. Tất cả các xử lí đều gọi từ trong phương thức actionPerformed của lớp nội tại này, bao gồm: lấy thông tin trên form giao diện và gửi sang server theo giao thức UDP, nhận kết quả đăng nhập từ server về và yêu cầu form giao diện hiển thị. Điều này đảm bảo nguyên tắc control điều khiển các phần còn lại trong hệ thống, đúng theo nguyên tắc của mô hình MVC. 5.2.2 Sơ đồ lớp phía server Hình 2.8: Sơ đồ lớp phía server UDP Sơ đồ lớp của phía server được thiết kế theo mô hình MVC trong Hình 2.8, bao gồm 3 lớp chính tương ứng với sơ đồ M-V-C nhưPTIT sau: - Lớp User: là lớp thực thể, dùng chung thống nhất với lớp phía bên client. - Lớp ServerView: là lớp tương ứng với thành phần view (V), là lớp dùng hiển thị các thông báo và trạng thái hoạt động bên server UDP. - Lớp ServerControl: là lớp tương ứng với thành phần control (C), nó đảm nhiệm vai trò xử lí của server UDP, bao gồm: nhận thông tin đăng nhập từ phía các client, kiểm tra trong cơ sở dữ liệu xem các thng tin này đúng hay sai, sau đó gửi kết quả đăng nhập về cho client tương ứng. 5.2.3 Tuần tự các bước thực hiện
  68. Hình 2.9: Tuần tự các bước thực hiện theo giao thức UDP Tuần tự các bước xử lí như sau (Hình 2.9): 1. Ở phía client, người dùng nhập username/password và click vào giao diện của lớp ClientView 2. Lớp ClientView sẽ đóng gói thông tin username/password trên form vào một đối tượng model User bằng phương thức getUser() và chuyển xuống cho lớp ClientControl xử lí 3. Lớp ClientControl gửi thông tin chứa trong đối tượng User này sang phía server để kiểm tra đăng nhập 4. Bên phía server, khi nhận được thông tin đăng nhập trong đối tượng User, nó sẽ gọi phương thức checkLogin() để kểm tra thông tin đăng nhập trong cơ sở dữ liệu. 5. Kết quả kiểm tra sẽ được trả về cho lớp ClientControl 6. Ở phía client, khi nhận được kết quả kiểm tra đăng nhập, lớp ClientControl sẽ chuyển cho lớp LoginView hiển thị bằng phương thức showMessage() 7. Lớp LoginView PTIThiển thị kết quả đăng nhập lên cho người dùng 5.3 Cài đặt 5.3.1 Các lớp phía client User.java package udp.client; import java.io.Serializable; public class User implements Serializable{ private String userName; private String password; public User(){ }
  69. public User(String username, String password){ this.userName = username; this.password = password; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } ClientView.java package udp.client; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; public class ClientView extends JFrame implements ActionListener{ private JTextField txtUsername; private JPasswordFieldPTIT txtPassword; private JButton btnLogin; public ClientView(){ super("UDP Login MVC"); txtUsername = new JTextField(15); txtPassword = new JPasswordField(15); txtPassword.setEchoChar('*'); btnLogin = new JButton("Login"); JPanel content = new JPanel(); content.setLayout(new FlowLayout()); content.add(new JLabel("Username:")); content.add(txtUsername); content.add(new JLabel("Password:")); content.add(txtPassword);
  70. content.add(btnLogin); this.setContentPane(content); this.pack(); this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } public void actionPerformed(ActionEvent e) { } public User getUser(){ User model = new User(txtUsername.getText(), txtPassword.getText()); return model; } public void showMessage(String msg){ JOptionPane.showMessageDialog(this, msg); } public void addLoginListener(ActionListener log) { btnLogin.addActionListener(log); } } ClientControl.java package udp.client; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;PTIT public class ClientControl { private ClientView view; private int serverPort = 5555; private int clientPort = 6666; private String serverHost = "localhost"; private DatagramSocket myClient; public ClientControl(ClientView view){ this.view = view; this.view.addLoginListener(new LoginListener()); } class LoginListener implements ActionListener { public void actionPerformed(ActionEvent e) { openConnection();
  71. User user = view.getUser(); sendData(user); String result = receiveData(); if(result.equals("ok")) view.showMessage("Login succesfully!"); else view.showMessage("Invalid username and/or password!"); closeConnection(); } } private void openConnection(){ try { myClient = new DatagramSocket(clientPort); } catch (Exception ex) { view.showMessage(ex.getStackTrace().toString()); } } private void closeConnection(){ try { myClient.close(); } catch (Exception ex) { view.showMessage(ex.getStackTrace().toString()); } } private void sendData(User user){ try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(user); oos.flush(); InetAddress IPAddress = InetAddress.getByName(serverHost); byte[] sendData = baos.toByteArray(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, serverPort); myClient.send(sendPacket);PTIT } catch (Exception ex) { view.showMessage(ex.getStackTrace().toString()); } } private String receiveData(){ String result = ""; try { byte[] receiveData = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); myClient.receive(receivePacket); ByteArrayInputStream bais =