Đồ án Xây dựng ứng dụng dựa trên mạng ngang hàng

Tài liệu Đồ án Xây dựng ứng dụng dựa trên mạng ngang hàng: MỤC LỤC LỜI CẢM ƠN Em xin gửi lời cảm ơn chân thành đến thầy giáo, giáo viên hướng dẫn TS. Phạm Hồng Thái và CN. Lương Việt Nguyên. Trong quá trình nghiên cứu đề tài em đã được các thầy giáo tạo điều kiện về tài liệu và kiến thức liên quan giúp em hoàn thành tốt khoá luận tốt nghiệp này. Em xin chân thành cảm ơn thầy, cô giáo trong Bộ môn Tin học trường Đại Học Dân Lập Hải Phòng, những người đã dạy và cung cấp cho em những kiến thức quý báu để em có thể dễ dàng tiếp cận những công nghệ, kỹ thuật mới trong tương lai. Xin cảm ơn bạn bè, những người đã cùng tôi sánh bước, đã đóng góp, động viên tôi những lúc gặp khó khăn, tạo điều kiện giúp tôi hoàn thành tốt khóa luận này. Hải Phòng, Tháng 8 năm 2007 Sinh viên Nguyễn Thị Hoa MỞ ĐẦU Tốc độ phát triển của công nghệ đã mang đến cho người dùng cuối những ứng dụng, tiện ích miễn phí và chất lượng hơn. Nhưng dù công nghệ thay đổi, biến chuyển thế nào, nhu cầu chia sẻ dữ liệu vẫn luôn cần thiết đối với tất cả mọi người. Con người sử ...

