0

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 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 + "\n";
		}

		// 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 (\n) 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\n";
				} else {
					textToSend = textMessage.getText() + ("\n");
				}

				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 width, int height) {
		stage.getViewport().update(width, 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:

Screen Shot 2016-05-28 at 5.46.26 PM.png

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".

Source Code

Bạn có thể tham khảo source code của project này tại đây


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí