Xây dựng ứng dụng Client-Server với Socket trong Java

Công Nghệ
Xây dựng ứng dụng Client-Server với Socket trong Java
Bài viết được sự cho phép của tác giả Giang Phan Socket là phương tiện hiệu quả để xây dựng các ứng dụng theo kiến trúc Client-Server. Trong bài viết này, tôi sẽ hướng dẫn các bạn các bước cơ bản trong việc xây dựng các ứng dụng Client-Server sử dụng Socket làm phương tiện giao tiếp theo cả hai chế độ: có nối kết ( TCP – Transmission Control Protocol ) và không nối kết ( UDP – User Datagram Protocol ). Các bạn nên xem bài viết giới thiệu Lập trình mạng trong Java trước khi tiếp tục xem nội dung tiếp theo của bài viết. Mô hình Client-Server sử dụng Socket ở chế độ có nối kết (TCP) Có thể phân thành 4 giai đoạn như sau: Giai đoạn 1 : Server tạo Socket, gán số hiệu cổng và lắng nghe yêu cầu nối kết. Server sẵn sàng phục vụ Client.socket(): Server yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển. bind(): Server yêu cầu gán số hiệu cổng (port) cho socket. listen(): Server lắng nghe các yêu cầu nối kết từ các client trên cổng đã được gán. Giai đoạn 2 : Client tạo Socket, yêu cầu thiết lập một nối kết với Server. socket(): Client yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển, thông thường hệ thống tự động gán một số hiệu cổng còn rảnh cho socket của Client. connect(): Client gởi yêu cầu nối kết đến server có địa chỉ IP và Port xác định. accept(): Server chấp nhận nối kết của client, khi đó một kênh giao tiếp ảo được hình thành, Client và server có thể trao đổi thông tin với nhau thông qua...

Bài viết được sự cho phép của tác giả Giang Phan

Socket là phương tiện hiệu quả để xây dựng các ứng dụng theo kiến trúc Client-Server. Trong bài viết này, tôi sẽ hướng dẫn các bạn các bước cơ bản trong việc xây dựng các ứng dụng Client-Server sử dụng Socket làm phương tiện giao tiếp theo cả hai chế độ: có nối kết (TCP – Transmission Control Protocol) và không nối kết ( UDP – User Datagram Protocol).

Các bạn nên xem bài viết giới thiệu Lập trình mạng trong Java trước khi tiếp tục xem nội dung tiếp theo của bài viết.

Mô hình Client-Server sử dụng Socket ở chế độ có nối kết (TCP)

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Có thể phân thành 4 giai đoạn như sau:

  • Giai đoạn 1: Server tạo Socket, gán số hiệu cổng và lắng nghe yêu cầu nối kết. Server sẵn sàng phục vụ Client.socket(): Server yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển.
    • bind(): Server yêu cầu gán số hiệu cổng (port) cho socket.
    • listen(): Server lắng nghe các yêu cầu nối kết từ các client trên cổng đã được gán.
  • Giai đoạn 2: Client tạo Socket, yêu cầu thiết lập một nối kết với Server.
    • socket(): Client yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển, thông thường hệ thống tự động gán một số hiệu cổng còn rảnh cho socket của Client.
    • connect(): Client gởi yêu cầu nối kết đến server có địa chỉ IP và Port xác định.
    • accept(): Server chấp nhận nối kết của client, khi đó một kênh giao tiếp ảo được hình thành, Client và server có thể trao đổi thông tin với nhau thông qua kênh ảo này.
  • Giai đoạn 3: Trao đổi thông tin giữa Client và Server.
    • Sau khi chấp nhận yêu cầu nối kết, thông thường server thực hiện lệnh read() và nghẽn cho đến khi có thông điệp yêu cầu (Request Message) từ client gởi đến.
    • Server phân tích và thực thi yêu cầu. Kết quả sẽ được gởi về client bằng lệnh write().
    • Sau khi gởi yêu cầu bằng lệnh write(), client chờ nhận thông điệp kết quả (ReplyMessage) từ server bằng lệnh read().
  • Giai đoạn 4: Kết thúc phiên làm việc.
    • Các câu lệnh read(), write() có thể được thưc hiện nhiều lần (ký hiệu bằng hình ellipse).
    • Kênh ảo sẽ bị xóa khi Server hoặc Client đóng socket bằng lệnh close().