doc69 trang | Chia sẻ: hunglv | Lượt xem: 1311 | Lượt tải: 0download
Bạn đang xem trước 20 trang mẫu tài liệu Đồ án Xây dựng ứng dụng dựa trên mạng ngang hàng, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
MỤC LỤC LỜI CẢM ƠN Em xin gửi lời cảm ơn chân thành đến thầy giáo, giáo viên hướng dẫn TS. Phạm Hồng Thái và CN. Lương Việt Nguyên. Trong quá trình nghiên cứu đề tài em đã được các thầy giáo tạo điều kiện về tài liệu và kiến thức liên quan giúp em hoàn thành tốt khoá luận tốt nghiệp này. Em xin chân thành cảm ơn thầy, cô giáo trong Bộ môn Tin học trường Đại Học Dân Lập Hải Phòng, những người đã dạy và cung cấp cho em những kiến thức quý báu để em có thể dễ dàng tiếp cận những công nghệ, kỹ thuật mới trong tương lai. Xin cảm ơn bạn bè, những người đã cùng tôi sánh bước, đã đóng góp, động viên tôi những lúc gặp khó khăn, tạo điều kiện giúp tôi hoàn thành tốt khóa luận này. Hải Phòng, Tháng 8 năm 2007 Sinh viên Nguyễn Thị Hoa MỞ ĐẦU Tốc độ phát triển của công nghệ đã mang đến cho người dùng cuối những ứng dụng, tiện ích miễn phí và chất lượng hơn. Nhưng dù công nghệ thay đổi, biến chuyển thế nào, nhu cầu chia sẻ dữ liệu vẫn luôn cần thiết đối với tất cả mọi người. Con người sử dụng mạng Internet chính là để tìm kiếm thông tin, thông tin thì có trong rất nhiều định dạng. Trong thời gian gần đây, chia sẻ file ngang hàng đã nổi lên như một lĩnh vực ứng dụng chiếm tỉ lệ sử dụng băng thông lớn trong mạng Internet. Bắt đầu từ hiện tượng Napster vào cuối những năm 90, sự phổ biến của các chương trình chia sẻ file ngang hàng như Gnutella, Freenet, Kazzaa đã tạo nên một xu hướng phát triển mạnh mẽ việc chia sẻ nội dung trong cộng đồng người dùng Internet. Hệ thống mạng ngang hàng và các ứng dụng chia sẻ file ngang hàng cũng trở thành một đề tài thu hút được nhiều sự quan tâm, nghiên cứu của các nhà khoa học. Các hệ thống chia sẻ file ngang hàng đang ngày càng phổ dụng nhờ những lợi điểm rõ rệt so với hình thức chia sẻ file trên nền Web theo kiến trúc client - server. Tuy nhiên, các ứng dụng chia sẻ file ngang hàng phổ biến hiện nay trên Internet vẫn còn một hạn chế lớn. Chúng mới chỉ cho phép người dùng tìm kiếm file theo tên hay gọi chung là định danh chứ chưa có chức năng truy xuất theo nội dung. Mục đích của khóa luận tốt nghiệp này là khai thác những thành tựu mới nhất của công nghệ truy xuất thông tin để xây dựng một ứng dụng chia sẻ file ngang hàng có chức năng tìm kiếm theo nội dung. Hệ thống được xây dựng theo mô hình mạng ngang hàng lai ghép, một sự kết hợp giữa phương thức trao đổi trực tiếp không thông qua trung gian với giải pháp sử dụng máy chủ tìm kiếm. Chiến lược quản lý tập trung dựa trên máy chủ tìm kiếm giúp khắc phục những khó khăn trong việc tìm kiếm thông tin phân tán. Máy chủ tìm kiếm không chứa nội dung các file. Nó chỉ cho biết ứng với mỗi từ khóa cho trước có những file nào và chúng nằm ở đâu trong số các điểm nút tham gia vào hệ thống. Chương trình được phát triển bằng ngôn ngữ lập trình Java với những tính năng tìm kiếm theo nội dung được phát triển dựa trên thư viện mã nguồn mở Lucene. Luận văn này sẽ xây dựng ứng dụng dựa trên mạng ngang hàng. Luận văn được chia thành 5 chương Chương 1: Tổng quan về mạng chia sẻ file ngang hàng. Chương 2: Mô tả một số phương pháp, kỹ thuật tạo chỉ mục cho tài liệu và tìm kiếm dựa trên chỉ mục. Chương 3: Giải pháp xây dựng ứng dụng. Chương 4: Cài đặt chương trình. Chương 5: Kết quả thực hiện chương trình. Mặc dù đã cố gắng hết sức cùng với sự động viên giúp đỡ tận tình của thầy giáo hướng dẫn xong trình độ còn hạn chế, nội dung đề tài phức tạp, phạm vi của đề tài rộng nên khó tránh khỏi những sai sót trong quá trình làm đố án. Em rất mong được sự chỉ dẫn của thầy cô và sự góp ý của các bạn để chương trình của em được hoàn thiện hơn. Cuối cùng em xin chân thành cảm ơn sự động viên và giúp đỡ nhiệt tình của thầy hướng dẫn: TS. Phạm Hồng Thái và CN. Lương Việt Nguyên đã giúp đỡ em hoàn thành đề tài này. Hải Phòng, Tháng 8 năm 2007 Sinh viên: Nguyễn Thị Hoa Chương 1: TỔNG QUAN VỀ MẠNG CHIA SẺ FILE NGANG HÀNG 1.1. Giới thiệu về mạng ngang hàng (peer to peer – P2P) 1.1.1. Khái niệm cơ bản Mạng ngang hàng không phải là một vấn đề hoàn toàn mới. Các máy chủ dịch vụ thư điện tử (Mail servers) hoặc các máy chủ phân giải tên miền (Domain Name Servers) được kết nối với nhau tạo ra một mạng ngang hàng. Ví dụ như giữa các máy chủ thư điện tử có thể thực hiện tương tác trực tiếp với nhau. Chúng có thể gửi, nhận hoặc chuyển tiếp các email cho nhau. Tuy các dịch vụ thư điện tử hay DNS đã xuất hiện từ lâu trên Internet nhưng khái niệm mạng ngang hàng hay tính toán ngang hàng (P2P – Peer-to-Peer) thì mới được đưa ra gần đây. Mạng ngang hàng là những hệ phân tán với đặc thù là không tồn tại trong nó một cơ cấu điều khiển tập trung hoặc một tổ chức có phân cấp [16]. Trong một hệ thống thuần túy ngang hàng, chương trình chạy trên mỗi điểm nút có vai trò hoàn toàn tương đương và bình đẳng với nhau. Tính chất này đối lập hoàn toàn với kiến trúc client – server truyền thống nơi có một hoặc một số điểm nút chỉ đóng vai trò cung cấp dịch vụ (servers) và các điểm nút còn lại chỉ sử dụng dịch vụ (clients). Lợi điểm rõ rệt nhất của kiến trúc ngang hàng là khả năng tận dụng tốt hơn tài nguyên (xử lý, băng thông, lưu trữ) trong toàn mạng. Bên cạnh đó, kiến trúc này cũng giúp cho dịch vụ mạng tránh khỏi tình trạng ngừng trệ khi server gặp phải trục trặc. Tuy nhiên mô hình này cũng tồn tại nhược điểm là khó kiểm soát được trạng thái, hành vi của các điểm nút trên toàn mạng. Ngoài ra nó cũng đòi hỏi các máy khi tham gia vào một mạng ngang hàng phải có năng lực xử lý cũng như băng thông gần tương đương như nhau. Không giống như trong kiến trúc client – server, hiệu suất hoạt động chung của mạng ngang hàng có xu hướng tăng lên khi gia tăng số điểm nút tham gia. Hiệu suất này cũng phụ thuộc vào từng ứng dụng mạng cụ thể, vào giao thức ngang hàng và cấu hình mạng (topology). 1.1.2. Đặc điểm của các mạng ngang hàng Các mạng ngang hàng ngày nay thường mang một số đặc trưng phổ biến sau: ™ Các điểm nút trong mạng có thể nhận biết lẫn nhau. Nghĩa là có một cơ chế nào đó giúp cho một điểm nút khi tham gia vào mạng có thể xác định một máy khác cũng là thành viên của mạng. Từ đó chúng có thể định vị được nhau, gửi thông điệp tới nhau và nhận thông điệp từ nhau. ™ Các điểm nút tạo ra một mạng kết nối ảo và ở một mức trừu tượng cao hơn so với các cơ cấu tổ chức như: tường lửa (firewall), NAT (Network Address Translation), mạng con (subnet). Mỗi điểm nút có thể nằm trong các mạng con khác nhau, chịu những cơ chế tổ chức, kiểm soát và giới hạn hoàn toàn riêng biệt. Tuy nhiên khi đã tham gia vào mạng, chúng sẽ tổ chức được những mối liên kết logic với nhau thông qua việc sử dụng các dịch vụ hoặc chạy các ứng dụng ở tầng cao hơn so với những cơ chế vừa được nhắc tới. Tạo ra một mạng kết nối logic giữa những điểm nút bị biệt lập hóa trong các mạng riêng biệt chính là ý tưởng xuyên suốt nhất của kiến trúc ngang hàng. ™ Mỗi điểm nút tự nó có thể vừa đóng vai trò của client vừa đóng vai trò của server. Điều này thể hiện rõ vai trò bình đẳng và độc lập của từng điểm nút. Mọi điểm nút vừa có thể cung cấp dịch vụ cho các điểm nút khác vừa có thể sử dụng dịch vụ của một hay nhiều điểm nút còn lại. ™ Xuất hiện một số nhóm điểm nút liên kết với nhau để chia sẻ dữ liệu và cộng tác với nhau trong xử lý. Đây là sự tổ hợp lại các điểm nút có những mối liên hệ chặt chẽ và mang tính tương tác gần gũi hơn trong quá trình hoạt động của ứng dụng mạng. 1.1.3. Tiện ích mạng P2P mang lại. Giúp cho người dùng dễ dàng tìm được dữ liệu cần thiết. Tận dụng được tiện ích tổng hợp: Nơi lưu trữ, thông tin và chi phí tính toán được phân phối giữa các PEER, làm các máy tính tham gia vào mạng sẽ dễ dàng có được thông tin yêu cầu. Tăng độ tin cậy. Chứa đựng rất nhiều thông tin: Trong mạng P2P có rất nhiều các máy tính tham ra vào, bản thân mỗi máy tính đã chứa nhiều thông tin, trong khi đó các công cụ tìm kiếm chỉ có thể nắm bắt được khoảng 20% nội dung của các Website. 1.1.4. Những khó khăn trong thiết kế mạng ngang hàng ™ Cân đối băng thông: Trong phần lớn các ứng dụng chạy trên mạng ngang hàng, do mỗi điểm nút đều đóng cả hai vai trò: client và server nên tỉ lệ sử dụng băng thông đầu ra (outbound bandwidth) và băng thông đầu vào (inbound bandwidth) tại từng điểm nút là tương đối cân bằng. Tuy nhiên các nhà cung cấp dịch vụ mạng (ISPs) lại thường triển khai các mạng không đối xứng trong đó dành sự ưu tiên cho phần băng thông đầu vào. Ví dụ một số ISP của các mạng DSL hỗ trợ 1.5Mbps băng thông đầu vào nhưng chỉ có 128Kbps cho băng thông đầu ra. Cho dù băng thông tổng cộng của kết nối vật lý có được mở rộng thì hạ tầng kỹ thuật của các ISP vẫn sẽ chủ yếu hỗ trợ cơ chế bất đối xứng. Giải pháp triệt để cho vấn đề này có thể đến từ sự cộng tác giữa các ISP và khách hàng bằng việc triển khai những thiết bị mạng chuyên dụng. ™ Tổ chức không gian tên: Việc đặt tên cho các website được thực hiện thông qua hệ thống phân cấp của dịch vụ phân giải tên miền (DNS). Tuy nhiên trong các mạng ngang hàng không tồn tại một cơ chế tương tự. Không như các máy chủ dịch vụ web, các điểm nút trong mạng ngang hàng không tồn tại ở trạng thái tĩnh. Thời điểm và khoảng thời gian tham gia vào mạng của mỗi điểm nút cũng ko thể xác định được. Công việc tạo ra một tên (định danh) duy nhất cho các đối tượng, thành phần của mạng phải được thực hiện bởi chính người phát triển ứng dụng và do đó nó phụ thuộc vào đặc thù của từng ứng dụng. ™ Chứng thực và kiểm tra quyền truy cập của người dùng: Nếu tất cả các file đều được đặt trên server thì sẽ dễ dàng hơn trong việc chứng thực người dùng cũng như kiểm tra quyền hạn truy cập của họ đối với dữ liệu. Tuy nhiên do tính chất phân tán của mạng ngang hàng, công việc này phải được thực hiện bởi từng điểm nút. Người phát triển khi muốn xây dựng một ứng dụng ngang hàng hoàn chỉnh cần quan tâm nhiều đến vấn đề bảo mật, chống các hành động xâm nhập trái phép làm ảnh hưởng tới dữ liệu. ™ Kiểm soát hành vi của người dùng: Do không thể lưu trữ tập trung thông tin về hành động của các điểm nút nên rất khó kiểm soát được những hành động đó. Lấy ví dụ trong một mạng chia sẻ file ngang hàng, người dùng có thể thực hiện một trong những hành vi không thực sự phù hợp như sau: ƒ Không chia sẻ bất kỳ file nào trên máy mình. ƒ Chia sẻ các file bị lỗi. ƒ Chia sẻ các file chứa mã nguy hiểm, virus. ƒ Chia sẻ các file mà nội dung của nó chắc chắn không được bất kỳ ai quan tâm. ƒ Không cho phép các điểm nút khác tải về những file được chia sẻ trên máy mình. Nếu tất cả các điểm nút tham gia đều thực hiện những hành vi tiêu cực như trên thì hoạt động của mạng ngang hàng thực sự không hiệu quả và kém an toàn. Tùy thuộc vào từng ứng dụng cụ thể, người thiết kế và phát triển phải thiết lập những cơ chế kiểm soát hành vi của các điểm nút để bảo đảm rằng chúng thực sự có những đóng góp tích cực cho cộng đồng sử dụng mạng. 1.1.5. Phân loại các ứng dụng mạng ngang hàng Các ứng dụng mạng ngang hàng có thể được phân chia thành một số nhóm như sau: ™ Chia sẻ file: Gnutella, FastTrack, Napster. ™ Chia sẻ tài nguyên phân tán: SETI@Home, Avaki, Entropia và các dự án tính toán lưới. ™ Phân phối nội dung: OpenCola, Blue Falcon Networks, Konitiki. ™ Truyền thông P2P: AOL Instant Messenger, Yahoo! Messenger, ICQ, Jabber. ™ Các ứng dụng cộng tác: Hive, Groove, myJXTA. 1.2. Mô hình mạng P2P 1.2.1. Mô hình tập trung Mô tả: Mạng tập trung bao gồm Server trung tâm và xung quanh Server là các máy Clients. Có 2 mô hình mạng tập trung: Single Centralized: Trong mô hình này các máy Client sẽ kết nối trực tiếp với 1 Server duy nhất. Trong mô hình này tất cả các Client là bình đẳng như nhau, các Client giao tiếp với nhau thông qua Server trung tâm. Mô hình mạng tập trung. Cơ chế hoạt động của mô hình mạng Single Centralized: Mỗi khi một Client trong mạng yêu cầu một file nào đó thì yêu cầu sẽ được gửi đến Server, Server nhận yêu cầu và xử lý yêu cầu, nếu trong database của Server có thông tin về file đó, nó sẽ thông báo cho Client, sau đó bên có và bên xin để bắt đầu quá trình download. Ưu điểm của mô hình Single Centralized: Khả năng xử lý thông tin nhanh chóng, đáng tin cậy, thời gian tìm kiếm thông tin nhanh chóng và chính xác. Nhược điểm: Có thể khi có quá nhiều các yêu cầu của Client đồng loạt được gửi đến Server sẽ gây nên tình trạng quá tải của Server, khiến cho tốc độ hoạt động trung bình của hệ thống bị giảm sút. Hơn nữa khi Server trung tâm bị hỏng thì toàn bộ hệ thống sẽ ngừng hoạt động. Multiple Mini Centralized: Bao gồm nhiều Server kết nối với nhau, mỗi Server kết nối với nhiều Client. Một Client thì kết nối duy nhất với một Server, một Server sẽ kết nối với nhiều Client. Các Server có thể trao đổi thông điệp với nhau. Mô hình mạng Multiple Mini – Centralized. Cơ chế hoạt động của mô hình mạng Multiple Mini – Centralized: Khi một Client yêu cầu file, nó sẽ gửi yêu cầu đến Server mà nó kết nối trực tiếp. Nếu trong database của nó mà có thì sẽ có thông điệp sẽ được gửi lại cho Client yêu cầu và Client có file dữ liệu đó để thiết lập download. Trong trường hợp nó không có file đó, nó sẽ gửi thông điệp đến các Server hàng xóm để tiếp tục tìm kiếm. Ưu điểm của mô hình mạng Multiple Mini – Centralized: Có nhiều Server vì vậy khả năng xử lý thông tin sẽ rất lớn, bởi vì các yêu cầu của Client sẽ được phân tán gửi đến các Server khác nhau sẽ làm giảm tải của các Server. Hơn nữa việc có nhiều Server trong mạng sẽ làm tăng hệ số an toàn cho hệ thống vì khi một trong những Server bị hỏng vẫn có thể đảm bảo mạng hoạt động ổn định với những Client không kết nối với Server đó. 1.2.2. Mô hình phân tán. Mô tả: Trong mạng P2P phân tán hoàn toàn không có vai trò của các Server, bản thân mỗi Client lại đóng vai trò của các Server. Hai mô hình mạng phân tán: Phân tán hoàn toàn (Completely decentralized index): Mạng được tạo bởi chỉ các Client, khi một Client gửi yêu cầu thông tin thì yêu cầu đó sẽ được broadcast tới toàn bộ các Client trong mạng. Ưu điểm của mạng phân tán hoàn toàn: Đây thực sự là mô hình gốc của mạng P2P, yêu cầu được gửi đến nhiều PEER tham gia vì vậy khả năng tìm thấy thông tin yêu cầu là rất lớn. Nhược điểm: Do không xử lý tập trung nên thời gian chờ đợi của mỗi PEER khi gửi yêu cầu đi là rất lớn và khả năng mất mát thông tin cũng rất lớn. Mô hình mạng phân tán. Phân tán không hoàn toàn (Multiple semi decentralized index): Các Client có thể đóng vai trò của Server nếu cần thiết - trở thành super PEER, các Client khác sẽ gửi request đến super PEER này để tìm thông tin. Ưu điểm: Tận dụng được nguồn tài nguyên phần cứng rất lớn, tăng khả năng của toàn hệ thống lên. Khả năng các PEER có thể trở thành super PEER là không giới hạn. Nhược điểm: Việc phân chia “chức năng” giữa các PEER là rất phức tạp. Mô hình mạng phân tán không hoàn toàn. 1.3. Ưu, nhược điểm của P2P 1.3.1. Ưu điểm Máy tính được lắp đặt tại bàn làm việc của người dùng. Người dùng tự quản lý công việc và đề ra kế hoạch bảo mật riêng. Tất cả người dùng có thể chia sẻ tài nguyên của mình theo bất cứ cách thức nào tùy ý. Những tài nguyên này gồm có dữ liệu trong các thư mục dùng chung, máy in, card Fax, modem,… Mất mát dữ liệu do sơ ý không ảnh hưởng lớn đến hệ thống. Cáp đơn giản, dễ thấy, dễ sử dụng để nối từ máy tính này đến máy tính khác trong mạng. Trong môi trường P2P mỗi máy tính phải sử dụng tài nguyên của mình để hỗ trợ cho người dùng cục bộ, sử dụng tài nguyên bổ sung để hỗ trợ cho người dùng truy cập trong mạng từ xa. Chi phí thiết lập duy trì ứng dụng thấp, mỗi máy tham gia vào mạng sẽ đóng góp một phần tài nguyên và băng thông, dữ liệu của mạng nằm trên các máy tham gia. Mạng ngang hàng giải quyết được vấn đề cân bằng tải, các máy tính chia sẻ tài nguyên của mình đồng thời nhận tài nguyên từ máy tính khác công việc được chia nhỏ đến các máy. 1.3.2. Nhược điểm Thời gian trao đổi thông tin trong P2P lớn hơn rất nhiều so với trong Client/Server. Vấn đề về bảo mật, ngoài ra còn vấn đề trong việc lưu trữ những thông tin cần thiết lâu dài. Các kết nối trong mạng ngang hàng có độ trễ cao hơn so với các kết nối TCP hoặc UDP thông thường. Việc quản lý thông tin, tạo kết nối, tìm kiếm các máy khác trong mạng phức tạp. 1.4. Một số ứng dụng chia sẻ file ngang hàng Ý tưởng về một ứng dụng chia sẻ file ngang hàng lần đầu tiên được đưa ra bởi chàng sinh viên 18 tuổi Shawn Fanning. Fanning muốn tạo ra ứng dụng kết hợp chức năng của một máy tìm kiếm (search engine) với khả năng chia sẻ file và hội thoại qua mạng. Không đầy một năm, Napster đã trở thành một site phát triển nhanh nhất trong lịch sử và là một ứng dụng cực kỳ phổ biến trên Internet. Nó cho phép người dùng có thể tìm kiếm và tải về các file nhạc một cách nhanh chóng và tiện lợi. Tuy nhiên sự phát triển bùng nổ của Napster đã dẫn đến những cuộc tranh cãi về vấn đề bảo vệ bản quyền trong ngành âm nhạc. Những cuộc tranh cãi này đã dẫn đến việc kiện tụng. Cuối cùng, Napster bị buộc phải đóng cửa và ngừng cung cấp dịch vụ. Bước đi tiên phong của Napster đã dẫn tới sự ra đời của một loạt các chương trình chia sẻ file ngang hàng khác trên mạng như Gnutella hay Freenet. Tuy nhiên, các ứng dụng này ko sử dụng server tập trung như trong Napster. Do không có một điểm trung gian cố định trong mạng nên khó có thể kết tội đối với các ứng dụng này là tiếp tay cho nạn vi phạm bản quyền. Ở đây sự tồn tại của máy chủ tìm kiếm tập trung chính là điểm khác biệt giữa hai loại ứng dụng chia sẻ file ngang hàng [13]. 1.4.1. Hoạt động của Napster Máy chủ tìm kiếm trong hệ thống Napster có trách nhiệm lưu trữ danh sách các điểm nút hiện đang tham gia vào mạng và danh sách các file hiện chúng đang chia sẻ. Trong thông điệp khởi tạo kết nối, điểm nút sẽ chuyển cho máy chủ tìm kiếm tên đăng nhập, mật khẩu, tốc độ kết nối Internet và địa chỉ cổng của tiến trình (process) chia sẻ file. Khi tìm kiếm một bài hát, điểm nút gửi đến cho máy chủ tìm kiếm một từ khóa hoặc cụm từ khóa và số lượng kết quả tối đa mà nó muốn nhận về. Máy chủ sẽ làm nhiệm vụ tìm kiếm các điểm nút hiện đang kết nối vào mạng và có khả năng đáp ứng yêu cầu. Thông tin về các điểm nút đó sẽ gửi về cho điểm nút đưa ra yêu cầu. Các thông tin gửi về sẽ bao gồm địa chỉ IP, số cổng dịch vụ và tốc độ kết nối Internet của từng điểm nút trong danh sách kết quả. Sau đó người dùng có thể chọn lựa một trong số các điểm nút, thực hiện kết nối trực tiếp và tiến hành tải file về. Kiến trúc của Napster Có hai lý do chủ yếu giải thích tại sao Fanning sử dụng mạng ngang hàng thay vì lưu trữ tất cả các file trên một server. Thứ nhất là do khả năng lưu trữ của server là hữu hạn, không thể đủ chỗ cho hàng tỉ file nhạc mà người dùng trên mạng quan tâm. Nguyên nhân thứ hai là băng thông hạn hẹp của server khó có thể đáp ứng được hàng ngàn yêu cầu download mỗi giây [13]. 1.4.2. Hoạt động của Gnutella Gnutella được thiết kế dựa trên ý tưởng che giấu định danh của các điểm nút tham gia hay còn gọi là cơ chế nặc danh (anonymous). Các điểm nút phải tự nhận diện lấy nhau bằng cách gửi đi thông điệp ping để hỏi và gửi trả thông điệp pong để xác nhận lại. Nội dung của thông điệp pong bao gồm địa chỉ IP và dach sách các file chia sẻ trên điểm nút được hỏi. Kiến trúc của Gnutella. Để tìm kiếm ra điểm nút hiện đang chia sẻ một file cho trước, thông điệp truy vấn phát đi trong mạng theo cách thức được mô tả bởi hình vẽ trên. Từ điểm nút đưa ra yêu cầu tìm kiếm, thông điệp được chuyển tới một số điểm nút lân cận được lựa chọn. Tới mỗi điểm nút, truy vấn tìm kiếm sẽ được đối sánh với danh sách các file đang chia sẻ tại đó đi kèm một tiêu chí xác định trước. Nếu tiêu chí này thỏa mãn thì danh sách kết quả sẽ được gửi về ngay cho điểm nút nguồn. Nếu tiêu chí không được thỏa mãn thì thông điệp truy vấn sẽ tiếp tục được chuyển đến cho các điểm nút lân cận tiếp theo. Quá trình này sẽ dừng lại cho đến khi tiêu chí tìm kiếm đã được thỏa mãn hoặc sau một số lần chuyển tiếp thông điệp nhất định. Khi đã thu thập được danh sách kết quả hoàn chỉnh, người dùng có thể kết nối trực tiếp đến và tải file về từ điểm nút được lựa chọn. 1.4.3. So sánh Gnutella và Napster 2 ứng dụng Gnutella và Napster đều có những ưu và nhược điểm riêng. Một trong số những ưu điểm của Napster là tính dễ sử dụng. Napster có một cơ chế tìm kiếm tập trung nhanh và hiệu quả. Ngoài ra trong Napster người ta có thể xác định rõ ràng đâu là bên cung cấp dịch vụ (người quản lý máy chủ tìm kiếm) và đâu là bên sử dụng dịch vụ (người đăng nhập vào mạng tại các điểm nút). Nhờ thế mà việc duy trì, phát triển dịch vụ đối với các công ty quản lý cũng như việc xác thực đối với người dùng trở nên dễ dàng hơn. Tuy nhiên, hệ thống Napster cũng bộc lộ một số nhược điểm. Do việc tìm kiếm diễn ra tập trung ở phía máy chủ nên khi mạng mở rộng, số lượng điểm nút truy cập tăng lên thì có thể dẫn tới máy chủ tìm kiếm bị quá tải. Một hạn chế nữa của Napster là trong mạng chỉ có thể chia sẻ các file MP3. Ngoài ra Napster không hỗ trợ bất kỳ định dạng file nhạc nào khác. Với cơ chế làm việc phi tập trung, Gnutella đã hoàn toàn tránh được các rắc rối về mặt pháp lý liên quan đến việc bảo vệ bản quyền âm nhạc. Ngoài MP3, Gnutella còn hỗ trợ được nhiều định dạng file nhạc khác. Tuy vậy, vẫn còn tồn tại nhiều nhược điểm đối với hệ thống Gnutella. Do cơ chế hoạt động hoàn toàn phân tán và bình đẳng nên không thực sự tồn tại một nhà cung cấp và quản lý dịch vụ tập trung. Bởi vậy sẽ không có các hoạt động hỗ trợ kỹ thuật dành cho ứng dụng khi có yêu cầu của người dùng hoặc khi xảy ra trục trặc, sự cố. Thời gian tìm kiếm file trong Gnutella cũng lâu hơn do phải chuyển thông điệp tìm kiếm tới nhiều điểm nút để dò hỏi. Băng thông của mạng cũng bị tiêu tốn nhiều cho việc quảng bá thông điệp tìm kiếm này. Một hạn chế nữa của Gnutella là khi một điểm nút đăng nhập vào mạng nó phải liên tục xử lý, trả lời các yêu cầu truy vấn cũng như tiêu tốn băng thông cho việc upload file tới các điểm nút khác. Cuối cùng, do các điểm nút khi tham gia vào mạng đều là nặc danh nên rất khó thẩm tra, xác thực được tính hợp lệ và an toàn của các thông điệp truy vấn cũng như nội dung các file dữ liệu tải về. 1.5. Một số nghiên cứu lý thuyết Tài liệu [14] trình bày kiến thức tổng quan về công nghệ mạng ngang hàng. Những kết quả nghiên cứu mới nhất về lĩnh vực chia sẻ file ngang hàng nói riêng được giới thiệu chi tiết trong [17]. Bài báo [7] tập trung khảo sát những chiến lược sử dụng nhiều máy chủ tìm kiếm trong các hệ thống chia sẻ file ngang hàng theo mô hình lai ghép. Ở đây các tác giả đã đưa ra những đánh giá lý thuyết về hiệu quả của các chiến lược và trình bày một số kết quả thực nghiệm. Một số vấn đề tìm kiếm theo nội dung trong mạng thuần túy ngang hàng được trình bày trong [9]. Bài báo [18] đưa ra một mô hình định tuyến thông điệp tìm kiếm trong mạng dựa trên nội dung của truy vấn. Trong mô hình này, mỗi điểm nút sẽ lưu trữ lại các kết quả tìm kiếm gần nhất được trả về từ một số điểm nút lân cận với nó. Thông điệp tìm kiếm sẽ được gửi đến một trong các điểm nút lân cận dựa trên việc so sánh nội dung của nó với danh sách kết quả gần nhất. Bài báo [10] cũng đề xuất và đánh giá bằng thực nghiệm mô hình toán học được áp dụng để tìm kiếm nội dung tài liệu trong mạng ngang hàng lai ghép. Tuy nhiên đây là một mô hình dựa trên việc lựa chọn tài liệu cũng như lựa chọn điểm nút lân cận theo xác suất thay vì tìm kiếm trực tiếp dựa trên chỉ mục của tài liệu. Chương 2: MÔ TẢ MỘT SỐ PHƯƠNG PHÁP, KỸ THUẬT TẠO CHỈ MỤC CHO TÀI LIỆU VÀ TÌM KIẾM DỰA TRÊN CHỈ MỤC 2.1. Tổ chức chỉ mục tìm kiếm Trong mục này sẽ tập trung vào việc trình bày những cơ chế hỗ trợ tìm kiếm một cách hiệu quả trên file văn bản với giả thiết xâu truy vấn bao gồm một tập hợp các từ hoặc cụm từ. Nhiệm vụ của thao tác tìm kiếm là trả về danh sách các file mà nội dung của chúng có chứa các từ, cụm từ trong xâu truy vấn. Tần suất xuất hiện các từ và thậm chí cả vị trí chính xác của các từ trong file văn bản cũng được xem là một tiêu chí trong việc xếp hạng kết quả tìm kiếm. Ta có thể dễ dàng hình dung ra một phương pháp tìm kiếm đơn giản nhất dựa trên việc quét tuần tự toàn bộ nội dung file văn bản. Qua mỗi lượt quét như vậy, ta có thể thu được những thông tin về số lần xuất hiện cũng như vị trí của các từ khóa tìm kiếm trong file văn bản. Tuy nhiên phương pháp này chỉ phù hợp với những file văn bản có kích thước nhỏ. Bên cạnh đó, mỗi lần có một truy vấn tìm kiếm gửi tới, ta lại phải thực hiện quét lại toàn bộ file từ đầu đến cuối. Đây là một thao tác lặp lại và tốn kém thời gian. Để tăng tốc độ tìm kiếm, người ta đề xuất ra phương pháp thực hiện quét một lần trên các file văn bản và lưu lại danh sách các thành tố (từ, cụm từ) có trong file đó cũng như các thông tin đi kèm với mỗi thành tố (vị trí, tần suất, độ quan trọng, …). Các thông tin này sẽ được tổ chức theo một cấu trúc dữ liệu riêng và được gọi là chỉ mục. Lúc này các thao tác tìm kiếm sẽ được tiến hành dựa trên chỉ mục thay vì được thực hiện trực tiếp trên file văn bản. 2.2. Tạo chỉ mục Tạo chỉ mục là một bước quan trọng, quyết định đến việc tăng tốc độ, hiệu quả và chất lượng tìm kiếm. Hiện nay có ba phương pháp chủ yếu để tạo chỉ mục dựa trên việc sử dụng các cấu trúc: file đảo ngược (Inverted file), mảng hậu tố (Suffix array) và file chữ ký (Signature file) [15]. Chúng tôi sẽ tập trung mô tả cách tạo chỉ mục dựa trên cấu trúc file đảo ngược – phương pháp tạo chỉ mục phổ biến nhất hiện nay. File đảo ngược là một cấu trúc dùng để tạo chỉ mục bằng phương pháp hướng từ (word-oriented) nhằm hỗ trợ tìm kiếm nhanh trên dữ liệu văn bản. Một cấu trúc file đảo ngược bao gồm hai thành phần cơ bản: bảng từ vựng (vocabulary) và bảng vị trí (occurrences). Bảng từ vựng là một tập hợp các từ phân biệt xuất hiện trong file văn bản. Tương ứng với mỗi từ trong bảng từ vựng là một danh sách mô tả thông tin về tất cả các vị trí trong văn bản mà từ đó xuất hiện. Tập hợp các danh sách này của toàn bộ các từ có trong bảng từ vựng tạo nên bảng vị trí. Ví dụ ở hình dưới đây cho ta hình dung một so sánh giữa đoạn văn bản đầu vào và kết quả tạo ra sau bước tạo chỉ mục dựa trên cấu trúc file đảo ngược. Ở đây có sự chuyển đổi từ danh sách các từ được sắp theo thứ tự trong văn bản gốc sang tập hợp các từ được sắp theo thứ tự từ điển. Lúc này đối tượng quan tâm của chúng ta không phải là toàn bộ nội dung của file văn bản gốc nữa mà là tập hợp các từ xuất hiện trong đó. Đây chính là sự giải thích ý nghĩa cho tên của cấu trúc “file đảo ngược”. Tạo chỉ mục theo cấu trúc file đảo ngược. Không gian cần thiết để lưu trữ bảng từ vựng là tương đối nhỏ. Theo định luật Heaps kích thước của bảng từ vựng tăng trưởng theo cỡ O(nβ) trong đó n là cỡ của văn bản và β là một hằng số nằm trong khoảng (0, 1) phụ thuộc vào từng văn bản [11]. Trong thực tế β biến thiên trong khoảng từ 0.4 đến 0.6. Bảng vị trí đòi hỏi một không gian lưu trữ lớn hơn với kích thước tăng trưởng theo cỡ O(n). Trong thực tế, bảng vị trí chiếm một không gian lưu trữ khoảng 30% tới 40% so với kích thước của tài liệu. Để giảm kích thước lưu trữ bảng vị trí, một kỹ thuật có tên gọi là đánh địa chỉ khối được sử dụng. File văn bản được phân chia thành các khối và thông tin trong bảng vị trí sẽ được trỏ đến các khối thay vì tới các từ. Bằng cách đánh địa chỉ khối, kích thước của thành phần con trỏ định vị sẽ nhỏ hơn do số lượng khối ít hơn số lượng từ. Ngoài ra tất cả các vị trí xuất hiện của cùng một từ trong một khối sẽ được rút gọn về chỉ một tham chiếu chung như được thể hiện ở hình dưới đây. Các khối có thể cùng kích thước hoặc được phân chia theo cấu trúc nội dung: đoạn tài liệu (paragraph), tệp tài liệu (file), … Tạo chỉ mục theo cấu trúc file đảo ngược sử dụng kỹ thuật đánh địa chỉ khối 2.3. Tìm kiếm dựa trên chỉ mục Dựa trên hệ thống chỉ mục được tổ chức theo cấu trúc file đảo ngược, thuật toán tìm kiếm thường tuân theo 3 bước sau: Tìm kiếm trên bảng từ vựng: Tách, phân tích xâu truy vấn thành các từ đơn hoặc các cụm từ. Thực hiện tìm kiếm các từ, cụm từ này trên bảng từ vựng. Thu thập danh sách thông tin vị trí của các từ, cụm từ tìm được sau bước một thông qua bảng vị trí. Xử lý các thông tin thu thập được từ bước hai và tạo ra danh sách kết quả tìm kiếm. Do bảng từ vựng được sắp xếp theo thứ tự từ điển nên thao tác tìm kiếm trên đó mất một chi phí cỡ O(logn) bằng cách sử dụng thuật toán tìm kiếm nhị phân. Bước thứ hai của thuật toán đơn giản chỉ là thu thập lại danh sách về thông tin vị trí của các từ đơn lẻ xuất hiện trong truy vấn thỏa mãn kết quả tìm kiếm trên bảng từ vựng. Bước thứ ba tương đối phức tạp trong trường hợp phải tìm kiếm cho các cụm từ. Khi đó người ta phải tổ chức tìm kiếm đồng thời trên các danh sách ứng với các từ đơn có mặt trong cụm. Bằng phương pháp trộn nhiều danh sách được sắp theo thứ tự vị trí người ta có thể tìm được những chuỗi tuần tự các từ theo đúng thứ tự trong cụm. Trong trường hợp chỉ mục có sử dụng kỹ thuật đánh địa chỉ khối, ta sẽ phải tiếp tục duyệt tuần tự bên trong nội dung của nhiều khối thu được từ các danh sách thông tin về vị trí để tìm ra những cụm thỏa mãn. Các xấp xỉ được đánh giá bằng lý thuyết cũng như các kết quả thực nghiệm cho thấy việc sử dụng chỉ mục theo cấu trúc file đảo ngược đem lại một thời gian tìm kiếm cũng như kích thước cần lưu trữ tăng theo kích thước file văn bản với một tốc độ chậm hơn tốc độ tăng của hàm tuyến tính. Điều này là không thể đạt được khi thực hiện tạo chỉ mục theo các cấu trúc khác. 2.4. Xếp hạng kết quả tìm kiếm Khi tiến hành truy vấn tìm kiếm theo nội dung một tập hợp văn bản cho trước (gọi là thư viện) thì người dùng không chỉ muốn nhận về danh sách kết quả tìm kiếm mà họ còn muốn danh sách này phải được sắp xếp theo một tiêu chí nào đó. Tiêu chí này chính là độ liên quan (relevance) giữa các kết quả với truy vấn tìm kiếm do người dùng đưa ra. Việc này quy về bài toán xác định độ liên quan giữa một truy vấn q với các tài liệu trong một thư viện C cho trước. Bài toán này đã được nghiên cứu khá chi tiết và toàn diện trong lĩnh vực truy xuất thông tin. Trong mục này, chúng tôi sẽ trình bày tóm lược nội dung của một thuật toán xác định độ liên quan nổi tiếng và hiện đang được sử dụng rộng rãi. Đó là thuật toán TF-IDF [12]. Xét bài toán trong trường hợp đơn giản: ta xem mỗi truy vấn q gồm một tập hợp các từ khóa ki . Với một văn bản D bất kỳ thuộc thư viện C thì ta có: R(q) = Σ R(ki) (2.3.1) Trong đó R(q) là độ liên quan của q với D còn R(ki) là độ liên quan của từ khóa ki với D. Nếu một từ khóa ki xuất hiện nhiều lần hơn từ khóa kj trong văn bản D thì khi chỉ xét trong phạm vi văn bản D ta có thể nói rằng từ khóa ki mang ý nghĩa quan trọng hơn từ khóa kj. Hay trong phần lớn các trường hợp ta có thể rút ra rằng từ khóa ki có ảnh hưởng tới nội dung của văn bản D lớn hơn hay R(ki) > R(kj). Vậy tần suất xuất hiện của một từ khóa trong văn bản tỉ lệ thuận với độ liên quan của nó với văn bản đó. Đại lượng tần suất xuất hiện này của từ khóa ki được gọi là tf(i) (tf viết tắt của term frequency). Mặt khác nếu ta xét trên phạm vi toàn bộ thư viện C chứ không chỉ trong văn bản D nữa thì có một vấn đề khác nảy sinh. Nếu một từ khóa k xuất hiện trong văn bản D và cũng xuất hiện trong nhiều văn bản khác thuộc C thì ta có thể thấy từ khóa k không còn mang tính đặc trưng cho văn bản D nữa. Lúc này không chỉ có văn bản D mà từ khóa k còn liên quan tới nội dung của nhiều văn bản khác nữa. Khái quát điều này ta có thể suy ra nếu từ khóa k xuất hiện trong càng nhiều văn bản thuộc C thì độ liên quan của nó đến D phải càng nhỏ hay R(k) tỉ lệ nghịch với số văn bản trong C có chứa từ khóa k. Để định lượng cho tính chất này, đối với từ khóa ki ta đưa vào tham số idf(i) được tính như sau: idf(i) = log(N/ni) (2.3.2) Trong đó N là số văn bản có trong C và ni là số văn bản trong C có chứa ki. idf(i) sẽ tăng khi ni giảm nghĩa là có ít văn bản trong C chứa ki (idf viết tắt của inverse document frequency). Từ hai nhận xét quan trọng trên ta có thể rút ra rằng: R(ki) = tf(i) * idf(i) (2.3.3) Từ hai công thức (2.3.1) và (2.3.3) ta có thể suy ra công thức ước lượng độ liên quan của truy vấn q với một văn bản D thuộc C như sau: R(q) = Σ (tf(i) * idf(i)) (2.3.4) Như vậy ta có thể sắp xếp các văn bản thuộc C theo thứ tự giảm dần của đại lượng R(q). Nếu đặt một ngưỡng dưới đối với giá trị này thì ta sẽ lọc ra được một danh sách các tài liệu kết quả có độ liên quan với q giảm dần. Đó chính là nội dung của thuật toán TF-IDF . Chương 3: GIẢI PHÁP XÂY DỰNG ỨNG DỤNG 3.1. Khái quát ý tưởng Trong mục này chúng tôi sẽ đưa ra giải pháp xây dựng một chương trình ứng dụng chia sẻ file trong mạng ngang hàng theo kiến trúc lai ghép cung cấp khả năng tìm kiếm theo nội dung đối với các tài liệu thuần văn bản (viết tắt là tài liệu). Ứng dụng cần đảm bảo thực hiện được 3 chức năng lớn sau: Cho phép người dùng tại các điểm nút khi tham gia vào mạng có thể tiến hành chia sẻ và dừng chia sẻ các tài liệu nằm trên máy của mình. Cho phép người dùng có thể đưa ra những truy vấn để tìm kiếm theo nội dung các tài liệu hiện đang được chia sẻ trên phạm vi toàn mạng. Cho phép người dùng tải về các tài liệu được chia sẻ nằm trên một điểm nút khác. Luồng thông điệp giữa các thành phần trong mạng. Để thực hiện được ba chức năng lớn nêu trên chương trình sẽ được tách thành hai thành phần triển khai ở hai phía: phía các điểm nút và phía máy chủ tìm kiếm. Nhiệm vụ của thành phần triển khai trên máy chủ tìm kiếm: Tổ chức xây dựng và cập nhật chỉ mục tìm kiếm. Tiếp nhận truy vấn từ các điểm nút, tìm kiếm dựa trên chỉ mục và trả về danh sách kết quả. Nhiệm vụ của thành phần triển khai tại các điểm nút: Gửi tới máy chủ tìm kiếm các yêu cầu đăng nhập vào và đăng xuất ra khỏi hệ thống chia sẻ file ngang hàng. Tiếp nhận yêu cầu của người dùng và gửi đến máy chủ tìm kiếm các thông báo chia sẻ hoặc dừng chia sẻ các file được lưu trữ tại các điểm nút. Tiếp nhận truy vấn do người dùng nhập, gửi đến máy chủ tìm kiếm và tổ chức hiển thị kết quả trả về. Tiếp nhận và gửi yêu cầu tải tài liệu kết quả của người dùng tới điểm nút đích. Đóng vai trò là phía client trong hoạt động tải tài liệu. Tiếp nhận yêu cầu tải tài liệu được chia sẻ trên máy cục bộ và đóng vai trò một server cung cấp tài liệu cho các điểm nút khác. Vấn đề cơ bản và khó khăn nhất của một hệ thống tìm kiếm theo nội dung là tổ chức tạo chỉ mục cho các tài liệu đồng thời duy trì việc cập nhật cho hệ thống chỉ mục này. Trong khuôn khổ một ứng dụng chia sẻ file ngang hàng, có ba sự kiện xảy ra ở phía các điểm nút đòi hỏi phải tiến hành cập nhật hệ thống chỉ mục là: chia sẻ một tài liệu, dừng chia sẻ một tài liệu và đăng xuất khỏi hệ thống. Bởi vì đó là ba sự kiện dẫn đến việc thay đổi thông tin liên quan tới tính tồn tại của một tài liệu trong mạng chia sẻ file ngang hàng. Bài toán đặt ra là phải tiến hành cập nhật hệ thống chỉ mục tập trung nằm trên máy chủ tìm kiếm cho tất cả các tài liệu được lưu trữ phân tán, rải rác trên các điểm nút. Để giải quyết bài toán này, chúng tôi đề xuất giải pháp phân chia việc việc cập nhật chỉ mục làm hai bước. Bước đầu tiên được thực hiện tại các điểm nút và bắt đầu khi có một trong ba sự kiện nói trên xảy ra. Sau khi bước này kết thúc, các điểm nút thông báo tới máy chủ tìm kiếm để thực hiện bước thứ hai là cập nhật trực tiếp vào hệ thống chỉ mục tập trung. Chi tiết hơn, với mỗi sự kiện xảy ra ở phía điểm nút ta sẽ thực hiện như sau: Với sự kiện một tài liệu được chia sẻ: Bước đầu tiên, điểm nút sẽ tạo chỉ mục cho tài liệu được chia sẻ. Kết thúc bước này, bảng chỉ mục nhỏ của tài liệu này được chuyển lên cho máy chủ tìm kiếm. Tại máy chủ tìm kiếm, bước thứ hai là trộn bảng chỉ mục nhỏ này với bảng chỉ mục tập trung hiện thời để tạo ra một bảng chỉ mục tập trung mới. Với sự kiện dừng chia sẻ một tài liệu: Điểm nút sẽ thực hiện bước đầu tiên và xác định được định danh của tài liệu sẽ dừng chia sẻ. Định danh này được chuyển lên cho máy chủ tìm kiếm. Máy chủ tìm kiếm sẽ tiến hành xóa bỏ các thông tin chỉ mục liên quan đến tài liệu này trong bảng chỉ mục tập trung. Đăng xuất khỏi hệ thống: Hành động này của một điểm nút tương đương với việc dừng chia sẻ tất cả các tài liệu hiện đang được chia sẻ trên điểm nút đó. Sau bước đầu tiên, điểm nút cần chuyển tới cho máy chủ tìm kiếm định danh của chính nó và công việc của máy chủ tìm kiếm là tìm và xóa bỏ mọi thông tin chỉ mục liên quan đến các tài liệu được chia sẻ nằm trên điểm nút này. Ngoài ra chúng ta cần quan tâm tới một sự kiện nữa xảy ra khi người dùng trên một điểm nút tiến hành cập nhật lại file tài liệu mà nội dung của nó đã có sự thay đổi tính từ lần chia sẻ đầu tiên hoặc từ lần cập nhật cuối cùng. Ta có thể xem sự kiện này tương đương với việc xảy ra liên tiếp hai sự kiện: dừng chia sẻ tài liệu đã thay đổi nội dung và sau đó tiến hành lặp lại bước chia sẻ chính tài liệu đó. Một điểm cần chú ý là máy chủ tìm kiếm sẽ tiến hành cập nhật chỉ mục tập trung trong điều kiện tương tranh (concurrent). Nghĩa là máy chủ tìm kiếm có thể phải nhận đồng thời nhiều yêu cầu cập nhật chỉ mục tập trung từ các điểm nút. Có thể thấy rằng nếu không đồng bộ hóa các hoạt động cập nhật này thì sẽ dẫn đến tình trạng dữ liệu (chỉ mục) trở nên không hợp lệ do cùng lúc bị thay đổi bởi nhiều tiểu trình chạy song song. Hình vẽ dưới đây mô tả một cách trực quan các bước trong quá trình tạo chỉ mục bộ phận và cập nhật chỉ mục tập trung cũng như hành động truy vấn tìm kiếm và tải về các file dữ liệu. Hoạt động của cơ chế cập nhật chỉ mục và tìm kiếm dựa trên chỉ mục. 3.2. Cấu trúc chỉ mục Từ yêu cầu chức năng của các thành phần trong hệ thống cũng như dựa trên giải pháp được đề xuất trong việc cập nhật từng bước hệ thống chỉ mục tìm kiếm ta có thể xác định được những thông tin của mỗi tài liệu cần thiết phải lưu trong chỉ mục: Các trường thông tin liên quan đến định danh của tài liệu: session_id: Đây là thông tin định danh của một điểm nút do máy chủ tìm kiếm tạo ra và phân phối cho các điểm nút. Nó có giá trị tính từ lúc điểm nút đăng nhập thành công cho tới lúc đăng xuất. Tại một thời điểm không thể có hai điểm nút có cùng một session_id. session_id cũng là thông tin được gửi tới máy chủ tìm kiếm khi điểm nút thực hiện hành động đăng xuất. Máy chủ tìm kiếm sẽ tìm và xóa mọi thông tin chỉ mục liên quan tới các tài liệu có session_id trùng với giá trị được gửi tới này. document_id: Đây là thông tin định danh giúp xác định duy nhất một tài liệu hiện đang được chia sẻ trên mạng. Thông tin này do các điểm nút tạo ra và có giá trị từ lúc tài liệu bắt đầu được chia sẻ cho tới lúc dừng chia sẻ hoặc đăng xuất. Thông tin này cũng được lưu trữ lại giúp cho việc quản lý các tài liệu hiện đang được chia sẻ tại từng điểm nút. Khi dừng chia sẻ một tài liệu, điểm nút sẽ gửi document_id của tài liệu đó lên cho máy chủ tìm kiếm và mọi thông tin chỉ mục liên quan tới tài liệu có giá trị document_id này sẽ bị xóa bỏ. Vấn đề đặt ra là làm thế nào để một điểm nút có thể sinh ra giá trị document_id mới không trùng lặp với bất kỳ tài liệu nào hiện đang được chia sẻ trên mạng. Có thể giải quyết vấn đề này nếu document_id được tổ chức theo hệ thống phân cấp tương tự như hệ thống tên miền. Nếu đưa giá trị session_id giúp xác định duy nhất điểm nút vào thì ta có thể tạo ra những miền giá trị của document_id không xung đột nhau. Các trường thông tin liên quan đến việc định vị tài liệu: IP_address: Địa chỉ IP giúp một điểm nút có thể thiết lập kết nối tới một điểm nút khác hiện đang lưu trữ tài liệu mà nó cần. Do vậy địa chỉ IP luôn luôn có mặt trong thông tin về tài liệu được trả về trong kết quả truy vấn. path: Đường dẫn chỉ ra vị trí lưu trữ tài liệu trên điểm nút. Cùng với địa chỉ IP, thông tin đường dẫn này cung cấp cho một điểm nút đầy đủ thông tin để định vị tài liệu mà nó cần. Nếu document_id là định danh logic thì path + IP_address chính là định danh vật lý của mỗi tài liệu. Các trường thông tin liên quan đến việc tìm kiếm nội dung của tài liệu: name: tên tài liệu là một thông tin tìm kiếm quan trọng. Thông tin này thường là tiêu chí tìm kiếm duy nhất trong các hệ thống chỉ hỗ trợ tìm kiếm theo định danh. content: nội dung của tài liệu. Ta sẽ không lưu toàn bộ nội dung của tài liệu mà chỉ lấy nó như một đầu vào của thuật toán tạo chỉ mục theo cấu trúc file đảo ngược. 3.3. Đánh giá giải pháp Điểm đáng chú ý trong giải pháp của chúng tôi là công việc cập nhật cho bảng chỉ mục tìm kiếm tập trung được thực hiện thông qua sự cộng tác chặt chẽ giữa các điểm nút và máy chủ tìm kiếm. Nếu so sánh với một hệ thống máy tìm kiếm thông thường theo kiến trúc client - server (Google là một ví dụ điển hình) thì công việc này được dồn toàn bộ về phía máy chủ. Điều này đòi hỏi một năng lực xử lý cũng như băng thông rất lớn cho các máy chủ để có thể tải về tất cả các tài liệu trong mạng và sau đó tiến hành tạo chỉ mục. Bên cạnh đó, một nhược điểm nữa của máy tìm kiếm theo kiến trúc client/server so với giải pháp được đưa ra ở đây dành cho kiến trúc ngang hàng là khả năng theo dõi tính tồn tại của các tài liệu chia sẻ trên mạng. Ngay như với máy tìm kiếm mạnh nhất hiện nay trên Internet là Google thì trong danh sách kết quả trả về, người dùng luôn nhận được không ít các địa chỉ liên kết tới các tài nguyên hiện không còn tồn tại. Kiểm tra tính tồn tại của các tài liệu trên toàn mạng trong thời gian thực gần như là một nhiệm vụ không thể thực hiện được. Thay vào đó người ta đề xuất ra phương pháp tiến hành kiểm tra lặp lại theo một chu kỳ nào đó. Phương pháp này về bản chất chỉ là sự rời rạc hóa trục thời gian thành các điểm kiểm tra theo một cách có chọn lựa. Nếu các điểm kiểm tra càng gần nhau thì chi phí cho băng thông và xử lý càng lớn và nếu ngược lại thì khả năng theo dõi sự tồn tại của các tài liệu càng kém tính chính xác. Với giải pháp mà chúng tôi đưa ra ở đây trước hết là đã giúp phân chia công việc cập nhật chỉ mục cho cả hai phía – các điểm nút và máy chủ tìm kiếm – nhằm bảo đảm khai thác tốt hơn tài nguyên của mạng. Sau nữa, việc cập nhật chỉ mục theo sự kiện chính là biện pháp tốt nhất để theo dõi sự tồn tại của các tài liệu chia sẻ trên mạng vì khi đó hoạt động cập nhật chỉ được thực hiện khi thực sự cần thiết và cũng không phải tiến hành kiểm tra lặp lại. Tuy nhiên giải pháp này cũng bộc lộ bất cập khi cùng lúc trên toàn mạng xảy ra quá nhiều sự kiện chia sẻ tài liệu. Khi đó do phải tải về chỉ mục của một số lượng lớn tài liệu được chia sẻ nên lưu lượng mạng dồn về phía máy chủ tìm kiếm sẽ tăng vọt, có thể dẫn đến nguy cơ quá tải với máy chủ tìm kiếm. Để giúp cho hệ thống vẫn hoạt động trong những điều kiện như vậy ta có thể tiến hành nâng cấp khả năng xử lý và mở rộng băng thông đầu vào của máy chủ tìm kiếm. Ngoài ra ta có thể khắc phục bằng cách giảm tỉ lệ kích thước chỉ mục của một tài liệu so với kích thước của chính tài liệu đó. Có thể thực hiện điều này bằng cách áp dụng các cơ chế đánh địa chỉ khối, nén chỉ mục … Tuy nhiên việc giảm lưu lượng trên mạng bằng cách thay đổi nội dung, phương pháp tạo chỉ mục có thể dẫn đến tốc độ của quá trình tìm kiếm và chất lượng của kết quả tìm kiếm không được như mong muốn. Chương 4: CÀI ĐẶT CHƯƠNG TRÌNH Chương trình thực nghiệm được xây dựng dựa trên ngôn ngữ lập trình Java. Việc sử dụng Java làm ngôn ngữ cài đặt dựa trên một số lý do cơ bản sau: Java là một ngôn ngữ cho phép xây dựng các ứng dụng chạy trên nhiều nền tảng khác nhau (cross-platform). Điều này bảo đảm khả năng tương thích của chương trình với nhiều nền tảng phần cứng cũng như hệ điều hành trên các điểm nút. Java cung cấp những cơ chế lập trình mạng và lập trình đa tuyến (multi threading) tiện lợi và hiệu quả. Trong chương trình thực nghiệm có sử dụng các giao diện lập trình ứng dụng (API) được cung cấp bởi bộ thư viện mã nguồn mở Lucene. 4.1. Mô tả về thư viện mã nguồn mở Lucene 4.1.1. Khái quát về Lucene Lucene là bộ thư viện mã nguồn mở được xây dựng bằng ngôn ngữ lập trình Java nhằm mục đích hỗ trợ việc truy xuất thông tin [6]. Lucene cung cấp một giao diện lập trình ứng dụng cho phép người phát triển xây dựng các chương trình có khả năng tạo chỉ mục cho những file dữ liệu dạng ký tự và thực hiện tìm kiếm dựa trên chỉ mục. Lucene không quan tâm tới định dạng của các nguồn dữ liệu (ví dụ các định dạng html, doc, pdf, txt, …) miễn là chúng có khả năng chuyển đổi sang dữ liệu thuần văn bản (text). Các gói (package), lớp (class) và phương thức (method) trong Lucene được thiết kế nhằm xoay quanh việc thực hiện hai nhiệm vụ cơ bản sau: Nhiệm vụ đầu tiên của Lucene là cung cấp khả năng tạo chỉ mục cho các tài liệu để xây dựng nên hệ thống chỉ mục. Nhiệm vụ thứ hai của Lucene là cung cấp khả năng tiếp nhận các xâu truy vấn của người dùng, thực hiện tìm kiếm dựa trên hệ thống chỉ mục đã có và trả về kết quả. Cộng đồng những người phát triển dự án mã mở Lucene cũng đã đóng góp công sức tạo nên một số thư viện bổ sung nhằm hỗ trợ việc chuyển đổi từ các định dạng file văn bản phổ biến sang dữ liệu thuần text. Một số thư viện cũng được bổ sung vào để mở rộng khả năng tạo chỉ mục và tìm kiếm đối với những tài liệu được soạn bằng các ngôn ngữ phổ biến khác bên cạnh tiếng Anh. Nhiệm vụ, chức năng của thư viện Lucene [8]. 4.1.2. Tổ chức chỉ mục logic của Lucene Document: Lucene đưa ra khái niệm Document để khái quát hóa toàn bộ cấu trúc chỉ mục của một tài liệu [8]. Field: Một document bao gồm một dãy các field. Mỗi field bao gồm hai xâu: một xâu là tên của field và một xâu là nội dung của field. Lấy ví dụ nếu ta có một tệp tài liệu cần tạo chỉ mục thì document đại diện cho tài liệu đó có thể sẽ gồm 2 field: tên tài liệu và nội dung tài liệu Term: Nội dung của một field có thể là một term duy nhất hoặc có thể bao gồm một dãy các term. Một term cũng bao gồm một xâu biểu diễn giá trị nội dung của term đó và một xâu biểu diễn tên field mà term đó thuộc về. Term chính là đơn vị tìm kiếm nhỏ nhất trong một hệ thống chỉ mục. Hai term có giá trị nội dung giống hệt nhau nhưng thuộc về 2 field khác nhau thì được coi là phân biệt. Ví dụ: Một document có 2 field: NAME và CONTENT đại diện cho tài liệu abc.txt và nội dung tài liệu này có chứa một xâu “abc.txt” thì hai term: (NAME, “abc.txt”) và (CONTENT, “abc.txt”) là phân biệt nhau. 4.1.3. Xây dựng và khai thác chỉ mục trong Lucene Lucene cung cấp nhiều lớp, phương thức liên quan đến việc tạo, cập nhật chỉ mục và tìm kiếm dựa trên chỉ mục. Tuy nhiên trong khuôn khổ ứng dụng này, chúng tôi quan tâm và sử dụng chủ yếu những phương thức của các lớp sau: Lớp IndexWriter: Cho phép tạo ra hệ thống chỉ mục được lưu trữ trong một thư mục và bổ sung thêm các Document mới vào hệ thống chỉ mục đó (Sử dụng khi tạo chỉ mục ở phía các điểm nút trong sự kiện chia sẻ tài liệu). Lớp này cũng cho phép trộn nhiều hệ thống chỉ mục với nhau để tạo thành một hệ thống chỉ mục mới (Sử dụng khi cập nhật hệ thống chỉ mục ở phía máy chủ tìm kiếm khi có nhiều yêu cầu chia sẻ tài liệu đến từ các điểm nút) Lớp IndexReader: Các phương thức của lớp này cung cấp khả năng tìm và loại bỏ các thông tin chỉ mục với những tiêu chí xác định. Nó được sử dụng khi có các yêu cầu dừng chia sẻ tài liệu hoặc đăng xuất đến từ phía các điểm nút. Lớp QueryParser: Được sử dụng để phân tích xâu truy vấn tìm kiếm do người dùng nhập vào và tạo ra các truy vấn hợp lệ của Lucene. Lớp IndexSearcher: Nhận đầu vào là các truy vấn hợp lệ do QueryParser tạo ra, tìm kiếm trong chỉ mục và trả lại kết quả bao gồm một danh sách các Document có chứa những thông tin liên quan. 4.2. Tổ chức chương trình Chương trình thực nghiệm sẽ được tổ chức làm ba khối chính và giữa chúng có mối liên hệ mật thiết với nhau. 4.2.1. Khối chức năng cơ bản Khối này là trung tâm của chương trình thực hiện các chức năng chính và bảo đảm duy trì sự liên lạc giữa điểm nút với máy chủ tìm kiếm. Khối được phân làm hai bộ phận triển khai tại hai phía: phía máy chủ tìm kiếm và phía điểm nút. Truyền thông giữa hai bộ phận sẽ được thực hiện thông qua socket. Bộ phận triển khai phía máy chủ tìm kiếm bao gồm: Lớp Server: Lớp này có hai nhiệm vụ cơ bản: Thường xuyên lắng nghe yêu cầu kết nối đến từ các điểm nút tại cổng 3000. Khi một yêu cầu kết nối được chấp nhận, Server sẽ tạo ra một tiểu trình (thread) riêng thông qua việc tạo mới một đối tượng thuộc lóp ServerThread. Tiểu trình này sẽ giữ vai trò tương tác với bộ phận triển khai ở phía điểm nút tương ứng. Quản lý việc cập nhật hệ thống chỉ mục tập trung liên quan đến các thao tác bổ sung thêm tài liệu mới được chia sẻ, xoá bỏ tài liệu dừng chia sẻ và xoá bỏ tất cả các tài liệu thuộc về điểm nút thực hiện đăng xuất. Ba phương thức thực hiện nhiệm vụ này là: addDocument (), removeDocument () và removePeer (). Vì máy chủ tìm kiếm phải thực hiện cập nhật chỉ mục tập trung trong điều kiện tương tranh nên cả 3 phương thức này đều phải được đồng bộ hoá thông qua từ khoá synchronized. Lớp ServerThread: hai nhiệm vụ chính của lớp này là: Liên tục chạy một vòng lặp để lắng nghe các yêu cầu đến từ phía các điểm nút: chia sẻ, dừng chia sẻ, đăng xuất và gọi tới các phương thức tương ứng của lớp Server như đã nói ở trên đế tiến hành cập nhật chỉ mục tập trung. Kết quả của các hành động được trả lại cho phía điểm nút. Nhận xâu truy vấn từ phía điểm nút, thực hiện tìm kiếm trực tiếp trong chỉ mục tập trung và gửi trả danh sách kết quả về. Sở dĩ đối tượng thuộc lớp ServerThread có thể tìm kiếm trực tiếp trên chỉ mục tập trung vì hành động tìm kiếm chỉ yêu cầu các thao tác đọc chỉ mục. Thư viện Lucene hỗ trợ nhiều hành động tìm kiếm có thể xảy ra cùng lúc trong khi chỉ mục đang được cập nhật. Bộ phận triển khai tại các điểm nút bao gồm: Lớp ClientPeer: thực hiện một loạt các nhiệm vụ cơ bản sau: Thiết lập một socket kết nối đến máy chủ tìm kiếm và nhận giá trị sessionId được trả về. Tạo ra một tiểu trình mới lắng nghe các yêu cầu kết nối từ các điểm nút khác bằng cách sinh một thể hiện của lớp FileServer. Sinh mới một thể hiện của lớp Indexer với nhiệm vụ tạo ra chỉ mục bộ phận cho các tài liệu được chia sẻ. Quản lý danh sách các tài liệu hiện đang được chia sẻ thông qua một thể hiện của lớp SharedList. Quản lý danh sách kết quả tìm kiếm trả về từ phía máy chủ thông qua một thể hiện của lớp ResultList. Chạy một vòng lặp liên tục lắng nghe các thông tin trả về từ phía máy chủ tìm kiếm, cập nhật vào SharedList và ResultList. Cung cấp một loạt các phương thức được gọi đến khi có sự kiện xảy ra trên giao diện tương tác với người dùng. Ví dụ như phương thức share (),unshare (), search (), update (), logout () được gọi tương ứng với các hành động của người dùng. Lớp Indexer: nhiệm vụ của lớp này là sinh chỉ mục cho một file tài liệu đầu vào. Một chỉ mục của tài liệu sẽ bao gồm các trường được liệt kê ở bảng sau: Khuôn dạng các thông điệp trao đổi giữa hai bộ phận trong khối chức năng cơ bản được định nghĩa thông qua việc sử dụng các hằng số của Interface Protocol: Hằng số INIT: Khi Server chấp nhận yêu cầu kết nối của điểm nút và tạo ra lớp ServerThread tương ứng thì một thông điệp mở đầu bằng hằng số INIT sẽ được gửi về cho điểm nút báo rằng đã thiết lập kết nối thành công. Hằng số SHARE: Được điểm nút gửi lên cho máy chủ tìm kiếm báo hiệu rằng chuỗi dữ liệu tiếp theo sẽ là nội dung một file thuộc chỉ mục bộ phận của tài liệu vừa được chia sẻ. Máy chủ tìm kiếm sẽ tiếp tục nhận các file được gửi lên cho đến khi có một thông điệp mở đầu bằng hằng số SHARE_FINISH báo hiệu rằng việc truyền chỉ mục bộ phận của tài liệu chia sẻ đã hoàn tất. Ngay lập tức, máy chủ tìm kiếm sẽ thu thập các file thành phần lại để tái tạo chỉ mục bộ phận và tiến hành cập nhật vào chỉ mục tập trung. Hằng số SHARE_OK: Mở đầu cho thông điệp được máy chủ tìm kiếm gửi về điểm nút báo hiệu rằng việc cập nhật chỉ mục tập trung đối với tài liệu chia sẻ đã hoàn tất. Khi đó đối tượng ClientPeer tại điểm nút mới thực hiện thao tác cập nhật thông tin về file tài liệu vào danh sách chia sẻ thông qua đối tượng SharedList. Hằng số SHARE_FALL: Mở đầu cho thông điệp được máy chủ tìm kiếm gửi về điểm nút báo hiệu rằng việc cập nhật chỉ mục tập trung đối với tài liệu chia sẻ đã không thành công. Hằng số UNSHARE: Mở đầu cho thông điệp gửi từ máy chủ tìm kiếm về điểm nút báo hiệu rằng quá trình cập nhật lại chỉ mục tập trung đối với việc dừng chia sẻ file tài liệu đã hoàn tất. Danh sách tài liệu chia sẻ tại điểm nút sẽ được cập nhật lại thông qua đối tượng SharedList. Hằng số UNSHARE_FALL: Khi quá trình cập nhật chỉ mục tập trung trong trường hợp dừng chia sẻ một file tài liệu trên một điểm nút không được hoàn tất thì máy chủ tìm kiếm sẽ gửi về một thông điệp báo lỗi bắt đầu bằng hằng số này. Hằng số UPDATE: Mở đầu cho thông điệp được điểm nút gửi lên máy chủ tìm kiếm báo hiệu rằng chuỗi dữ liệu tiếp sau sẽ là nội dung một file thuộc chỉ mục bộ phận của tài liệu cần được cập nhật. Thông điệp mở đầu bằng hằng số UPDATE_FINISH sẽ báo hiệu rằng quá trình chuyển chỉ mục bộ phận cho tài liệu cần cập nhật đã kết thúc. Hằng số UPDATE_OK: Báo về cho điểm nút rằng quá trình cập nhật chỉ mục bộ phận của tài liệu bị thay đổi nội dung vào chỉ mục tập trung đã hoàn tất. Hằng số UPDATE_FALL: Báo về cho điểm nút rằng quá trình cập nhật chỉ mục bộ phận của tài liệu đã thay đổi nội dung vào chỉ mục tập trung không được hoàn thành. Hằng số SEARCH: Mở đầu cho thông điệp được gửi từ điểm nút lên máy chủ tìm kiếm, tiếp sau hằng số này sẽ là nội dung của xâu truy vấn. Hằng số INVALID_QUERY: Máy chủ tìm kiếm tiến hành phân tích xâu truy vấn để tạo ra truy vấn tìm kiếm theo chuẩn của Lucene. Nếu xâu truy vấn đầu vào không thể phân tích được thì thông điệp báo lỗi với nội dung là hằng số này sẽ được gửi lại cho điểm nút. Hằng số RESULT: Mở đầu thông điệp gửi về cho điểm nút mà nội dung của nó là danh sách các kết quả nhận được sau khi tìm kiếm. Mỗi kết quả trong danh sách này sẽ bao gồm 5 thông tin cơ bản về một tài liệu: Địa chỉ IP của điểm nút chứa tài liệu. Tên của file tài liệu (có cả phần mở rộng). Đường dẫn của file tài liệu trên điểm nút hiện đang chia sẻ nó. Kích cỡ của file tài liệu (tính bằng bytes). Thời điểm thay đổi nội dung file lần cuối cùng. Hằng số LOG_OUT: Khi điểm nút tiến hành đăng xuất khỏi mạng, nó sẽ gửi lên máy chủ tìm kiếm thông điệp có nội dung là hằng số này. 4.2.2. Khối giao diện người dùng. ™ Cửa sổ giao diện chính của chương trình (Lớp MainWindow). Cửa sổ giao diện chính của chương trình. Cửa sổ chính của chương trình hiển thị danh sách các tài liệu hiện đang được chia sẻ. Qua giao diện này người dùng có thể logout khỏi mạng chia sẻ file ngang hàng. Khi chọn một hoặc nhiều tài liệu trong danh sách, người dùng có thể dừng chia sẻ khi nhấn vào nút Unshare hoặc cập nhật lại bằng cách nhấn vào nút Update. Khi người dùng nhấn vào nút Share để chia sẻ một tài liệu mới chương trình sẽ cho phép họ chọn tài liệu thông qua hộp thoại dưới đây. Hộp thoại chọn tài liệu chia sẻ. ™ Cửa sổ tìm kiếm (Lớp SearchWindow) Từ cửa sổ chính của chương trình, người dùng khi muốn tìm kiếm một tài liệu trong mạng sẽ nhấn vào nút Search. Khi đó, cửa sổ tìm kiếm sẽ hiện thị với một ô cho phép người dùng nhập vào xâu truy vấn. Số lượng kết quả tìm kiếm sẽ được hiển thị ở ngay phía dưới. Danh sách kết quả được phân trang và trình bày trong một bảng. Người dùng có thể duyệt qua các trang kết quả bằng hai nút Prev và Next. Thông tin về mỗi tài liệu kết quả sẽ bao gồm: tên tài liệu, địa chỉ IP của điểm nút hiện đang chia sẻ tài liệu, kích cỡ file và thời gian cập nhật tài liệu lần cuối. Khi người dùng chọn vào một tài liệu trong danh sách kết quả và nhấn nút Download thì kết nối sẽ được thiết lập tới điểm nút có địa chỉ IP tương ứng và gửi yêu cầu download tài liệu chia sẻ về thư mục có đường dẫn do người dùng lựa chọn thông qua hộp thoại. Cửa sổ giao diện tìm kiếm. 4.2.3. Khối giao tiếp ngang hàng. Lớp FileServer Khi một điểm nút đăng nhập vào mạng cũng là lúc một tiểu trình mới được tạo ra với nhiệm vụ lắng nghe các kết nối đến từ các điểm nút khác. Một thể hiện của lớp FileServer sẽ được sinh ra và liên tục lắng nghe ở cổng 3001. Khi chấp nhận yêu cầu kết nối mới, một tiểu trình sẽ được tạo ra từ một thể hiện của lớp FileServerThread để trực tiếp liên lạc với phía bên kia. ™ Lớp FileServerThread Nhiệm vụ của lớp này là nhận yêu cầu từ một điểm nút khác liên quan đến việc tải tài liệu về. Sau khi tiến hành kiểm tra tính tồn tại và tính chia sẻ của tài liệu, quá trình tải tài liệu về điểm nút từ xa sẽ được bắt đầu. ™ Lớp FilePeerThread Khi người dùng nhấn vào nút Download trên cửa sổ tìm kiếm, một tiểu trình mới sẽ được tạo ra thông qua một thể hiện của lớp FilePeerThread. Nhiệm vụ của tiểu trình này là thiết lập kết nối và chuyển yêu cầu tải về tới điểm nút hiện đang chia sẻ tài liệu. Sau đó quá trình tải tài liệu sẽ được bắt đầu với một bên là FileServerThread và một bên là FilePeerThread. Khuôn dạng thông điệp được trao đổi giữa FilePeerThread và FileServerThread được định nghĩa thông qua việc sử dụng các hằng số trong Interface Protocol: Hằng số DOWNLOAD: FilePeerThread gửi thông điệp bắt đầu bằng hằng số DOWNLOAD cho FileServerThread, tiếp theo sau là đường dẫn của của file cần tải về trên điểm nút chia sẻ. Hằng số RESPONSE: Mở đầu cho thông điệp gửi từ FileServerThread về FilePeerThread, tiếp theo sau là nội dung của file yêu cầu. Hằng số UNVAILABLE: Khi đường dẫn của file yêu cầu không hợp lệ hoặc trỏ đến một file đã dừng chia sẻ thì FileServerThread sẽ gửi lại cho FilePeerThread một thông điệp báo lỗi bắt đầu bằng hằng số này. Sơ đồ lớp của chương trình. Sơ đồ lớp của chương trình Hình trên cho ta một cài nhìn khái quát về các lớp được cài đặt trong chương trình. Mũi tên mảnh liền nét cho ta biết các lớp sẽ tạo ra hoặc sử dụng đối tượng của nhau theo chiều hướng như thế nào. Mũi tên chấm gạch đậm nét biểu thị luồng thông điệp được truyền trong mạng giữa các điểm nút và máy chủ tìm kiếm. Mũi tên đứt đoạn đậm nét biểu thị các luồng thông tin yêu cầu cũng như dữ liệu trao đổi giữa các điểm nút với nhau. Chương 5: KẾT QUẢ THỰC HIỆN CHƯƠNG TRÌNH 5.1. Tìm kiếm theo nội dung Để thuận tiện cho việc trình bày, chúng tôi đưa ra một tình huống giả định như sau: ™ Điểm nút thứ nhất kết nối tới máy chủ tìm kiếm và đăng nhập vào mạng. Điểm nút này được gọi là Peer 0. Sau đó Peer 0 tiến hành chia sẻ 3 file nằm trong thư mục C:\\share được mô tả ở bảng trên. Cửa sổ giao diện chính trên Peer 0 (3 file được chia sẻ). ™ Sau đó, điểm nút thứ 2 đăng nhập vào mạng và được gọi là Peer 1. Peer 1 lần lượt gửi các xâu truy vấn tới cho máy chủ tìm kiếm và nhận về kết quả. Tìm kiếm theo từ và cụm từ: Tìm tài liệu có chứa từ khóa “mini”. Tìm tài liệu có chứa cụm từ “mini shoes”. ƒ Ngoài khả năng tìm theo từ, cụm từ chương trình còn hỗ trợ xây dựng các xâu truy vấn theo dạng biểu thức logic thông qua các toán tử && (AND), || (OR), + (Bắt buộc tồn tại), - (Bắt buộc không được tồn tại). Tìm tài liệu có chứa từ “curly” hoặc từ “brown”. Tìm tài liệu có chứa cả hai từ khóa “mini” và “shoes”. Tìm tài liệu phải chứa “mini” và không được chứa “shoes”. ƒ Bên cạnh tìm theo nội dung tài liệu, người dùng cũng có thể tìm kiếm theo tên của tài liệu như cách thức truyền thống. Tìm tài liệu có tên chứa từ khóa “information”. 5.2. Theo dõi trạng thái chia sẻ và nội dung tài liệu ™ Khi thay đổi nội dung của file size.txt bằng cách thay thế từ “mini” bằng từ “big” và Peer 0 cập nhật lại sự thay đổi này qua chức năng Update thì khi tìm kiếm với từ khóa “mini” chỉ nhận được kết quả là 2 file còn lại như hình vẽ dưới đây chỉ ra Tìm kiếm với từ khóa “mini” sau khi Peer 0 cập nhật lại size.txt ™ Sau bước trên nếu Peer 0 tiếp tục dừng chia sẻ file fashion.txt thì khi tìm kiếm với từ khóa “mini”, kết quả nhận được chỉ là 1 file: complex information.txt Tìm kiếm với từ khóa “mini” sau khi Peer 0 dừng chia sẻ fashion.txt ™ Khi người dùng trên Peer 1 chọn file complex information.txt và thực hiện download thì file này sẽ được tải từ Peer 0 về một thư mục trên Peer 1 được lựa chọn thông qua hộp thoại. File complex information.txt được tải từ Peer 0 về Peer 1. ™ Cuối cùng, sau khi Peer 0 đăng xuất khỏi mạng thì hành động tìm kiếm trên Peer1 với từ khóa “mini” sẽ cho không cho bất kỳ kết quả nào. Tìm kiếm với từ khóa “mini” sau khi Peer 0 đăng xuất. KẾT LUẬN VÀ CÁC HƯỚNG PHÁT TRIỂN Khóa luận tốt nghiệp này đã trình bày tổng quan về mạng chia sẻ file ngang hàng. Một số phương pháp, kỹ thuật tạo chỉ mục và tìm kiếm dựa trên chỉ mục cũng đã được giới thiệu nhằm mục tiêu áp dụng chúng để xây dựng một ứng dụng chia sẻ file ngang hàng có khả năng tìm kiếm theo nội dung. Giải pháp để xây dựng một ứng dụng như vậy đã được đề xuất dựa trên cơ chế cộng tác trong việc quản lý, cập nhật hệ thống chỉ mục giữa máy chủ tìm kiếm và các điểm nút. Cấu trúc và hoạt động của chương trình thử nghiệm minh họa cho ý tưởng tìm kiếm theo nội dung cũng được trình bày chi tiết đi kèm với mô tả kết quả thực hiện. Nội dung của đề tài này có thể được tiếp tục phát triển theo một số hướng như sau: ™ Đưa vào các kiến thức trong lĩnh vực xử lý ngôn ngữ tự nhiên và sử dụng các phương pháp xếp hạng kết quả tìm kiếm tiên tiến nhất nhằm làm tăng chất lượng tìm kiếm, bám sát hơn nữa yêu cầu của người dùng. ™ Tiến hành cài đặt bổ sung một số thư viện để hỗ trợ cho chương trình thử nghiệm ban đầu có thể phân tích thêm nhiều định dạng văn bản (doc, pdf, …), từ đó mở rộng khả năng tìm kiếm trên nhiều loại văn bản khác nhau. ™ Cải tiến theo hướng tổ chức trong mạng không chỉ có một mà nhiều máy chủ tìm kiếm được kết nối với nhau và trao đổi thông tin chỉ mục cũng như hỗ trợ nhau trong việc đáp ứng các truy vấn. Điều này giúp tăng khả năng mở rộng của mạng. Phụ lục: Mà NGUỒN MỘT SỐ LỚP QUAN TRỌNG Lớp server.Server public class Server { private final int PORT = 3000; private int nextSessionId = 0; private ServerSocket serverSock; static final File INDEX_DIR = new File("server\\index"); boolean indexed = false; public Server() { try { serverSock = new ServerSocket(PORT); System.out.println("Server started and listening on port: " + PORT); } catch(IOException ioe) { System.out.println("Server cannot be started"); ioe.printStackTrace(); } } public void listen() { try { ThreadGroup peerSet = new ThreadGroup("PeerSet"); while(true) { Socket s = serverSock.accept(); new ServerThread(this, s, nextSessionId, peerSet).start(); nextSessionId ++; } } catch(IOException ioe) { } finally { try { serverSock.close(); } catch(IOException ioe) {} } } public void stopServer() { File[] fileList = INDEX_DIR.listFiles(); for(int i = 0; i < fileList.length; i ++) fileList[i].delete(); System.out.println("Server stopped"); System.exit(0); } public synchronized void addDocument(File tempIndex) throws IOException { IndexWriter writer; if(!indexed) { File[] fileList = tempIndex.listFiles(); FileInputStream fin; FileOutputStream fout; for(int i = 0; i < fileList.length; i ++) if(fileList[i].isFile()) { fin = new FileInputStream(fileList[i]); fout = new FileOutputStream( new File( INDEX_DIR, fileList[i].getName())); byte[] temp = new byte[32]; int amount; while((amount = fin.read(temp)) != -1) fout.write(temp, 0, amount); fin.close(); fout.close(); } indexed = true; return; } writer = new IndexWriter(INDEX_DIR, new StandardAnalyzer(), false); Directory[] dirs = new Directory[1]; dirs[0] = FSDirectory.getDirectory(tempIndex, false); writer.addIndexes(dirs); writer.optimize(); dirs[0].close(); writer.close(); } public synchronized void removeDocument(String id) throws IOException { if(!indexed) return; IndexReader reader = IndexReader.open(INDEX_DIR); int a = reader.deleteDocuments(new Term("documentId",id.trim())); reader.close(); } public synchronized void removePeer(String id) throws IOException { if(!indexed) return; IndexReader reader = IndexReader.open(INDEX_DIR); reader.deleteDocuments(new Term("sessionId", id.trim())); reader.close(); } public static void main(String[] args) { new Server().listen(); } } Lớp server.ServerThread class ServerThread extends Thread { private Server server; private Socket s; private String sessionId; private File tempDir; private DataInputStream fromPeer; private DataOutputStream toPeer; public ServerThread(Server server, Socket s, int id, ThreadGroup tg) throws IOException { super(tg, String.valueOf(id)); this.server = server; this.s = s; sessionId = String.valueOf(id); fromPeer = new DataInputStream(s.getInputStream()); toPeer = new DataOutputStream(s.getOutputStream()); // create a temporary index for the connected peer tempDir = new File("server\\temp\\" + sessionId); tempDir.mkdirs(); System.out.println("Peer " + sessionId + " connected"); } public void run() { try { toPeer.writeInt(Protocol.INIT); // Login sucessfully toPeer.writeUTF(sessionId); // return sessionId to peer toPeer.writeUTF(s.getInetAddress().getHostAddress()); boolean stop = false; while(!stop) { int request = fromPeer.readInt(); // One fragment of index is sent to server if(request == Protocol.SHARE || request == Protocol.SHARE_FINISH) shareDocument(request); if(request == Protocol.UNSHARE) unshareDocument(request); if(request == Protocol.DELETE) unshareDocument(request); if(request == Protocol.UPDATE || request == Protocol.UPDATE_FINISH) shareDocument(request); if(request == Protocol.SEARCH) search(); if(request == Protocol.LOG_OUT) { server.removePeer(sessionId); stop = true; } } } catch(IOException ioe) { ioe.printStackTrace(); } finally { finish(); } } private void search() throws IOException { String queryString = fromPeer.readUTF(); if(!server.indexed) { toPeer.writeInt(Protocol.RESULT); toPeer.writeInt(0); return; } Directory dir = FSDirectory.getDirectory(Server.INDEX_DIR, false); IndexSearcher searcher = new IndexSearcher(dir); // default searching field is "content" QueryParser parser = new QueryParser("content", new StandardAnalyzer()); queryString = "(" + queryString + ")" + " AND " + "-sessionId:" + sessionId; Query query; try { // parsing the user string to standard query query = parser.parse(queryString); } catch(ParseException pe) { toPeer.writeInt(Protocol.INVALID_QUERY); return; } Hits resultSet = searcher.search(query); int numResult = resultSet.length(); toPeer.writeInt(Protocol.RESULT); toPeer.writeInt(numResult); for(int i = 0; i < numResult; i ++) { Document doc = resultSet.doc(i); toPeer.writeUTF(doc.get("ip")); toPeer.writeUTF(doc.get("fullName")); toPeer.writeUTF(doc.get("path")); toPeer.writeUTF(doc.get("size")); toPeer.writeUTF(doc.get("modified")); } toPeer.flush(); dir.close(); searcher.close(); } private void shareDocument(int request) throws IOException { // name of component file String fileName = fromPeer.readUTF(); File file = new File(tempDir, fileName); DataOutputStream toFile = new DataOutputStream(new FileOutputStream(file)); byte[] byteArr = new byte[1024]; long size = fromPeer.readLong(); long remain = size; int interval; interval = (remain > 1024) ? 1024 : (int)remain; int amount; // write the received file to temporary directory while((amount = fromPeer.read(byteArr, 0, interval)) != -1) { toFile.write(byteArr, 0, amount); toFile.flush(); remain -= amount; if(remain <= 0) break; interval = (remain > 1024) ? 1024 : (int)remain; } toFile.close(); // if all files are received if (request == Protocol.SHARE_FINISH || request == Protocol.UPDATE_FINISH) { try { server.addDocument(tempDir); // update the Index } catch(IOException ioe) { ioe.printStackTrace(); if(request == Protocol.SHARE_FINISH) toPeer.writeInt(Protocol.SHARE_FAIL); else toPeer.writeInt(Protocol.UPDATE_FAIL); toPeer.flush(); return; } // delete all files in temporary directory File[] fileList = tempDir.listFiles(); for(int i = 0; i < fileList.length; i ++) fileList[i].delete(); if(request == Protocol.SHARE_FINISH) toPeer.writeInt(Protocol.SHARE_OK); else toPeer.writeInt(Protocol.UPDATE_OK); toPeer.flush(); } } private void unshareDocument(int request) throws IOException { String documentId = fromPeer.readUTF(); try { server.removeDocument(documentId); } catch(IOException ioe) { ioe.printStackTrace(); if(request == Protocol.UNSHARE) toPeer.writeInt(Protocol.UNSHARE_FAIL); else toPeer.writeInt(Protocol.UPDATE_FAIL); return; } if(request == Protocol.UNSHARE) toPeer.writeInt(Protocol.UNSHARE_OK); } private void finish() { try { File[] fileList = tempDir.listFiles(); for(int i = 0; i < fileList.length; i ++) fileList[i].delete(); tempDir.delete(); fromPeer.close(); toPeer.close(); s.close(); System.out.println("Peer " + sessionId + " disconnected"); } catch(IOException ioe) { ioe.printStackTrace(); } finally { if(this.currentThread().activeCount() == 1) server.stopServer(); // Notify here (used test only) } } } Lớp peer.ClientPeer public class ClientPeer { private int shareDocCount = 0; private Socket sock; private DataInputStream fromServer; private DataOutputStream toServer; private String sessionId; private boolean stop = false; private Indexer indexer; private Vector sharingDoc = new Vector(); private Vector unsharingDoc = new Vector(); private Vector updatingDoc = new Vector(); private FileServer fileServer; private SearchWindow searchWindow; private MainWindow mainWindow; SharedList sharedList = new SharedList(); ResultList resultList = new ResultList(); public ClientPeer() { try { sock = new Socket("localhost", 3000); fromServer = new DataInputStream(sock.getInputStream()); toServer = new DataOutputStream(sock.getOutputStream()); } catch(IOException ioe) { System.out.println("Cannot connect to indexing server"); ioe.printStackTrace(); return; } try { if(fromServer.readInt() == Protocol.INIT) sessionId = fromServer.readUTF(); System.out.println("Session Id: " + sessionId); String ip = fromServer.readUTF(); System.out.println("IP: " + ip); indexer = new Indexer(sessionId, ip); } catch(IOException ioe) { ioe.printStackTrace(); } mainWindow = new MainWindow(this, "Peer " + sessionId); searchWindow = new SearchWindow(this, "Search " + sessionId); mainWindow.setVisible(true); searchWindow.setVisible(false); System.out.println("Connected to server"); try { fileServer = new FileServer(this); fileServer.start(); } catch(IOException ioe) { ioe.printStackTrace(); return; } } public void run() { try { while(!stop) { SharedDocument doc; int response = fromServer.readInt(); if(response == Protocol.SHARE_OK) { sharedList.addDocument((SharedDocument) sharingDoc.remove(0)); mainWindow.repaint(); } if(response == Protocol.SHARE_FAIL) { doc = (SharedDocument)sharingDoc.remove(0); JOptionPane.showMessageDialog(mainWindow, doc.file.getName() + "cannot be shared", "Error", JOptionPane.ERROR_MESSAGE); } if(response == Protocol.UNSHARE_OK) { sharedList.removeDocument((SharedDocument) unsharingDoc.remove(0)); mainWindow.repaint(); } if(response == Protocol.UNSHARE_FAIL) { doc = (SharedDocument)unsharingDoc.remove(0); JOptionPane.showMessageDialog(mainWindow, doc.file.getName() + "cannot be unshared", "Error", JOptionPane.ERROR_MESSAGE); } if(response == Protocol.UPDATE_OK) { doc = (SharedDocument)updatingDoc.remove(0); SharedDocument newDoc = new SharedDocument( doc.file, doc.documentId); sharedList.updateDocument(doc, newDoc); mainWindow.repaint(); } if(response == Protocol.UPDATE_FAIL) { doc = (SharedDocument)updatingDoc.remove(0); JOptionPane.showMessageDialog(mainWindow, doc.file.getName() + "cannot be updated", "Error", JOptionPane.ERROR_MESSAGE); } if(response == Protocol.RESULT) fillResultList(); if(response == Protocol.INVALID_QUERY) JOptionPane.showMessageDialog(searchWindow, "Your query is invalid", "Error", JOptionPane.ERROR_MESSAGE); } } catch (Exception ioe) { // ioe.printStackTrace(); finish(); } finally { System.exit(0); } } private void fillResultList() throws IOException { int numResults = fromServer.readInt(); resultList.clear(); if(numResults <= 0) searchWindow.lblResult.setText("No result"); else { searchWindow.lblResult.setText(numResults + " results"); for(int i = 0; i < numResults; i ++){ String ip = fromServer.readUTF(); String fullName = fromServer.readUTF(); String path = fromServer.readUTF(); String size = fromServer.readUTF(); String modified = fromServer.readUTF(); resultList.list.add( new Result(ip, fullName, path, size, modified)); } resultList.fireTableDataChanged(); searchWindow.repaint(); } } private void uploadFile(File file) throws IOException { byte[] byteArr = new byte[1024]; toServer.writeUTF(file.getName()); toServer.writeLong(file.length()); DataInputStream fromFile = new DataInputStream( new FileInputStream(file)); int num; while((num = fromFile.read(byteArr)) != -1) { toServer.write(byteArr, 0, num); toServer.flush(); } fromFile.close(); } public void switchSearchWindow() { if(!searchWindow.isVisible()) { resultList.clear(); searchWindow.setVisible(true); searchWindow.refresh(); searchWindow.repaint(); } } public synchronized void search(String query) throws IOException { toServer.writeInt(Protocol.SEARCH); toServer.writeUTF(query); } public boolean isShared(String path) { int numShares = sharedList.list.size(); for(int i = 0; i < numShares; i ++) if(((SharedDocument)sharedList.list.elementAt(i)). file.getPath().equals(path)) return true; return false; } public int isShared(File file) { int numShares = sharedList.list.size(); for(int i = 0; i < numShares; i ++) if(((SharedDocument)sharedList.list.elementAt(i)).file.equals(file)) return i; return -1; } public synchronized void share(File file) throws IOException { indexer.index(file, shareDocCount); File[] fileList = indexer.tempDir.listFiles(); byte[] byteArr = new byte[1024]; sharingDoc.add(new SharedDocument(file, shareDocCount)); shareDocCount ++; for(int i = 0; i < fileList.length; i ++) { if(i < fileList.length - 1) toServer.writeInt(Protocol.SHARE); else toServer.writeInt(Protocol.SHARE_FINISH); uploadFile(fileList[i]); fileList[i].delete(); } } public synchronized void unshare(SharedDocument doc) throws IOException { unsharingDoc.addElement(doc); String docPart = String.valueOf(doc.documentId); String documentId = sessionId + "." + docPart; toServer.writeInt(Protocol.UNSHARE); toServer.writeUTF(documentId); } public synchronized void update(SharedDocument doc) throws IOException { updatingDoc.addElement(doc); indexer.index(doc.file, doc.documentId); String docPart = String.valueOf(doc.documentId); String documentId = sessionId + "." + docPart; toServer.writeInt(Protocol.DELETE); toServer.writeUTF(documentId); File[] fileList = indexer.tempDir.listFiles(); byte[] byteArr = new byte[1024]; for(int i = 0; i < fileList.length; i ++) { if(i < fileList.length - 1) toServer.writeInt(Protocol.UPDATE); else toServer.writeInt(Protocol.UPDATE_FINISH); uploadFile(fileList[i]); fileList[i].delete(); } } public synchronized void logout() { try { toServer.writeInt(Protocol.LOG_OUT); toServer.flush(); } catch(IOException ioe) {} } private void finish() { try { fileServer.close(); fromServer.close(); toServer.close(); sock.close(); System.out.println("Disconnected"); } catch(IOException ioe) { ioe.printStackTrace(); } } public static void main(String[] args) { new ClientPeer().run(); } } Lớp peer.Indexer public class Indexer { private String sessionId; private String ip; static File tempDir = new File("peer\\temp"); FileReader reader; public Indexer(String sesId, String ip) throws IOException { this.sessionId = sesId; this.ip = ip; } private Document createDocument(File file, String documentId) throws IOException { Document doc = new Document(); // add session id field (identification of peer) doc.add(new Field("sessionId", this.sessionId.trim(), Field.Store.YES, Field.Index.UN_TOKENIZED)); // add id address field (location) doc.add(new Field("ip", this.ip, Field.Store.YES, Field.Index.NO)); // add document id (identification of document) doc.add(new Field("documentId", documentId.trim(), Field.Store.YES, Field.Index.UN_TOKENIZED)); String fullName = file.getName(); // add full name (search by full name) doc.add(new Field("fullName", fullName, Field.Store.YES, Field.Index.UN_TOKENIZED)); // name without extension (search in name) int dotPosition = fullName.lastIndexOf('.'); String name; if(dotPosition == -1) name = fullName; else name = fullName.substring(0, dotPosition); doc.add(new Field("name", name, Field.Store.YES, Field.Index.TOKENIZED)); // add absolute path (location) doc.add(new Field("path", file.getPath(), Field.Store.YES, Field.Index.NO)); // add file size (information) doc.add(new Field("size", String.valueOf(file.length()), Field.Store.YES, Field.Index.NO)); // add modified date (information) doc.add(new Field("modified", String.valueOf(file.lastModified()), Field.Store.YES, Field.Index.NO)); // add indexed content reader = new FileReader(file); doc.add(new Field("content", reader)); return doc; } public void index(File file, int docId) { try { IndexWriter writer = new IndexWriter(tempDir, new StandardAnalyzer(), true); String documentId = this.sessionId + "." + String.valueOf(docId); writer.addDocument(createDocument(file, documentId)); reader.close(); writer.optimize(); writer.close(); } catch(IOException ioe) { System.out.println("Cannot add document to INDEX"); ioe.printStackTrace(); } } } TÀI LIỆU THAM KHẢO Napster Home Page. Gnutella Development Home Page. Freenet Home Page. ICQ Home Page. SETI@ Home Page. Lucene Home Page. Beverly Yang and Hector Garcia-Molina, “Comparing Hybrid Peer-to- Peer Systems”. Stanford University, USA. Erik Hatcher and Otis Gospodnetic, “Lucene in Action”, Manning Publications Co, 2005. F.Cuenca-Acuna and T.Nguyen, “Text-based content search and retrieval in ad hoc p2p communities”, Technical Report DCS-TR-483, Rutgers University, 2002. Jie Lu, Jamie Callan, “Content-based retrieval in Hybrid Peer-to-Peer networks”, School of Computer Science – Carnegie Mellon University – Pittsburgh, 2003. J. Heaps. Information Retrieval – Computational and Theoretical Aspects. Academic Press, 1978. Juan Ramos, “Using TF-IDF to Determine Word Relevance in Document Queries ”, Department of Computer Science – Rutgers University, 2003. Kian Pokorny, “Peer to Peer Networking and Filesharing”, CSI 490 Senior Semina, 2004. Ramesh Subramanian and Brian D. Goodman, “Peer to Peer Computing: The Evolution of a Disruptive Technology”, Idea Group, 2005. R.A. Baeza-Yates and B.A. Ribeiro-Neto, “Modern Information Retrieval.” ACM Press Series / Addision Wesley, New York, 1999. Songmei Han, Bijit Hore, Ilya Issenin, Sean McCarthy and Shannon Tauro, “HollyShare: Peer-to-Peer File Sharing Application”, Final Report, 2001. Stephanos Androutsellis-Theotokis and Diomidis Spinellis, A Survey of Peer-to-Peer Content Distribution Technologies, ACM Computing Surveys, December 2004. V.Kalogeraki, D.Gunopulos and D. Zeinalipour-Yazti, “A local search mechanism for peer-to-peer network”, In Proc. Of the 11th International Conference on Information Knowledge Managament, 2002.

Các file đính kèm theo tài liệu này:

  • docLuan van TN_hoa2.doc
  • pptBao cao pp_hoa.ppt
Tài liệu liên quan