package org.kne.cloud.network.frpc; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.lang.ProcessHandle.Info; import java.net.BindException; import java.net.ConnectException; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; public class FrpcProcess extends Process{ private static File defaultBase=new File("frpc"); private volatile Thread hook; public static File getDefaultBase() { return defaultBase; } public static void setDefaultBase(File defaultBase) { FrpcProcess.defaultBase = defaultBase; } private File base; private File exe; private Process theProcess; private File config_file; public FrpcProcess(String ini) throws IOException { this(defaultBase,ini); } public FrpcProcess(File base,String ini) throws IOException { this.base=base; if(System.getProperty("os.name").toLowerCase().contains("windows")) { exe=new File(base,"frpc_windows_amd64.exe"); }else { exe=new File(base,"frpc_linux_amd64"); } AtomicReferencearex=new AtomicReference(); synchronized (getClass()) { UUID uid=UUID.randomUUID(); config_file = new File(base,"frpc_conf_"+uid+".ini"); config_file.createNewFile(); config_file.deleteOnExit(); PrintWriter ps=null; try { ps=new PrintWriter(config_file); ps.println(ini); }finally { if(ps!=null) ps.close(); } Listlstc=new ArrayList<>(); lstc.add(exe.getAbsolutePath()); lstc.add("-c"); lstc.add(config_file.getName()); ProcessBuilder psb=new ProcessBuilder(lstc); psb.directory(base); theProcess=psb.start(); Thread t=Thread.currentThread(); hook=new Thread(()->{ try { hook=null; finalize(); } catch (Throwable e) { e.printStackTrace(); } }); Runtime.getRuntime().addShutdownHook(hook); CountDownLatch cdl=new CountDownLatch(1); new Thread(()->{ BufferedReader br=new BufferedReader(new InputStreamReader(theProcess.getInputStream())); try { while(true) { String s=br.readLine(); if(s==null) { break; } cdl.countDown(); System.out.println ("{"+uid+"}"+s); if(s.contains("start proxy success")) { t.interrupt(); }else if(s.contains("port already used")){ arex.set(new BindException("port on server already used")); t.interrupt(); }else if(s.contains("远程端口已经在使用了")) { arex.set(new BindException("port on server already used")); t.interrupt(); }else if(s.contains("no such host")) { arex.set(new UnknownHostException("no such host")); t.interrupt(); }else if(s.contains("connection write timeout")) { arex.set(new ConnectException("connection write timeout")); t.interrupt(); }else if(s.contains("already exists")&&s.contains("proxy")) { arex.set(new BindException("proxy already exists")); t.interrupt(); }else if(s.contains("已经存在, 请检查是否重复开启")&&s.contains("隧道")) { arex.set(new BindException("proxy already exists")); t.interrupt(); } } } catch (IOException e) { e.printStackTrace(); }finally { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); try { cdl.await(5000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } try { if(theProcess.waitFor(20000, TimeUnit.MILLISECONDS)) { destroy(); throw new SocketException("Frpc exit code:"+theProcess.exitValue()); }else { destroy(); throw new SocketException("Frpc timed out"); } } catch (InterruptedException e) { //destroy(); Thread.interrupted(); if(arex.get()!=null) { destroy(); arex.get().fillInStackTrace(); throw arex.get(); } } } public File getBase() { return base; } @Override public OutputStream getOutputStream() { return theProcess.getOutputStream(); } @Override public InputStream getInputStream() { return theProcess.getInputStream(); } @Override public InputStream getErrorStream() { return theProcess.getErrorStream(); } @Override public int waitFor() throws InterruptedException { return theProcess.waitFor(); } @Override public int exitValue() { return theProcess.exitValue(); } @Override public void destroy() { theProcess.destroy(); //System.out.println("frpc已关闭"); if(hook!=null) { Runtime.getRuntime().removeShutdownHook(hook); } if(config_file!=null) { config_file.delete(); } } @Override public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException { return theProcess.waitFor(timeout, unit); } @Override public Process destroyForcibly() { Process b= theProcess.destroyForcibly(); //System.out.println("frpc已关闭"); if(hook!=null) { Runtime.getRuntime().removeShutdownHook(hook); } return b; } @Override public boolean supportsNormalTermination() { return theProcess.supportsNormalTermination(); } @Override public boolean isAlive() { return theProcess.isAlive(); } @Override public long pid() { return theProcess.pid(); } @Override public CompletableFuture onExit() { return theProcess.onExit(); } @Override public ProcessHandle toHandle() { return theProcess.toHandle(); } @Override public Info info() { return theProcess.info(); } @Override public Stream children() { return theProcess.children(); } @Override public Stream descendants() { return theProcess.descendants(); } public static void main(String[] args) throws IOException { String str=""" [common] server_addr = us.afrps.cn server_port = 7000 token = afrps.cn protocol=kcp [test5] type = tcp local_ip = 127.0.0.1 local_port = 7461 remote_port = 49966 use_encryption = true use_compression = true """; FrpcIni fi=new FrpcIni(str); str=fi.storeToString(); //System.out.println(fi.getServerPort()); FrpcProcess fp=new FrpcProcess(str); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override protected void finalize() throws Throwable { if(theProcess!=null) { destroy(); } } }