Mô hình Client-Server sử dụng Socket ở chế độ không nối kết (UDP)

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Có thể phân thành 3 giai đoạn như sau:

  • Giai đoạn 1:  Server tạo Socket – gán số hiệu cổng.
    • socket(): Server yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển.
    • bind(): Server yêu cầu gán số hiệu cổng cho socket.
  • Giai đoạn 2:  Client tạo Socket.
    • socket(): Client yêu cầu tạo một socket để có thể sử dụng các dịch vụ của tầng vận chuyển, thông thường hệ thống tự động gán một số hiệu cổng còn rảnh cho socket của Client.
  • Giai đoạn 3:  Trao đổi thông tin giữa Client và Server.
    • Sau khi tạo Socket xong, Client và Server có thể trao đổi thông tin qua lại với nhau thông qua hai hàm send() và receive().
    • Đơn vị dữ liệu trao đổi giữa Client và Server là các Datagram Package (Gói tin thư tín).
    • Protocol của ứng dụng phải định nghĩa khuôn dạng và ý nghĩa của các Datagram Package. Mỗi Datagram Package có chứa thông tin về địa chỉ người gởi và người nhận (IP, Port).

Xây dựng ứng dụng Client-Server với Socket trong Java

Thông qua các lớp trong gói java.net, các chương trình Java có thể sử dụng TCP hoặc UDP để giao tiếp qua Internet.

  • Lớp IntetAddress:  Lớp này quản lý địa chỉ Internet bao gồm địa chỉ IP và tên máy tính.
  • Lớp Socket: Hỗ trợ các phương thức liên quan đến Socket cho chương trình Client ở chế độ có nối kết.
  • Lớp ServerSocket: Hỗ trợ các phương thức liên quan đến Socket cho chương trình Server ở chế độ có nối kết.
  • Lớp DatagramSocket: Hỗ trợ các phương thức liên quan đến Socket ở chế độ không nối kết cho cả Client và Server.
  • Lớp DatagramPacket: Lớp cài đặt gói tin dạng thư tín người dùng (Datagram Packet) trong giao tiếp giữa Client và Server ở chế độ không nối kết.

Tuyển Java lương cao up to 2000 USD

Xây dựng chương trình Client – Server ở chế độ có nối kết (TCP)

Trong phần tiếp theo tôi sẽ hướng dẫn các bạn xây dựng chương trình Client – Server đơn giản. Client sẽ gởi lần lượt các số từ 0 đến 9 tới Server. Server lần lượt sẽ gởi các số nhận được về Client.

Các bước tổng quát xây dựng một chương trình Client – Server ở chế độ có nối kết như sau:

  • Mở một socket nối kết đến server đã biết địa chỉ IP (hay tên miền) và số hiệu cổng.
  • Lấy InputStream và OutputStream gán với Socket.
  • Tham khảo Protocol của dịch vụ để định dạng đúng dữ liệu trao đổi với Server.
  • Trao đổi dữ liệu với Server nhờ vào các InputStream và OutputStream.
  • Đóng Socket trước khi kết thúc chương trình.

Xây dựng chương trình Client ở chế độ có nối kết

Một số phương thức cần thiết để xây dựng các chương trình client sử dụng socket ở chế độ có nối kết:

    • public Socket(String HostName, int PortNumber) : Phương thức này dùng để nối kết đến một server có tên là HostName, cổng là PortNumber. Nếu nối kết thành công, một kênh ảo sẽ được hình thành giữa Client và Server.
      • HostName: Địa chỉ IP hoặc tên logic theo dạng tên miền.
      • PortNumber: có giả trị từ 0 ..65535
    • public InputStream getInputStream() : Phương thức này trả về InputStream nối với Socket. Chương trình Client dùng InputStream này để nhận dữ liệu từ Server gởi về.
    • public OutputStream getOutputStream() :  Phương thức này trả về OutputStream nối với Socket. Chương trình Client dùng OutputStream này để gởi dữ liệu cho Server.
    • public close() :  Phương thức này sẽ đóng Socket lại, giải phóng kênh ảo, xóa nối kết giữa Client và
      Server.

Code của chương trình Client như sau:

package com.gpcoder.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class EchoChatClient {
	public final static String SERVER_IP = "127.0.0.1";
	public final static int SERVER_PORT = 7;

	public static void main(String[] args) throws IOException, InterruptedException {
		Socket socket = null;
		try {
			socket = new Socket(SERVER_IP, SERVER_PORT); // Connect to server
			System.out.println("Connected: " + socket);

			InputStream is = socket.getInputStream();
			OutputStream os = socket.getOutputStream();
			for (int i = '0'; i <= '9'; i++) {
				os.write(i); // Send each number to the server
				int ch = is.read(); // Waiting for results from server
				System.out.print((char) ch + " "); // Display the results received from the server
				Thread.sleep(200);
			}
		} catch (IOException ie) {
			System.out.println("Can't connect to server");
		} finally {
			if (socket != null) {
				socket.close();
			}
		}
	}
}

