採用雙 Socket 通道分離關注點:
Movemouse,x,y)客戶端並行接收兩個通道數據,分別在 Swing 界面顯示圖片、用 Robot.mouseMove 同步鼠標。
public class BroadCastServer {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(new SendScreenImg()); // 截圖發送線程
pool.execute(new SendMouseMessage()); // 鼠標發送線程
System.out.println("廣播服務已啟動");
}
}
public class SendScreenImg extends Thread {
private final int PORT = 8002;
private final ServerSocket server;
private final Robot robot;
private final Rectangle screenRect;
public SendScreenImg() throws AWTException, IOException {
server = new ServerSocket(PORT);
robot = new Robot();
Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
screenRect = new Rectangle(size);
}
@Override
public void run() {
while (true) {
try (Socket client = server.accept();
ZipOutputStream zos = new ZipOutputStream(new DataOutputStream(client.getOutputStream()))) {
zos.setLevel(9);
BufferedImage img = robot.createScreenCapture(screenRect);
zos.putNextEntry(new ZipEntry("screen.jpg"));
ImageIO.write(img, "jpg", zos);
zos.closeEntry();
} catch (IOException e) {
System.err.println("截圖通道異常: " + e.getMessage());
}
}
}
}
public class SendMouseMessage extends Thread {
private final int PORT = 8001;
private final ServerSocket server;
public SendMouseMessage() throws IOException {
server = new ServerSocket(PORT);
}
@Override
public void run() {
while (true) {
try (Socket client = server.accept();
DataOutputStream out = new DataOutputStream(client.getOutputStream())) {
Point pt = MouseInfo.getPointerInfo().getLocation();
String msg = "Movemouse," + pt.x + "," + pt.y;
out.write(msg.getBytes());
out.flush();
} catch (IOException e) {
System.err.println("鼠標通道斷開: " + e.getMessage());
}
}
}
}
public class BroadcastClient {
private static final String SERVER_IP = "192.168.1.100"; // 替換為服務端 IP
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(new ReceiveImages(SERVER_IP));
pool.execute(new OperateMouse(SERVER_IP));
}
}
public class ReceiveImages extends Thread {
private final String ip;
private final JFrame frame = new JFrame("遠程桌面");
private final JLabel imgLabel = new JLabel();
public ReceiveImages(String ip) {
this.ip = ip;
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(imgLabel);
frame.setSize(800, 600);
frame.setVisible(true);
}
@Override
public void run() {
while (frame.isDisplayable()) {
try (Socket socket = new Socket(ip, 8002);
ZipInputStream zis = new ZipInputStream(socket.getInputStream())) {
zis.getNextEntry();
Image img = ImageIO.read(zis);
if (img != null) {
SwingUtilities.invokeLater(() -> {
imgLabel.setIcon(new ImageIcon(img));
frame.pack();
});
}
Thread.sleep(50); // 幀率控制 ~20fps
} catch (Exception e) {
System.err.println("圖片接收異常: " + e.getMessage());
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
}
}
}
}
public class OperateMouse extends Thread {
private final String ip;
private final Robot robot;
public OperateMouse(String ip) throws AWTException {
this.ip = ip;
this.robot = new Robot();
}
@Override
public void run() {
while (true) {
try (Socket socket = new Socket(ip, 8001);
DataInputStream in = new DataInputStream(socket.getInputStream())) {
StringBuilder sb = new StringBuilder();
int b;
while ((b = in.read()) != -1) sb.append((char) b);
String msg = sb.toString().trim();
if (msg.startsWith("Movemouse")) {
String[] parts = msg.split(",");
if (parts.length == 3) {
int x = Integer.parseInt(parts[1]);
int y = Integer.parseInt(parts[2]);
robot.mouseMove(x, y);
}
}
} catch (IOException e) {
System.err.println("鼠標同步斷開: " + e.getMessage());
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
}
}
}
}
| 點 | 說明 |
|---|---|
| Robot 類 | 同時用於截圖 (createScreenCapture) 與鼠標控制 (mouseMove) |
| ZIP 壓縮 | 截圖傳輸前壓縮,顯著降低帶寬(設級別 9 最大壓縮) |
| 線程池 | 固定 2 線程隔離兩通道,避免相互阻塞 |
| Swing 線程安全 | UI 更新必須在 EDT 執行,使用 SwingUtilities.invokeLater |
| 異常重連 | 客戶端捕獲異常後 sleep 重試,實現斷線自動重連 |