Truy cập: Võ Văn Hải's blog
Hình ảnh: Võ Văn Hải's blog
Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách sử dụng Socket trong Java với đa tiến trình. Hãy cùng xem các bước thực hiện nhé!
Tạo lớp Server
Đầu tiên, chúng ta tạo một lớp Server bằng cách tạo một ServerSocket
lắng nghe trên cổng 9999. Với chức năng đa tiến trình, chúng ta sẽ tạo một luồng (thread) mới cho mỗi kết nối từ client.
package vovanhai.wordpress.com.server;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class CalculatorServer {
public static void main(String[] args) {
try {
final ServerSocket svr = new ServerSocket(9999);
System.out.println("Server đang lắng nghe trên cổng 9999...");
while (true) {
Socket soc = svr.accept();
new Thread(new CalcRunnable(soc)).start();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
class CalcRunnable implements Runnable {
Socket client = null;
CalcRunnable(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
// Nhận yêu cầu từ client
InputStream is = client.getInputStream();
Scanner in = new Scanner(is);
OutputStream os = client.getOutputStream();
PrintWriter out = new PrintWriter(os, true); // auto-flush
while (in.hasNextLine()) {
double a = Double.parseDouble(in.nextLine());
double b = Double.parseDouble(in.nextLine());
char op = in.nextLine().charAt(0);
double kq = tinhtoan(a, b, op);
out.println(kq);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private double tinhtoan(double a, double b, char op) {
double ret = 0;
switch (op) {
case '+':
ret = a + b;
break;
case '-':
ret = a - b;
break;
case '*':
ret = a * b;
break;
case '/':
ret = a / b;
break;
}
return ret;
}
}
Tạo lớp Client
Tiếp theo, chúng ta tạo một lớp client để thử nghiệm. Trong ví dụ này, chúng ta sẽ sử dụng giao diện đồ họa để client tương tác với Server. Dưới đây là mã nguồn của lớp client:
package vovanhai.wordpress.com.client;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class CalcClientGUI extends JFrame implements ActionListener {
private JTextField tfA, tfB, tfRes, tfAdd;
private JButton btnCong, btnTru, btnNhan, btnChia;
public CalcClientGUI() {
setTitle("Mẹt Calculator");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 200);
setResizable(false);
setLocation(100, 200);
Box b = Box.createVerticalBox();
Box b1 = Box.createHorizontalBox();
Box b2 = Box.createHorizontalBox();
Box b3 = Box.createHorizontalBox();
Box b4 = Box.createHorizontalBox();
Box b5 = Box.createHorizontalBox();
JLabel l1, l2, l4;
b5.add(l4 = new JLabel("Địa chỉ Server:", JLabel.RIGHT));
b5.add(tfAdd = new JTextField("localhost"));
b1.add(l1 = new JLabel("Số A:", JLabel.RIGHT));
b1.add(tfA = new JTextField());
b2.add(l2 = new JLabel("Số B:", JLabel.RIGHT));
b2.add(tfB = new JTextField());
b4.add(new JLabel("Kết quả:", JLabel.RIGHT));
b4.add(tfRes = new JTextField());
b3.add(btnCong = new JButton("+"));
btnCong.addActionListener(this);
b3.add(btnTru = new JButton("-"));
btnTru.addActionListener(this);
b3.add(btnNhan = new JButton("*"));
btnNhan.addActionListener(this);
b3.add(btnChia = new JButton("/"));
btnChia.addActionListener(this);
l1.setPreferredSize(l4.getPreferredSize());
l2.setPreferredSize(l4.getPreferredSize());
tfRes.setEditable(false);
b.add(Box.createVerticalStrut(10));
b.add(b5);
b.add(Box.createVerticalStrut(10));
b.add(b1);
b.add(Box.createVerticalStrut(10));
b.add(b2);
b.add(Box.createVerticalStrut(10));
b.add(b3);
b.add(Box.createVerticalStrut(10));
b.add(b4);
b.add(Box.createVerticalStrut(10));
this.add(b, BorderLayout.NORTH);
}
@Override
public void actionPerformed(ActionEvent e) {
Object o = e.getSource();
try {
double a = Double.parseDouble(tfA.getText());
double b = Double.parseDouble(tfB.getText());
String op = "";
if (o.equals(btnCong)) {
op = "+";
} else if (o.equals(btnTru)) {
op = "-";
} else if (o.equals(btnNhan))
op = "*";
else if (o.equals(btnChia))
op = "/";
Socket soc = new Socket(tfAdd.getText(), 9999);
Scanner in = new Scanner(soc.getInputStream());
PrintWriter out = new PrintWriter(soc.getOutputStream(), true);
out.println(a);
out.println(b);
out.println(op);
String kq = in.nextLine();
tfRes.setText(kq);
} catch (Exception e2) {
JOptionPane.showMessageDialog(null, e2.getMessage());
}
}
public static void main(String[] args) {
new CalcClientGUI().setVisible(true);
}
}
Biên dịch và chạy
Sau khi đã hoàn thành việc viết mã nguồn, chúng ta tiến hành biên dịch 2 file này. Để dễ thao tác, ta có thể tạo các file .bat
như sau (đảm bảo bạn đã thiết lập biến môi trường PATH
):
File build.bat
dùng để biên dịch:
javac -d . -encoding UTF-8 *.java
pause
Tạo file RunServer.bat
với nội dung sau để chạy Server:
java vovanhai.wordpress.com.server.CalculatorServer
pause
Tạo file RunClient.bat
với nội dung sau để chạy Client:
java vovanhai.wordpress.com.client.CalcClientGUI
pause
Bạn chỉ cần chạy file build.bat
để biên dịch các file Java. Sau khi biên dịch, nếu không có lỗi, bạn sẽ nhận được cấu trúc thư mục như sau:
vovanhai.wordpress.com
|-- client
| |-- CalcClientGUI.class
| `-- CalcClientGUI.java
`-- server
|-- CalculatorServer.class
`-- CalculatorServer.java
Tiếp theo, chạy file RunServer.bat
để khởi động Server. Bạn sẽ thấy thông báo như sau:
Hình ảnh: Chạy Server
Cuối cùng, chạy file RunClient.bat
để mở giao diện client:
Hình ảnh: Chạy Client
Bạn có thể triển khai phần Server trong máy chủ của mình và chạy Client trên máy tính cá nhân. Hãy nhập đúng địa chỉ/tên máy chủ vào ô "Địa chỉ Server" và giá trị cần tính vào 2 ô textfield còn lại. Bạn sẽ nhận được kết quả sau khi nhấn nút tương ứng.
Chúc bạn thành công!