Xây dựng chương trình Server ở chế độ có nối kết

Một số phương thức cần thiết để xây dụng các chương trình Server sử dụng socket ở chế độ có nối kết.:

  • public ServerSocket(int portNumber) : phương thức này tạo một Socket với số hiệu cổng là portNumber mà sau đó Server sẽ lắng nghe trên cổng này.
  • public Socket accept() Phương thức này lắng nghe yêu cầu nối kết của các Client. Đây là một phương thức hoạt động ở chế độ nghẽn. Nó sẽ bị nghẽn cho đến khi có một yêu cầu nối kết của client gởi đến. Khi có yêu cầu nối kết của Client gởi đến, nó sẽ chấp nhận yêu cầu nối kết, trả về
    một Socket là một đầu của kênh giao tiếp ảo giữa Server và Client yêu cầu nối kết.
  • public InputStream getInputStream() : Phương thức này trả về InputStream nối với Socket. Chương trình Server dùng InputStream này để nhận dữ liệu từ Client gởi đến.
  • public OutputStream getOutputStream() :  Phương thức này trả về OutputStream nối với Socket. Chương trình Server dùng OutputStream này để trả dữ liệu cho Client.
  • public close() :  Phương thức này sẽ đóng Socket lại, giải phóng kênh ảo, xóa nối kết giữa Client và
    Server.

Một Server có thể được cài đặt để phục vụ các Client theo hai cách: phục vụ tuần tự hoặc phục vụ song song.

  • Trong chế độ phục vụ tuần tự, tại một thời điểm Server chỉ chấp nhận một yêu cầu nối kết. Các yêu cầu nối kết của các Client khác đều không được đáp ứng (đưa vào hàng đợi).
  • Ngược lại trong chế độ phục vụ song song, tại một thời điểm Server chấp nhận nhiều yêu cầu nối kết và phục vụ nhiều Client cùng lúc.

Xây dựng chương trình Server phục vụ tuần tự

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Các bước thực hiện như sau:

  • Tạo socket và gán số hiệu cổng cho server.
  • Lắng nghe yêu cầu nối kết.
  • Với một yêu cầu nối kết được chấp nhận thực hiện các bước sau:
    • Lấy InputStream và OutputStream gắn với Socket của kênh ảo vừa được hình thành.
    • Lặp lại công việc sau:
      • Chờ nhận các yêu cầu (công việc).
      • Phân tích và thực hiện yêu cầu.
      • Tạo thông điệp trả lời.
      • Gởi thông điệp trả lời về Client.
      • Nếu không còn yêu cầu hoặc Client kết thúc, đóng Socket và quay lại bước 2.

Code của chương trình Server như sau:

package com.gpcoder.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoChatSingleServer {

	public final static int SERVER_PORT = 7;

	public static void main(String[] args) throws IOException {
		ServerSocket serverSocket = null;
		try {
			System.out.println("Binding to port " + SERVER_PORT + ", please wait  ...");
			serverSocket = new ServerSocket(SERVER_PORT);
			System.out.println("Server started: " + serverSocket);
			System.out.println("Waiting for a client ...");
			while (true) {
				try {
					Socket socket = serverSocket.accept();
					System.out.println("Client accepted: " + socket);

					OutputStream os = socket.getOutputStream();
					InputStream is = socket.getInputStream();
					int ch = 0;
					while (true) {
						ch = is.read(); // Receive data from client
						if (ch == -1) {
							break;
						}
						os.write(ch); // Send the results to client
					}
					socket.close();
				} catch (IOException e) {
					System.err.println(" Connection Error: " + e);
				}
			}
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (serverSocket != null) {
				serverSocket.close();
			}
		}
	}

}

