Thu dọn “rác” (Garbage collection) cải tạo hoặc làm trống bộ nhớ đã định vị cho các đối tượng mà các đối tượng này không sử dụng trong thời gian dài. Trong ngôn ngữ lập trình hướng đối tượng khác như C++, lập trình viên phải làm cho bộ nhớ trống mà đã không được yêu cầu trong thời gian dài. Tình trạng không hoạt động để bộ nhớ trống có thể là kết quả trong một số vấn đề. Java tự động tiến trình thu dọn rác để cung cấp giải pháp duy nhất cho vấn đề này. Một đối tượng trở nên thích hợp cho sự dọn rác nếu không có tham chiếu đến nó, hoặc nếu nó đã đăng ký rỗng.
Sự dọn rác thực thi như là một luồng riêng biệt có quyền ưu tiên thấp. Bạn có thể viện dẫn một phương thức gc() của thể nghiệm để viện dẫn sự dọn rác. Tuy nhiên, bạn không thể dự đoán hoặc bảo đảm rằng sự dọn rác sẽ thực thi một cách trọn vẹn sau đó.
Sử dụng câu lện sau để tắt đi sự dọn rác trong ứng dụng:Java –noasyncgc ….
Nếu chúng ta tắt đi sự dọn rác, chương trình hầu như chắc chắn rằng bị treo do bởi việc đó.
Java cung cấp một con đường để làm sạch một tiến trình trước khi điều khiển trở lại hệ điều hành. Điều này tương tự như phương thức phân hủy của C++. Phương thức finalize(), nếu hiện diện, sẽ được thực thi trên mỗi đối tượng, trước khi sự dọn rác. Câu lệnh của phương thức finalize() như sau: protected void finalize() throws Throwable.
II.2.3. Lập trình mạng trong Java:
Nền tảng ngôn ngữ Java đã thu hút được sự quan tâm rất lớn trước đây bởi tính tương thích của nó cho việc viết các chương trình sử dụng và tương tác với các nguồn tài nguyên trên Internet và World Wide Web. Thực tế, các trình duyệt có hỗ trợ Java sử dụng rất nhiều những khả năng này của Java để truyền tải và thực hiện các applet trên Internet.
Phần này của báo cáo sẽ trình bày những kiến thức nhất định để có thể viết được các ứng dụng mạng bằng ngôn ngữ Java.
II.2.3.1. Các kiến thức cơ sở về mạng máy tính:
Các máy tính trên mạng Internet liên lạc với nhau hoặc qua giao thức điều khiển truyền (TCP - Transport Control Protocol) hoặc giao thức đơn vị dữ liệu người dùng (UDP - User Datagram Protocol) như hình vẽ sau
Khi viết các chương trình Java để kết nối trên mạng, chúng ta sẽ làm việc với tầng ứng dụng (Application). Hầu như chúng ta không cần phải quan tâm gì đến tầng TCP và UDP. Thay vào đó, chúng ta có thể dùng các lớp trong gói java.net. Những lớp này cung cấp cho ta hệ thống độc lập mạng truyền thông. Tuy nhiên, để có thể quyết định được nên sử dụng lớp nào của Java cho ứng dụng của mình thì chúng ta cũng cần phải hiểu rõ được sự khác nhau giữa TCP và UDP.
Hình: Các giao thức sử dụng trên các tầng.
Khi hai ứng dụng muốn kết nối với nhau một cách chắc chắn, chúng thiết lập một kết nối và gửi dữ liệu qua lại thông qua kết nối đó. TCP phải đảm bảo việc gửi dữ liệu từ một đầu của kết nối đến được đầu kia và theo đúng trình tự đã gửi đi, nếu không nó sẽ thông báo lỗi.
TCP cung cấp một kênh truyền điểm-tới-điểm (point to point channel) cho ứng dụng có yêu cầu một kết nối tin cậy. Giao thức truyền siêu văn bản (HTTP), giao thức truyền file (FTP) và Telnet là tất cả các ví dụ về các ứng dụng có yêu cầu kênh kết nối tin cậy.
TCP được định nghĩa là một giao thức kết nối có liên kết cung cấp một dòng dữ liệu tin cậy giữa hai máy tính.
II.2.3.3. Khái niệm UDP:
Giao thức UDP không phải là kết nối có liên kết như TCP, cung cấp một kết nối không đảm bảo giữa hai ứng dụng trên mạng. Nó gửi các gói dữ liệu độc lập trên mạng gọi là các đơn vị dữ liệu (datagrams), từ ứng dụng này đến ứng dụng khác. Việc gửi dữ liệu thế này giống như việc gửi thư trên dịch vụ bưu điện vậy. Trình tự phân phối không quan trọng và không được đảm bảo. Các thông điệp gửi đi là độc lập với nhau.
UDP được định nghĩa là một giao thức gửi các gói dữ liệu độc lập nhau, gọi là đơn vị dữ liệu, từ máy tính này đến máy tính khác mà không đảm bảo việc gửi đến đích. UDP không phải là kết nối có liên kết như TCP.
II.2.3.4. Các cổng (Ports):
Nói chung, mỗi máy tính có một kết nối vật lý đơn đến mạng máy tính. Tất cả các dữ liệu đã được trù định từ trước cho mỗi máy tính riêng biệt được gửi đến qua kết nối đó. Tuy nhiên dữ liệu có thể được trù định để gửi cho một ứng dụng đang chạy nào đó trong máy tính. Để máy tính có thể chuyển tiếp dữ liệu cho đúng ứng dụng cần nhận, nó dùng khái niệm cổng (port).
Dữ liệu gửi trên mạng Internet thì được gửi kèm theo thông tin về địa chỉ nhằm xác định máy tính và cổng cần gửi đến. Mỗi máy tính được định danh bởi địa chỉ IP 32-bit, còn các cổng được định danh bởi một số 16-bit.
Trong truyền thông có liên kết như TCP, một ứng dụng server nối kết một socket tới một địa chỉ cổng đã chỉ định. Việc này sẽ được đăng ký vào hệ thống để server có thể nhận được mọi dữ liệu đi đến cổng đó. Mỗi client có thể kết nối được với server qua địa chỉ cổng mà server đã đăng ký.
Trong truyền thông đơn vị dữ liệu như UDP, các gói đơn vị dữ liệu sẽ bao gồm địa chỉ cổng và UDP hướng gói tin đến ứng dụng cần nhận.
Địa chỉ cổng nằm trong phạm vi từ 0 đến 65535, nhưng trong phạm vi từ 0 đến 1023 thì bị hạn chế. Chúng dùng để dự trữ cho các dịch vụ nổi tiếng như HTTP, FTP và các dịch vụ khác của hệ thống. ứng dụng của ta không nên cố thử nối kết tới chúng.
II.2.3.5. Các lớp thiết kế cho mạng của Java:
Thông qua các lớp trong gói java.net, chương trình Java có thể sử dụng giao thức TCP hoặc UDP để kết nối trên Internet. Các lớp: URL, URLConnection, Socket và ServerSocket phục vụ cho kết nối bằng giao thức TCP, còn các lớp DatagramPacket, DatagramSocket và MulticastSocket là dùng với giao thức UDP.
II.2.3.6. Tổng quan về Sockets:
II.2.3.6.1. Khái niệm Socket:
Socket là một điểm cuối của kết nối truyền thông hai đường giữa hai chương trình đang chạy trên mạng. Mỗi socket được bao bởi một địa chỉ cổng để tầng TCP có thể xác định được ứng dụng mà dữ liệu định gửi đến. Gói java.net có cung cấp một lớp Socket nhằm thiết lập đơn phương một kết nối hai chiều giữa chương trình Java của bạn với một ứng dụng đang chạy trên mạng. Hơn nữa, gói java.net còn cung cấp thêm một lớp ServerSocket phục vụ cho việc thiết lập một socket mà server có thể dùng để nghe và chấp nhận các kết nối tới phía client.
II.2.3.6.2. Đọc và ghi tới một Socket:
Để một chương trình có thể thiết lập một kết nối tới chương trình server sử dụng lớp Socket và rồi để client có thể gửi và nhận dữ liệu từ phía server thông qua soket, cần thực hiện những bước cơ bản sau:
• Mở một dòng vào và dòng ra cho socket.
• Đọc và ghi dữ liệu tuỳ thuộc vào giao thức của server.
• Đóng Socket
Để hỗ trợ nhiều client kết nối tới server, ta tạo luồng (Thread) cho mỗi client.
II.2.3.7. Tổng quan về đơn vị dữ liệu (DataGrams):
II.2.3.7.1. Khái niệm đơn vị dữ liệu (datagram):
Đơn vị dữ liệu là một thông điệp độc lập, được gửi đi trên mạng mà nơi đến, thời gian đến cũng như nội dung của thông điệp là không được đảm bảo. Gói java.net có hai lớp để giúp bạn viết các chương trình Java mà sử dụng các đơn vị dữ liệu để gửi và nhận các gói trên mạng: DatagramSocket, DatagramPacket và MulticastSocket. Mỗi ứng dụng có thể gửi và nhận các DatagramPacket qua một DatagramSocket. Hơn nữa, các DatagramPacket có thể được quảng bá tới nhiều nơi nhận mà nó đang Ęlắng ngheę MulticastSocket.
II.2.3.7.2. Datagram client và server:
Ta hãy giả sử rằng ta đang xây dựng hai ứng dụng: một client và một server. Server sẽ liên tục nhận các gói đơn vị dữ liệu qua Datagram socket. Mỗi gói đơn vị dữ liệu nhận được chỉ ra yêu cầu thông tin của client. Khi server nhận được một đơn vị dữ liệu, nó đáp lại bằng cách gửi một gói đơn vị dữ liệu chứa thông tin như phía client đã yêu cầu.
Client có nhiệm vụ gửi gói đơn vị dữ liệu đơn lẻ đến server cho server biết nó cần nhận thông tin gì, sau đó nó sẽ đợi phía server gửi thông tin trả lời.
Phía server: Phía Server sẽ tạo ra mảng buffer để chứa dữ liệu nhận được từ client gửi đến.
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet);
Server sau khi nhận được yêu cầu, nó sẽ lấy thông tin từ file, đưa vào buf và tiến hành gửi cho client. Phương thức run gửi trả lời đến client qua DatagramSocket.
InetAddress address = packet.getAddress(); int port = packet.getPort();
packet = new DatagramPacket(buf, buf.length, address, port); socket.send(packet);
Sau khi gửi xong, server đóng socket lại, kết thúc quá trình gửi trả thông tin socket.close();
Phía client: Phần đầu của hàm main của Client chứa mô tả vài biến int port;
InetAddress address;
DatagramSocket socket = null; DatagramPacket packet;
byte[] sendBuf = new byte[256]; Tiếp theo sẽ là hàm tạo DatagramSocket:
DatagramSocket socket = new DatagramSocket(); Sau đó là gửi yêu cầu đến server:
byte[] buf = new byte[256];
InetAddress address = InetAddress.getByName(args[0]);
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 4445);
socket.send(packet);
Cuối cùng là hiển thị thông tin nhận được từ server: packet = new DatagramPacket(buf, buf.length); socket.receive(packet);
String received = new String(packet.getData()); System.out.println("Info: " + received);
II.2.3.8. Quảng bá tới nhiều nơi nhận:
Để bổ xung cho lớp DatagramSocket có thể gửi các gói tin đến nhiều nơi khác, gói java.net có thêm lớp MulticastSocket sử dụng bên phía client nhằm đợi các gói tin mà server quảng bá tới. Như vậy, phía server phải sử dụng lớp Thread để hỗ trợ đa luồng.
Lúc này, người ta dùng InetAddress và port được gửi từ phía các client đến server để dựng DatagramPacket. Lý do này là để server cần trả lời trực tiếp đến các client.
InetAddress group = InetAddress.getByName( "230.0.0.1"); DatagramPacket packet;
packet = new DatagramPacket(buf, buf.length, group, 4446); socket.send(packet);
Theo cách tạo này, DatagramPacket sẽ được gửi đến mọi client đang nghe theo cổng 4446, những client là thành viên của nhóm "230.0.0.1".
Để nghe được cổng 4446, mỗi client mới chỉ cần tạo MulticastSocket của nó với cổng 4446. Và để trở thành thành viên của nhóm "230.0.0.1", client gọi phương thức joinGroup của MulticastSocket với InetAddress xác định nhóm.
MulticastSocket socket = new MulticastSocket(4446); InetAddress group = InetAddress.getByName("230.0.0.1"); socket.joinGroup(group);
DatagramPacket packet;
for (int i = 0; i < 5; i++) { byte[] buf = new byte[256];
packet = new DatagramPacket(buf, buf.length); socket.receive(packet);
String received = new String(packet.getData());
System.out.println("Quote of the Moment: " + received); }
socket.leaveGroup(group); socket.close();
II.3. Apache Ant:
Apache Ant 1.5.x được viết bằng Java, có tính nǎng tương tự công cụ Make trong C/C++. Phiên bản 1.5.1 hiện nay có thể download tại site
http://jakarta.apache.org/ant/.
Apache Ant đã trở nên nổi trội do đã giải quyết được các vấn đề xuất hiện trong các công cụ tạo lập khác như là make, gnumake, nmake và jam. Như là một công cụ tạo lập hướng vào Java, công cụ này tập trung vào việc dùng các lớp Java và các đối tượng cho các tác vụ cụ thể trong khi xây dựng một chương trình ứng dụng. Hơn thế nữa, bằng cách sử dụng các lớp, Ant đã đạt được khả nǎng có thể làm việc trên nhiều nền khác nhau - một đặc điểm mà các công cụ tạo lập khác không có. Thêm vào đó, có nhiều công cụ có sẵn khác cho phép bạn tích hợp Ant vào. Các công cụ này bao gồm NetBeans, WebSphere Studio Application
Developer, Eclipse, Oracle 9i Developer, NetBeans/Sun ONE Studio, JDE(E) 2.2.8, IntelliJ IDEA 2.0 và hơn thế nữa.
Apache Ant 1.5.x nhận được giải thưởng sản phẩm của nǎm do JupiterMedia bình chọn cho nhóm tiện ích phát triển.
II.4. Giới thiệu một vài IDE sử dụng để phát triển ngôn ngữ Java:
Việc sử dụng IDE trong phát triển ngôn ngữ là rất quan trong, nó giúp giảm thiểu thời gian xây dựng và quản lý chương trình, tăng hiệu quả của lập trình. Sau đây nhóm em xin giới thiệu hai IDE cho ngôn ngữ Java, đây là hai IDE mạnh và được sử dụng nhiều hiện nay:
II.4.1. NetBeans – Môi trường phát triển mã nguồn mở:
NetBeans là môi trường phát triển mã nguồn mở cho ngôn ngữ Java của Sun Microsystems. Một giao diện chuyên nghiệp đến bất ngờ là ấn tượng đầu tiên khi bạn chạy chương trình. Một lợi thế của NetBeans là chi phí về bản quyền. Để tải NetBeans các bạn vào http://www.netbeans.org/downloads/ide/index.html.
NetBeans hỗ trợ rất nhiều loại ứng dụng trên Java và luôn được sửa lỗi để ngày càng hoàn thiện hơn. Sau đây là giao diện của IDE này:
Hình: Giao diện NetBeans.
II.4.2. Eclipse:
Những ai đã từng lập trình Java trên Eclipse thường chỉ có một nhận xét: hài lòng. Thật vậy, công cụ được viết bằng Java của anh cả IBM là một công cụ phát triển phần mềm mạnh mẽ. Eclipse được phát triển theo mô hình nền (platform), tự nó không cung cấp các chức năng cho người dùng cuối mà chỉ cung cấp một kiến trúc mở để các plugin gắn vào nó làm việc đó. Chính vì vậy Eclipse giống như một hệ điều hành để chúng ta cài đặt các công cụ cần thiết cho quá trình phát triển ứng dụng. Nó làm hài lòng giới lập trình viên Java vì có khá nhiều plugin mã nguồn mở phục vụ công việc. Hơn nữa, nếu thích, bạn còn dễ dàng phát triển một plugin phục vụ mục đích của mình.
Hình: Giao diện Eclipse.
PHẦN III: PHÂN TÍCH CHƯƠNG TRÌNH
III.1. Giới thiệu chung:
Ở đây chúng em xây dựng một chương trình mô phỏng môn thể thao vua hiện nay, đó là môn bóng đá. Nó cũng thể hiện gần như đầy đủ nhất các tính chất của agent. Trong bóng đá, mục đích của cầu thủ là cùng toàn đội đưa bóng vào lưới đối phương đồng thời không cho đối phương đưa bóng vào lưới mình. Để đạt được mục tiêu này, cầu thủ phải tìm mọi cơ hội đưa bóng vào gần lưới đối phương và sút, đây chính là thể hiện của tính chủ động. Theo nguyên tắc cầu thủ phải luôn đưa bóng hướng về phía cầu ngôn đối phương, tuy nhiên trong từng tình huống cụ thể trên sân, có khi cầu thủ phải thay đổi mục tiêu tạm thời như chuyền ngang thậm chí chuyền về. Khi đối phương vào bóng thô bạo thì mục tiêu trước mắt có
thể là chưa phải sút bóng mà là giữ an toàn cho bản thân mình, đó là thể hiện của tính phản xạ. Các cầu thủ trên sân cũng phải có tính cộng đồng thể hiện sự phối hợp trên sân theo đúng chiến thuật của huấn luyện viên.
Trò chơi này được viết bằng ngôn ngữ Java, một ngôn ngữ được sử dụng rộng rãi nhất và được đánh giá cao hiện nay. Trò chơi sử dụng kiến trúc client-server, bao gồm:
• Server: Tạo một sân bóng ảo, mô phỏng tất cả các cầu thủ và quả bóng, và kiểm soát để đảm bảo các cầu thủ theo đúng các luật của trò chơi.
• Client: Gồm hai loại client đó là người chơi (player client) và khung nhìn (viewer client). Mỗi loại client hoặc được điều khiển bởi người chơi thông qua giao diện đồ họa hoặc thông qua một chương trình trí tuệ nhân tạo. Server sẽ giao tiếp với các client thông qua việc trao đổi các xâu ASCII (LISP) thông qua socket theo giao thức UDP. Player client (tương như một agent) sẽ tự thu nhận các thông tin từ server thông qua các bộ cảm nhận ‘thính giác’ và ‘thị giác’ và qua đó phản ứng lại thông qua việc gửi các lệnh tới server.
III.2. Kiến trúc của game:
Hình 13: Kiến trúc của game.
Server cung cấp nhận thức cho các client (agent) đồng thời nhận các hành động (action) từ chúng. Server gồm 5 lớp chính:
• SoccerWorld: Biểu diễn trạng thái môi trường trong games bao gồm: sân bóng, hai đội chơi, một nhóm các khung nhìn và một quả bóng.
• Host: Một thread chờ kết nối từ các agent và nhận các hành động (action)