Nội dung chính
Kiến thức cần nhớ
- UDP có cơ chế gửi tin không tin cậy, có nghĩa là chỉ cần gửi không cần check xem đã đến đích hay chưa.
- Trong java, cả UDP server và UDP client đều sử dụng đối tượng java.net.DatagramSocket để giao tiếp với nhau.
- Đối tượng java.net.DatagramPacket được sử dụng để đóng gói dữ liệu để gửi đi, và nhận dữ liệu dưới dạng được đóng gói. Số lượng maximum bytes bạn có thể gửi thông qua UDP là 65508 bytes cho một lần.
UDP transfer file example
Đây là một ví dụ về việc sử dụng giao thức UDP để truyền file.
Cách giải bài toán
Các file được transfer có thể có dung lượng (bytes) khác nhau, đối với UDP mỗi lần transfer, số maximum bytes là 65508, do vậy chúng ta cần chia nhỏ file thành nhiều phần để gửi đi. Trước khi gửi file từ client lên server, client phải thông báo cho server biết rằng đang chuẩn bị gửi file với các thông tin như tên file, file được chia ra thành bao nhiêu phần, mỗi phần có dung lượng là bao nhiêu... các thông tin này được đóng gói trong đối tượng FileInfo. Trong ví dụ này chúng tôi chia file ra thành các phần có dung lượng 32k, và phần cuối cùng có dung lượng là phần dư của tổng dung lượng chia cho 32k.
Cài đặt phía server
Cấu trúc project:
Tạo lớp vn.kienthuclaptrinh.common.FileInfo để nhận thông tin của file:
package vn.kienthuclaptrinh.common; import java.io.Serializable; public class FileInfo implements Serializable { private static final long serialVersionUID = 1L; private String destinationDirectory; private String sourceDirectory; private String filename; private long fileSize; private int piecesOfFile; private int lastByteLength; private String status; // Constructors // Getter and setter }
Tạo lớp UDPServer.java
Tạo lớp UDPServer.java với các nội dung sau:
Định nghĩa dung lượng (32KB) mỗi lần nhận, đối tượng DatagramSocket, và port giao tiếp:
private static final int PIECES_OF_FILE_SIZE = 1024 * 32; private DatagramSocket serverSocket; private int port = 6677;
Phương thức openServer():
private void openServer() { try { serverSocket = new DatagramSocket(port); System.out.println("Server is opened on port " + port); listening(); } catch (SocketException e) { e.printStackTrace(); } }
Xử lý nhận thông tin file, và nội dung file:
public void receiveFile() { byte[] receiveData = new byte[PIECES_OF_FILE_SIZE]; DatagramPacket receivePacket; try { // get file info receivePacket = new DatagramPacket(receiveData, receiveData.length); serverSocket.receive(receivePacket); InetAddress inetAddress = receivePacket.getAddress(); ByteArrayInputStream bais = new ByteArrayInputStream( receivePacket.getData()); ObjectInputStream ois = new ObjectInputStream(bais); FileInfo fileInfo = (FileInfo) ois.readObject(); // show file info if (fileInfo != null) { System.out.println("File name: " + fileInfo.getFilename()); System.out.println("File size: " + fileInfo.getFileSize()); System.out.println("Pieces of file: " + fileInfo.getPiecesOfFile()); System.out.println("Last bytes length: "+ fileInfo.getLastByteLength()); } // get file content System.out.println("Receiving file..."); File fileReceive = new File(fileInfo.getDestinationDirectory() + fileInfo.getFilename()); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(fileReceive)); // write pieces of file for (int i = 0; i < (fileInfo.getPiecesOfFile() - 1); i++) { receivePacket = new DatagramPacket(receiveData, receiveData.length, inetAddress, port); serverSocket.receive(receivePacket); bos.write(receiveData, 0, PIECES_OF_FILE_SIZE); } // write last bytes of file receivePacket = new DatagramPacket(receiveData, receiveData.length, inetAddress, port); serverSocket.receive(receivePacket); bos.write(receiveData, 0, fileInfo.getLastByteLength()); bos.flush(); System.out.println("Done!"); // close stream bos.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
Hàm main():
public static void main(String[] args) { UDPServer udpServer = new UDPServer(); udpServer.openServer(); }
Cài đặt phía client
Cấu trúc project:
Tạo lớp vn.kienthuclaptrinh.common.FileInfo để gửi thông tin của file:
package vn.kienthuclaptrinh.common; import java.io.Serializable; public class FileInfo implements Serializable { private static final long serialVersionUID = 1L; private String destinationDirectory; private String sourceDirectory; private String filename; private long fileSize; private int piecesOfFile; private int lastByteLength; private String status; // Constructors // Getter and setter }
Tạo lớp UDPClient.java
Tạo lớp UDPClient.java với các nội dung sau:
Định nghĩa dung lượng (32KB) mỗi lần gửi, đối tượng DatagramSocket, host và port của server:
private static final int PIECES_OF_FILE_SIZE = 1024 * 32; private DatagramSocket clientSocket; private int serverPort = 6677; private String serverHost = "localhost";
Định nghĩa phương thức kết nối server connectServer():
private void connectServer() { try { clientSocket = new DatagramSocket(); } catch (SocketException e) { e.printStackTrace(); } }
Xử lý gửi thông tin file và nội dung file, phương thức sendFile():
private void sendFile(String sourcePath, String destinationDir) { InetAddress inetAddress; DatagramPacket sendPacket; try { File fileSend = new File(sourcePath); InputStream inputStream = new FileInputStream(fileSend); BufferedInputStream bis = new BufferedInputStream(inputStream); inetAddress = InetAddress.getByName(serverHost); byte[] bytePart = new byte[PIECES_OF_FILE_SIZE]; // get file size long fileLength = fileSend.length(); int piecesOfFile = (int) (fileLength / PIECES_OF_FILE_SIZE); int lastByteLength = (int) (fileLength % PIECES_OF_FILE_SIZE); // check last bytes of file if (lastByteLength > 0) { piecesOfFile++; } // split file into pieces and assign to fileBytess byte[][] fileBytess = new byte[piecesOfFile][PIECES_OF_FILE_SIZE]; int count = 0; while (bis.read(bytePart, 0, PIECES_OF_FILE_SIZE) > 0) { fileBytess[count++] = bytePart; bytePart = new byte[PIECES_OF_FILE_SIZE]; } // read file info FileInfo fileInfo = new FileInfo(); fileInfo.setFilename(fileSend.getName()); fileInfo.setFileSize(fileSend.length()); fileInfo.setPiecesOfFile(piecesOfFile); fileInfo.setLastByteLength(lastByteLength); fileInfo.setDestinationDirectory(destinationDir); // send file info ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(fileInfo); sendPacket = new DatagramPacket(baos.toByteArray(), baos.toByteArray().length, inetAddress, serverPort); clientSocket.send(sendPacket); // send file content System.out.println("Sending file..."); // send pieces of file for (int i = 0; i < (count - 1); i++) { sendPacket = new DatagramPacket(fileBytess[i], PIECES_OF_FILE_SIZE, inetAddress, serverPort); clientSocket.send(sendPacket); waitServer(40); } // send last bytes of file sendPacket = new DatagramPacket(fileBytess[count - 1], PIECES_OF_FILE_SIZE, inetAddress, serverPort); clientSocket.send(sendPacket); waitServer(40); // close stream bis.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("Sent."); }
Hàm waitMillisecond(): đây là chỉ biện pháp tạm thời để đảm bảo gói tin đã được nhận.
public void waitMillisecond(long millisecond) { try { Thread.sleep(millisecond); } catch (InterruptedException e) { e.printStackTrace(); } }
Hàm main():
public static void main(String[] args) { String sourcePath = "D:\\client\\test.zip"; String destinationDir = "D:\\server\\"; UDPClient udpClient = new UDPClient(); udpClient.connectServer(); udpClient.sendFile(sourcePath, destinationDir); }