Chạy chương trình Server, ta có kết quả sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Tiếp tục chạy chương trình Client, ta có kết quả sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xem lại cửa sổ console của Server, ta thấy kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xây dựng chương trình Server phục vụ song song

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Server phục vụ song song có thể chia thành 2 phần thực hiện song song nhau:

  • Phần 1 (Dispatcher Thread) : Xử lý các yêu cầu nối kết.  Lặp lại các công việc sau:
    • Lắng nghe yêu cầu nối kết của Client. 
    • Chấp nhận một yêu cầu nối kết: Tạo kênh giao tiếp ảo mới với Client, tạo Phần 2 để xử lý các thông điệp yêu cầu của Client.
  • Phần 2 (Worker Thread) : Xử lý các thông điệp yêu cầu từ khách hàng. Lặp lại các công việc sau:
    •  Chờ nhận thông điệp yêu cầu của khách hàng.
    • Phân tích và xử lý yêu cầu.
    • Gởi thông điệp trả lời cho khách hàng.

Phần 2 sẽ kết thúc khi kênh ảo bị xóa đi.

Với mỗi Client, trên Server sẽ có một Phần 2 để xử lý yêu cầu của khách hàng. Như vậy tại một thời điểm bất kỳ luôn tồn tại 1 Phần 1 và 0 hoặc nhiều Phần 2.

Code của chương trình Server như sau:

EchoChatMultiServer.java

package com.gpcoder.tcp;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EchoChatMultiServer {

	public static final int NUM_OF_THREAD = 4;
	public final static int SERVER_PORT = 7;

	public static void main(String[] args) throws IOException {
		ExecutorService executor = Executors.newFixedThreadPool(NUM_OF_THREAD);
		ServerSocket serverSocket = null;
		try {
			System.out.println("Binding to port " + SERVER_PORT + ", please wait  ...");
			serverSocket = new ServerSocket(SERVER_PORT);
			System.out.println("Server started: " + serverSocket);
			System.out.println("Waiting for a client ...");
			while (true) {
				try {
					Socket socket = serverSocket.accept();
					System.out.println("Client accepted: " + socket);

					WorkerThread handler = new WorkerThread(socket);
					executor.execute(handler);
				} catch (IOException e) {
					System.err.println(" Connection Error: " + e);
				}
			}
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (serverSocket != null) {
				serverSocket.close();
			}
		}
	}

}

WorkerThread.java

package com.gpcoder.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class WorkerThread extends Thread {
	private Socket socket;

	public WorkerThread(Socket socket) {
		this.socket = socket;
	}

	public void run() {
		System.out.println("Processing: " + socket);
		try {
			OutputStream os = socket.getOutputStream();
			InputStream is = socket.getInputStream();
			while (true) {
				int ch = is.read(); // Receive data from client
				if (ch == -1) {
					break;
				}
				os.write(ch); // Send the results to client
			}
		} catch (IOException e) {
			System.err.println("Request Processing Error: " + e);
		}
		System.out.println("Complete processing: " + socket);
	}
}

Chạy chương trình Server, ta có kết quả sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Chạy liên tục 3 chương trình Client, ta có kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Như bạn thấy, cả 3 chương trình Client đều được Server xử lý cùng lúc.

Xây dựng chương trình Client – Server ở chế độ không nối kết (UDP)

UDP – User Datagram Protocol : cung cấp cơ chế vận chuyển không bảo đảm và không nối kết trên mạng IP, ngược với giao thức vận chuyển tin cậy, có nối kết TCP.

Cả giao thức TCP và UDP đều phân dữ liệu ra thành các gói tin. Tuy nhiên TCP có thêm vào những tiêu đề (Header) vào trong gói tin để cho phép truyền lại những gói tin thất lạc và tập hợp các gói tin lại theo thứ tự đúng đắn. UDP không cung cấp tính năng này, nếu một gói tin bị thất lạc hoặc bị lỗi, nó sẽ không được truyền lại, và thứ tự đến đích của các gói tin cũng không giống như thứ tự lúc nó được gởi đi.

Tuy nhiên, về tốc độ, UDP sẽ truyền nhanh gấp 3 lần TCP. Cho nên chúng thường được dùng trong các ứng dụng đòi hỏi thời gian truyền tải ngắn và không cần tính chính xác cao, ví dụ truyền âm thanh, hình ảnh …

Mô hình client – server sử dụng lớp ServerSocket và Socket ở trên sử dụng giao thức TCP. Nếu muốn sử dụng mô hình client – server với giao thức UDP, ta sử dụng hai lớp java.net.DatagramSocket và java.net.DatagramPacket

DatagramSocket được sử dụng để truyền và nhận các DatagramPacket. Dữ liệu được truyền đi là một mảng những byte, chúng được gói vào trong lớp DatagramPacket. Chiều dài của dữ liệu tối đa có thể đưa vào DatagramPacket là khoảng 60.000 byte (phụ thuộc vào dạng đường truyền). Ngoài ra DatagramPacket còn chứa địa chỉ IP và cổng của quá trình gởi và nhận dữ liệu.

