Lập trình mạng: Xây dựng ứng dụng Client- Server ở chế độ UDP (không kết nối) Java

Đăng bởi: Admin | Lượt xem: 35 | Chuyên mục: Java

Ở bài trước ta đã tìm hiểu cách xây dựng ứng dụng Client- Server ở chế độ có kết nối (TCP – Transmission Control Protocol). Trong bài này, ta tìm hiểu cách xây dựng ứng dụng Client- Server ở chế độ không kết nối (UDP – User Datagram Protocol) tiếp tục sử dụng Socket.


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

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

2. 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.​​​​​​​​​​​​​​

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

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.

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:

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

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

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

Ở bài viết tiếp theo, chúng ta sẽ tìm hiểu các xây dựng chương trình Client- Server kết nối theo dạng multicast (truyền theo nhóm)

vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!



Khóa học liên quan

Khóa học: Java

Tổng hợp Bài tập Java có lời giải
Số bài học:
Lượt xem: 563
Đăng bởi: Admin
Chuyên mục: Java

Lập trình Java cơ bản
Số bài học:
Lượt xem: 9610
Đăng bởi: Admin
Chuyên mục: Java