Java实现端口映射/转发

20190630 14:57更新

添加了一个功能 检查当前连接状态 同时发现清除超时连接忘了加定时器 现在清除超时连接可以正常工作了

  1 import java.io.IOException;
  2 import java.io.InputStream;
  3 import java.io.OutputStream;
  4 import java.net.ServerSocket;
  5 import java.net.Socket;
  6 import java.text.SimpleDateFormat;
  7 import java.util.*;
  8 
  9 public class SimpleForward implements Runnable {
 10     // 服务器
 11     private ServerSocket server;
 12     // 监听本地端口
 13     private int localPort = 1080;
 14     // 目标主机地址
 15     private String remoteHostAddr = "0.0.0.0";
 16     // 目标主机端口
 17     private int remoteHostPort = 8388;
 18     // 设置超时时间 30s
 19     private static int TIMEOUT = 30;
 20     // 客户端列表 用于删除失效连接和超时连接
 21     private static HashMap<Socket, Date> clientList = new HashMap<>();
 22 
 23     public static void main(String[] args) throws IOException {
 24 //        new SimpleForward();
 25         new SimpleForward(1234, "127.0.0.1", 1080);
 26     }
 27 
 28     public SimpleForward() throws IOException {
 29         run();
 30     }
 31 
 32     public SimpleForward(int localPort, String remoteHostAddr, int remoteHostPort) throws IOException {
 33         this.localPort = localPort;
 34         this.remoteHostAddr = remoteHostAddr;
 35         this.remoteHostPort = remoteHostPort;
 36         run();
 37     }
 38 
 39     @Override
 40     public void run() {
 41         try {
 42             this.server = new ServerSocket(this.localPort);
 43             System.out.println("服务器开启成功");
 44             System.out.println("监听端口 : " + this.localPort);
 45         } catch (IOException e) {
 46             System.out.println("服务器开启失败");
 47             System.out.println(e.getMessage());
 48             System.out.println("退出运行");
 49             return;
 50         }
 51         // 自动清除失效连接和超时连接
 52         new Thread(new Terminal()).start();
 53         new Thread(new AutoDestroy()).start();
 54         while (true) {
 55             Socket socket = null;
 56             Socket remoteHost = null;
 57             try {
 58                 socket = server.accept();
 59                 // 接收到请求就把socket扔进map,value为刷新时间
 60                 clientList.put(socket, new Date());
 61                 String address = socket.getRemoteSocketAddress().toString();
 62                 System.out.println("新连接 : " + address);
 63                 // 建立与目标主机的连接
 64                 remoteHost = new Socket(this.remoteHostAddr, this.remoteHostPort);
 65                 System.out.println("连接地址 : " + this.remoteHostAddr + ":" + this.remoteHostPort);
 66                 // 端口转发
 67                 new Thread(new Switch(socket, remoteHost, remoteHost.getInputStream(), socket.getOutputStream())).start();
 68                 new Thread(new Switch(socket, remoteHost, socket.getInputStream(), remoteHost.getOutputStream())).start();
 69             } catch (IOException e) {
 70                 System.out.println("连接异常");
 71                 System.out.println(e.getMessage());
 72                 close(socket);
 73                 close(remoteHost);
 74             }
 75         }
 76     }
 77 
 78     private void close(Socket socket) {
 79         try {
 80             if (socket != null) {
 81                 socket.close();
 82             }
 83         } catch (IOException e) {
 84             e.printStackTrace();
 85         }
 86     }
 87 
 88     // 用于端口转发
 89     private class Switch implements Runnable {
 90         private Socket host;
 91         private Socket remoteHost;
 92         private InputStream in;
 93         private OutputStream out;
 94 
 95         Switch(Socket host, Socket remoteHost, InputStream in, OutputStream out) {
 96             this.host = host;
 97             this.remoteHost = remoteHost;
 98             this.in = in;
 99             this.out = out;
100         }
101 
102         @Override
103         public void run() {
104             int length = 0;
105             byte[] buffer = new byte[1024];
106             try {
107                 while (!host.isClosed() && (length = in.read(buffer)) > -1) {
108                     clientList.put(host, new Date());
109                     out.write(buffer, 0, length);
110                 }
111             } catch (IOException e) {
112                 System.out.println("连接关闭");
113             } finally {
114                 close(host);
115                 close(remoteHost);
116             }
117         }
118     }
119 
120     // 用于清除失效连接和超时连接
121     private class AutoDestroy implements Runnable {
122 
123         @Override
124         public void run() {
125             Timer timer = new Timer();
126             timer.schedule(new TimerTask() {
127                 @Override
128                 public void run() {
129                     List<Socket> list = new LinkedList<>();
130                     System.out.println("开始扫描失效与超时连接");
131                     Date start = new Date();
132                     for (Socket socket : clientList.keySet()) {
133                         Date lastTime = clientList.get(socket);
134                         long time = new Date().getTime() - lastTime.getTime();
135                         if (socket.isClosed() || time / 1000 >= TIMEOUT) {
136                             list.add(socket);
137                         }
138                     }
139                     System.out.println("找到" + list.size() + "个,用时 : " + (new Date().getTime() - start.getTime()) + "毫秒");
140                     System.out.println("开始清除失效与超时连接");
141                     for (Socket socket : list) {
142                         try {
143                             clientList.remove(socket);
144                             socket.close();
145                         } catch (IOException e) {
146                             e.printStackTrace();
147                         }
148                     }
149                     System.out.println("当前连接数 : " + clientList.size());
150                 }
151             }, 30 * 1000, 30 * 1000);
152         }
153     }
154 
155     private class Terminal implements Runnable {
156         private ReaderUtil reader = new ReaderUtil();
157         private String format = "yyyy-MM-dd HH:mm:ss";
158         private SimpleDateFormat dateFormat = new SimpleDateFormat(format);
159 
160         @Override
161         public void run() {
162             while (!server.isClosed()) {
163                 System.out.print("请输入命令 : ");
164                 String cmd = reader.readKB();
165                 handler(cmd);
166             }
167         }
168 
169         private void handler(String cmd) {
170             switch (cmd) {
171                 case "status":
172                     System.out.println("当前时间 : " + dateFormat.format(new Date()));
173                     System.out.println("总连接数 : " + clientList.size());
174                     for (Socket socket : clientList.keySet()) {
175                         long time = new Date().getTime() - clientList.get(socket).getTime();
176                         System.out.println("<" + socket.getRemoteSocketAddress().toString() + "> " + time / 1000);
177                     }
178                     break;
179             }
180         }
181     }
182 }

ReaderUtil这个东西是我自己写的从键盘读一行的工具类 没啥东西 你们需要自己写