Cổng trong giao thức TCP và UDP có thể trùng nhau. Trên cùng một máy tính, bạn có thể gán cổng 20 cho socket dùng giao thức TCP và cổng 20 cho socket sử dụng giao thức UDP.

Một số phương thức cần thiết để xây dựng các chương trình Client-Server sử dụng socket ở chế độ không nối kết:

  • public DatagramPacket(byte[] b, int n, InternetAddress ia, int port) :  Phương thức này cho phép tạo một DatagramPacket chứa dữ liệu và cả địa chỉ của máy nhận dữ liệu. Phương thức trả về một đối tượng thuộc lớp DatagramePacket.
  • public DatagramSocket( int port) :  Tạo Socket kiểu không nối kết cho Client với số hiệu cổng được xác định trong tham số (port). Nếu không xác định port, hệ thống tự động gán số hiệu cổng chưa sử dụng cho socket.
  • public void send(DatagramPacket dp) : Dùng để gởi một DatagramPacket đi.
  • public synchronized void receive(Datagrampacket dp) : Chờ nhận một DatagramPacket. Quá trình sẽ bị nghẽn cho đến khi có dữ liệu đến.

Các phương thức lấy thông tin trên một DatagramPacket nhận được:

Khi nhận được một DatagramPacket từ một quá trình khác gởi đến, ta có thể lấy thông tin trên DatagramPacket này bằng các phương thức sau:

  • public synchronized() InternetAddress getAddress() : Địa chỉ máy gởi.
  • public synchronized() int getPort() : Cổng của quá trình gởi.
  • public synchronized() byte[] getData() : Dữ liệu từ gói tin.
  • public synchronized() int getLength() : Chiều dài của dữ liệu trong gói tin.

Các phương thức đặt thông tin cho gói tin gởi:

Trước khi gởi một DatagramPacket đi, ta có thể đặt thông tin trên DatagramPacket này bằng các phương thức sau:

  • public synchronized() void setAddress(IntermetAddress address) : Đặt địa chỉ máy nhận.
  • public synchronized() void setPort(int port) : Đặt cổng quá trình nhận.
  • public synchronized() void setData(byte buffer[]) : Đặt dữ liệu gởi.
  • public synchronized() void setLength(int len) : Đặt chiều dài dữ liệu gởi.

Xây dựng chương trình Server ở chế độ không nối kết

Chương trình EchoServer cài đặt Echo Server ở chế độ không nối kết, cổng mặc định là 7. Chương trình chờ nhận từng gói tin, lấy dữ liệu ra khỏi gói tin nhận được và gởi ngược dữ liệu đó về Client.

