LibGDX Tutorial 13 - Network cơ bản
Trong tutorial này, chúng ta sẽ tìm hiểu về Network trong ứng dụng LibGDX. Network trong LibGDX tương đối nguyên thuỷ, chỉ hỗ trợ kết nối thông qua socket. Trong nhiều trường hợp, như vậy là quá đủ. Chúng ta sẽ ứng dụng socket đơn giản thông qua một ứng dụng chat. Lần này mình sẽ giải thích bằng ...
Trong tutorial này, chúng ta sẽ tìm hiểu về Network trong ứng dụng LibGDX. Network trong LibGDX tương đối nguyên thuỷ, chỉ hỗ trợ kết nối thông qua socket. Trong nhiều trường hợp, như vậy là quá đủ. Chúng ta sẽ ứng dụng socket đơn giản thông qua một ứng dụng chat. Lần này mình sẽ giải thích bằng cách comment ngay trên code vì đoạn code tương đối phức tạp.
Hãy nhìn vào đoạn code dưới đây:
package com.thinhhung.game; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Net; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.net.ServerSocket; import com.badlogic.gdx.net.ServerSocketHints; import com.badlogic.gdx.net.Socket; import com.badlogic.gdx.net.SocketHints; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.TextArea; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.utils.viewport.StretchViewport; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; public class NetworkingSample implements ApplicationListener { OrthographicCamera camera; SpriteBatch batch; Skin skin; Stage stage; Label labelDetails; Label labelMessage; TextButton button; TextArea textIPAddress; TextArea textMessage; // Tỉ lệ 16:9 float VIRTUAL_SCREEN_HEIGHT = 960; float VIRTUAL_SCREEN_WIDTH = 540; @Override public void create () { camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); batch = new SpriteBatch(); // Nạp UI Skin từ file. Chúng ta sử dụng skin mặc định có trong thư mục tests của LibGDX repository trên Gibhub giống như ở tutorial trước. // Đảm bảo những file: default.fnt, default.png, uiskin.[atlas/json/png] đã được thêm trong thư mục assets của project android. skin = new Skin(Gdx.files.internal("uiskin.json")); stage = new Stage(); // Sử dụng stage làm input processor vì chúng ta đang sử dụng Scene2D trong ví dụ này Gdx.input.setInputProcessor(stage); // Đoạn code sau duyệt qua chác Network Interface đang available // Có rất nhiều interfaces trên thiết bị. Như là NIC, wireless,... // Ở đây chúng ta chỉ quan tâm đến IPv4 address (với format x.x.x.x) List<String> addresses = new ArrayList<String>(); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); for(NetworkInterface ni : Collections.list(interfaces)){ for(InetAddress address : Collections.list(ni.getInetAddresses())) { if(address instanceof Inet4Address){ addresses.add(address.getHostAddress()); } } } } catch (SocketException e) { e.printStackTrace(); } // In ra các chuỗi IP Address từ mảng String ipAddress = new String(""); for (String str:addresses) { ipAddress = ipAddress + str + " "; } // Setup Scene UI // Nhóm nội dung theo chiều dọc sử dụng VerticalGroup VerticalGroup verticalGroup = new VerticalGroup().space(3).pad(5).fill(); // Thiết lập các giới hạn (bounds) cho virtual display verticalGroup.setBounds(0, 0, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT); // Tạo các form controls labelDetails = new Label(ipAddress, skin); labelMessage = new Label("Hello world", skin); textIPAddress = new TextArea("", skin); textMessage = new TextArea("", skin); button = new TextButton("Send message", skin); // Add chúng vào scene verticalGroup.addActor(labelDetails); verticalGroup.addActor(labelMessage); verticalGroup.addActor(textIPAddress); verticalGroup.addActor(textMessage); verticalGroup.addActor(button); // Add scene vào stage stage.addActor(verticalGroup); // Setup viewport stage.setViewport(new StretchViewport(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT)); stage.getCamera().position.set(VIRTUAL_SCREEN_WIDTH / 2, VIRTUAL_SCREEN_HEIGHT / 2, 0); // Tạo một thread lắng nghe những socket kết nối đến new Thread(new Runnable(){ @Override public void run() { ServerSocketHints serverSocketHint = new ServerSocketHints(); // 0 nghĩa là không có timeout. Chúng ta sẽ không làm như thế này trên production serverSocketHint.acceptTimeout = 0; // Tạo socket server sử dụng giao thức TCP và lắng nghe cổng 9021 // Chỉ một ứng dụng lắng nghe một cổng tại một thời gian, bạn có thể chọn một cổng khác chưa được sử dụng bởi ứng dụng khác // đặc biệt tránh sử dụng các cổng với số nhỏ ( như 21, 80, ... ) ServerSocket serverSocket = Gdx.net.newServerSocket(Net.Protocol.TCP, 9021, serverSocketHint); // Vòng lặp vô hạn while(true){ // Tạo một socket Socket socket = serverSocket.accept(null); // Đọc dữ liệu từ socket sử dụng BufferedReader BufferedReader buffer = new BufferedReader(new InputStreamReader(socket.getInputStream())); try { // Đọc dòng tiếp theo ( ) và hiển thị nội dung trên labelMessage labelMessage.setText(buffer.readLine()); } catch (IOException e) { e.printStackTrace(); } } } }).start(); // Và chạy thread // Đăng ký listener cho button button.addListener(new ClickListener(){ @Override public void clicked(InputEvent event, float x, float y){ // Khi button được click, nhận một message hoặc tạo ra một chuỗi mặc String textToSend = new String(); if(textMessage.getText().length() == 0) { textToSend = "The button was clicked "; } else { textToSend = textMessage.getText() + (" "); } SocketHints socketHints = new SocketHints(); // Socket sẽ timeout trong 4 giây socketHints.connectTimeout = 4000; // Tạo socket và kết nối tới server được nhập vào trong text box ( theo định dạng x.x.x.x ) trên cổng 9021 Socket socket = Gdx.net.newClientSocket(Net.Protocol.TCP, textIPAddress.getText(), 9021, socketHints); try { // Ghi các tin nhắn được nhập vào stream socket.getOutputStream().write(textToSend.getBytes()); } catch (IOException e) { e.printStackTrace(); } } }); } @Override public void resize(int awidth, int height) { stage.getViewport().update(awidth, height); } @Override public void render () { Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); batch.setProjectionMatrix(camera.combined); batch.begin(); stage.draw(); batch.end(); } @Override public void pause() { } @Override public void resume() { } @Override public void dispose() { batch.dispose(); } }
Và khi chạy code bạn sẽ thấy:
Hai giá trị đầu tiên là địa chỉ IP v4 mà máy bạn đang chạy, giá trị thứ nhất là địa chỉ localhost, giá trị thứ 2 là địa chỉ trong mạng LAN. Bạn cần phải nhập giá trị này trong ứng dụng mà chúng ta đã làm chạy trên một máy tính khác cùng một mạng LAN để nhận message. Địa chỉ IP xác định tính duy nhất máy tính của bạn trong network, trong khi đó chúng ta chỉ định cổng (9021) trong code.
Tiếp theo nhìn đến đoạn text "Hello world", nơi mà các message sẽ được hiển thị.
Text box đầu tiên là địa chỉ IP mà bạn muốn gửi tới. Trong trường hợp này bạn có thể nhập địa chỉ IP của chính mình, chỉ cần nhập địa chỉ IP của bạn. Text box thứ hai là nội dung message bạn muốn gửi. Và tất nhiên để gửi được message bạn cần nhấn nút "Send message".
Bạn có thể tham khảo source code của project này tại đây