package com.gpcoder.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class EchoServer {

	public final static int SERVER_PORT = 7; // Cổng mặc định của Echo Server
	public final static byte[] BUFFER = new byte[4096]; // Vùng đệm chứa dữ liệu cho gói tin nhận

	public static void main(String[] args) {
		DatagramSocket ds = null;
		try {
			System.out.println("Binding to port " + SERVER_PORT + ", please wait  ...");
			ds = new DatagramSocket(SERVER_PORT); // Tạo Socket với cổng là 7
			System.out.println("Server started ");
			System.out.println("Waiting for messages from Client ... ");

			while (true) { // Tạo gói tin nhận
				DatagramPacket incoming = new DatagramPacket(BUFFER, BUFFER.length);
				ds.receive(incoming); // Chờ nhận gói tin gởi đến

				// Lấy dữ liệu khỏi gói tin nhận
				String message = new String(incoming.getData(), 0, incoming.getLength());
				System.out.println("Received: " + message);

				// Tạo gói tin gởi chứa dữ liệu vừa nhận được
				DatagramPacket outsending = new DatagramPacket(message.getBytes(), incoming.getLength(),
						incoming.getAddress(), incoming.getPort());
				ds.send(outsending);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (ds != null) {
				ds.close();
			}
		}
	}
}

Xây dựng chương trình Client ở chế độ không nối kết

Chương trình này cho phép người sử dụng nhận các chuỗi từ bàn phím, gởi chuỗi sang EchoServer ở chế độ không nối kết ở cổng số 7, chờ nhận và in dữ liệu từ Server gởi về ra màn hình.

package com.gpcoder.udp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class EchoClient {

	public final static String SERVER_IP = "127.0.0.1";
	public final static int SERVER_PORT = 7; // Cổng mặc định của Echo Server
	public final static byte[] BUFFER = new byte[4096]; // Vùng đệm chứa dữ liệu cho gói tin nhận

	public static void main(String[] args) {
		DatagramSocket ds = null;
		try {
			ds = new DatagramSocket(); // Tạo DatagramSocket
			System.out.println("Client started ");

			InetAddress server = InetAddress.getByName(SERVER_IP);
			while (true) {
				System.out.println("Enter your message: ");
				InputStreamReader isr = new InputStreamReader(System.in); // Nhập
				BufferedReader br = new BufferedReader(isr); // một chuỗi
				String theString = br.readLine(); // từ bàn phím
				byte[] data = theString.getBytes(); // Đổi chuỗi ra mảng bytes

				// Tạo gói tin gởi
				DatagramPacket dp = new DatagramPacket(data, data.length, server, SERVER_PORT);
				ds.send(dp); // Send gói tin sang Echo Server

				// Gói tin nhận
				DatagramPacket incoming = new DatagramPacket(BUFFER, BUFFER.length);
				ds.receive(incoming); // Chờ nhận dữ liệu từ EchoServer gởi về

				// Đổi dữ liệu nhận được dạng mảng bytes ra chuỗi và in ra màn hình
				System.out.println("Received: " + new String(incoming.getData(), 0, incoming.getLength()));
			}
		} catch (IOException e) {
			System.err.println(e);
		} finally {
			if (ds != null) {
				ds.close();
			}
		}
	}
}

Chạy chương trình Server, ta có kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Chạy chương trình Client, ta có kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Tại Client, nhập nội dung message là Hello. Ta có kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Tại Cient, tiếp tục nhập nội dung là How are you. Ta có kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xây dựng chương trình Client-Server nối kết theo dạng multicast (truyền theo nhóm)

Các cơ chế Socket TCP và UDP đã giới thiệu ở trên đề gọi là unicast, nghĩa là giao tiếp chỉ diễn ra giữa một máy tính gửi và một máy tính nhận.

Multicast là việc gửi quảng bá (broadcast) nhưng đến một nhóm máy tính ở cùng một địa chỉ cho trước. Địa chỉ multicast là địa chỉ lớp D được xác định trong khoảng 244.0.0.0 đến 239.255.255.255. Địa chỉ 244.0.0.0 là địa chỉ riêng nên không sử dụng được.

Multicast được sử dụng trong game nhiều người chơi, trong những ứng dụng mà đối tượng là nhiều thiết bị hay nhiều máy tính cùng nhận một loại thông tin. Multicast cũng được sử dụng trong giải thuật vạch đường (Routing Protocol), khi các router muốn cập nhật thông tin với nhau.

Java hỗ trợ Multicast thông qua lớp java.net.MulticastSocket. Một Multicast Socket là 1 DatagramSocket (UDP) có khả năng gia nhập (joining) vào một nhóm các máy tính multicast trên mạng. Khi một máy tính nào gửi thông điệp đến nhóm thì tất cả các máy tính trong đó đều nhận được.

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Một số phương thức cần thiết để xây dựng các chương trình Multicast:

  • public MulticastSocket(int port): Tạo Socket kiểu Multicast với số hiệu cổng được xác định trong tham số (port).
  • public void joinGroup(InetAddress group) : Tham gia nhóm Multicast tại địa chỉ xác định.
  • public void leaveGroup(InetAddress group) : Rời khỏi nhóm Multicast tại địa chỉ xác định.
  • public void send(DatagramPacket dp) : Dùng để gởi một DatagramPacket đi.
    public synchronized void receive(Datagrampacket dp) : Chờ nhận một DatagramPacket.

Ví dụ:

Cài đặt một dịch vụ gởi tin nhắn trên cổng 8888, tại địa chỉ 224.0.0.1. Khi Client muốn nhận tin nhắn thì Join Group tại địa chỉ và cổng trên.

Lớp Sender

package com.gpcoder.multicast;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MulticastSender {

	public static final String GROUP_ADDRESS = "224.0.0.1";
	public static final int PORT = 8888;

	public static void main(String[] args) throws InterruptedException {
		DatagramSocket socket = null;
		try {
			// Get the address that we are going to connect to.
			InetAddress address = InetAddress.getByName(GROUP_ADDRESS);

			// Create a new Multicast socket
			socket = new DatagramSocket();

			DatagramPacket outPacket = null;
			long counter = 0;
			while (true) {
				String msg = "Sent message No. " + counter;
				counter++;
				outPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length, address, PORT);
				socket.send(outPacket);
				System.out.println("Server sent packet with msg: " + msg);
				Thread.sleep(1000); // Sleep 1 second before sending the next message
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			if (socket != null) {
				socket.close();
			}
		}
	}
}

Lớp Receiver

package com.gpcoder.multicast;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MulticastReceiver {

	public static final byte[] BUFFER = new byte[4096];

	public static void main(String[] args) {
		MulticastSocket socket = null;
		DatagramPacket inPacket = null;
		try {
			// Get the address that we are going to connect to.
			InetAddress address = InetAddress.getByName(MulticastSender.GROUP_ADDRESS);

			// Create a new Multicast socket
			socket = new MulticastSocket(MulticastSender.PORT);

			// Joint the Multicast group
			socket.joinGroup(address);

			while (true) {
				// Receive the information and print it.
				inPacket = new DatagramPacket(BUFFER, BUFFER.length);
				socket.receive(inPacket);
				String msg = new String(BUFFER, 0, inPacket.getLength());
				System.out.println("From " + inPacket.getAddress() + " Msg : " + msg);
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
}

Chạy chương trình MulticastSender, sau đó start tiếp một vài chương trình MulticastReceiver. Chúng ta nhận được kết quả như sau:

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Xây dựng ứng dụng Client-Server với Socket trong JavaXây dựng ứng dụng Client-Server với Socket trong Java

Như bạn thấy, bất kỳ client nào join group đều nhận được nội dung message giống nhau, và nhận cùng thời điểm. Chương trình Multicast Server vẫn phục vụ theo dạng UDP, chỉ khác là sẽ gửi gói tin Datagram đến địa chỉ IP thuộc lớp D.

Tài liệu tham khảo:

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm Việc làm IT hấp dẫn tại Station D

Bài viết liên quan

Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bài viết được sự cho phép của tác giả Chung Nguyễn Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “ laravel/installer ” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name , bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ: API Authentication trong Laravel-Vue SPA sử dụng Jwt-auth Cách sử dụng Laravel với Socket.IO laravel new foo --jet --dev Sau đó, nó sẽ hỏi bạn thích stack Jetstream nào hơn: Which Jetstream stack do you prefer? [0] Livewire [1] inertia > livewire Will your application use teams? (yes/no) [no]: ... Nếu bạn đã cài bộ Laravel Installer, để nâng cấp lên phiên bản mới bạn chạy lệnh: composer global update Một số trường hợp cập nhật bị thất bại, bạn hãy thử, gỡ đi và cài đặt lại nha composer global remove laravel/installer composer global require laravel/installer Bài viết gốc được đăng tải tại chungnguyen.xyz Có thể bạn quan tâm: Cài đặt Laravel Làm thế nào để chạy Sql Server Installation Center sau khi đã cài đặt xong Sql Server? Quản lý các Laravel route gọn hơn và dễ dàng hơn Xem thêm Tuyển dụng lập trình Laravel hấp dẫn trên Station D

By stationd
Principle thiết kế của các sản phẩm nổi tiếng

Principle thiết kế của các sản phẩm nổi tiếng

Tác giả: Lưu Bình An Phù hợp cho các bạn thiết kế nào ko muốn làm code dạo, design dạo nữa, bạn muốn cái gì đó cao hơn ở tầng khái niệm Nếu lập trình chúng ta có các nguyên tắc chung khi viết code như KISS , DRY , thì trong thiết kế cũng có những nguyên tắc chính khi làm việc. Những nguyên tắc này sẽ là kim chỉ nam, nếu có tranh cãi giữa các member trong team, thì cứ đè nguyên tắc này ra mà giải quyết (nghe hơi có mùi cứng nhắc, mình thì thích tùy cơ ứng biến hơn) Tìm các vị trí tuyển dụng designer lương cao cho bạn Nguyên tắc thiết kế của GOV.UK Đây là danh sách của trang GOV.UK Bắt đầu với thứ user cần Làm ít hơn Thiết kế với dữ liệu Làm mọi thứ thật dễ dàng Lặp. Rồi lặp lại lần nữa Dành cho tất cả mọi người Hiểu ngữ cảnh hiện tại Làm dịch vụ digital, không phải làm website Nhất quán, nhưng không hòa tan (phải có chất riêng với thằng khác) Cởi mở, mọi thứ tốt hơn Bao trừu tượng luôn các bạn, trang Gov.uk này cũng có câu tổng quát rất hay Thiết kế tốt là thiết kế có thể sử dụng. Phục vụ cho nhiều đối tượng sử dụng, dễ đọc nhất nhất có thể. Nếu phải từ bỏ đẹp tinh tế – thì cứ bỏ luôn . Chúng ta tạo sản phẩm cho nhu cầu sử dụng, không phải cho người hâm mộ . Chúng ta thiết kế để cả nước sử dụng, không phải những người đã từng sử dụng web. Những người cần dịch vụ của chúng ta nhất là những người đang cảm thấy khó sử dụng dịch...

By stationd
Hiểu về trình duyệt – How browsers work

Hiểu về trình duyệt – How browsers work

Bài viết được sự cho phép của vntesters.com Khi nhìn từ bên ngoài, trình duyệt web giống như một ứng dụng hiển thị những thông tin và tài nguyên từ server lên màn hình người sử dụng, nhưng để làm được công việc hiển thị đó đòi hỏi trình duyệt phải xử lý rất nhiều thông tin và nhiều tầng phía bên dưới. Việc chúng ta (Developers, Testers) tìm hiểu càng sâu tầng bên dưới để nắm được nguyên tắc hoạt động và xử lý của trình duyệt sẽ rất hữu ích trong công việc viết code, sử dụng các tài nguyên cũng như kiểm thử ứng dụng của mình. Cách để npm packages chạy trong browser Câu hỏi phỏng vấn mẹo về React: Component hay element được render trong browser? Khi hiểu được cách thức hoạt động của trình duyệt chúng ta có thể trả lời được rất nhiều câu hỏi như: Tại sao cùng một trang web lại hiển thị khác nhau trên hai trình duyệt? Tại sao chức năng này đang chạy tốt trên trình duyệt Firefox nhưng qua trình duyệt khác lại bị lỗi? Làm sao để trang web hiển thị nội dung nhanh và tối ưu hơn một chút?… Hy vọng sau bài này sẽ giúp các bạn có một cái nhìn rõ hơn cũng như giúp ích được trong công việc hiện tại. 1. Cấu trúc của một trình duyệt Trước tiên chúng ta đi qua cấu trúc, thành phần chung và cơ bản nhất của một trình duyệt web hiện đại, nó sẽ gồm các thành phần (tầng) như sau: Thành phần nằm phía trên là những thành phần gần với tương tác của người dùng, càng phía dưới thì càng sâu và nặng về xử lý dữ liệu hơn tương tác. Nhiệm...

By stationd
Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Lĩnh vực EdTech (ứng dụng công nghệ vào các sản phẩm giáo dục) trên toàn cầu hiện nay đã tương đối phong phú với nhiều tên tuổi lớn phân phối đều trên các hạng mục như Broad Online Learning Platforms (nền tảng cung cấp khóa học online đại chúng – tiêu biểu như Coursera, Udemy, KhanAcademy,…) Learning Management Systems (hệ thống quản lý lớp học – tiêu biểu như Schoology, Edmodo, ClassDojo,…) Next-Gen Study Tools (công cụ hỗ trợ học tập – tiểu biểu như Kahoot!, Lumosity, Curriculet,…) Tech Learning (đào tạo công nghệ – tiêu biểu như Udacity, Codecademy, PluralSight,…), Enterprise Learning (đào tạo trong doanh nghiệp – tiêu biểu như Edcast, ExecOnline, Grovo,..),… Hiện nay thị trường EdTech tại Việt Nam đã đón nhận khoảng đầu tư khoảng 55 triệu đô cho lĩnh vực này nhiều đơn vị nước ngoài đang quan tâm mạnh đến thị trường này ngày càng nhiều hơn. Là một trong những xu hướng phát triển tốt, và có doanh nghiệp đã hoạt động khá lâu trong ngành nêu tại infographic như Topica, nhưng EdTech vẫn chỉ đang trong giai đoạn sơ khai tại Việt Nam. Tại Việt Nam, hệ sinh thái EdTech trong nước vẫn còn rất non trẻ và thiếu vắng nhiều tên tuổi trong các hạng mục như Enterprise Learning (mới chỉ có MANA), School Administration (hệ thống quản lý trường học) hay Search (tìm kiếm, so sánh trường và khóa học),… Với chỉ dưới 5% số dân công sở có sử dụng một trong các dịch vụ giáo dục online, EdTech cho thấy vẫn còn một thị trường rộng lớn đang chờ được khai phá. *** Vừa qua Station D đã công bố Báo cáo Vietnam IT Landscape 2019 đem đến cái nhìn toàn cảnh về các ứng dụng công...

By stationd